001
014
015 package com.liferay.portal.scripting.ruby;
016
017 import com.liferay.portal.kernel.log.Log;
018 import com.liferay.portal.kernel.log.LogFactoryUtil;
019 import com.liferay.portal.kernel.scripting.BaseScriptingExecutor;
020 import com.liferay.portal.kernel.scripting.ExecutionException;
021 import com.liferay.portal.kernel.scripting.ScriptingException;
022 import com.liferay.portal.kernel.util.AggregateClassLoader;
023 import com.liferay.portal.kernel.util.FileUtil;
024 import com.liferay.portal.kernel.util.StringPool;
025 import com.liferay.portal.kernel.util.SystemProperties;
026 import com.liferay.portal.security.pacl.PACLClassLoaderUtil;
027 import com.liferay.portal.util.PortalUtil;
028 import com.liferay.portal.util.PropsValues;
029
030 import java.io.File;
031 import java.io.FileInputStream;
032 import java.io.FileNotFoundException;
033
034 import java.util.ArrayList;
035 import java.util.HashMap;
036 import java.util.List;
037 import java.util.Map;
038 import java.util.Set;
039
040 import jodd.io.ZipUtil;
041
042 import org.jruby.RubyInstanceConfig.CompileMode;
043 import org.jruby.RubyInstanceConfig;
044 import org.jruby.embed.LocalContextScope;
045 import org.jruby.embed.ScriptingContainer;
046 import org.jruby.embed.internal.LocalContextProvider;
047 import org.jruby.exceptions.RaiseException;
048
049
053 public class RubyExecutor extends BaseScriptingExecutor {
054
055 public static final String LANGUAGE = "ruby";
056
057 public RubyExecutor() {
058 try {
059 initRubyGems();
060 }
061 catch (Exception e) {
062 _log.error(e, e);
063 }
064
065 _scriptingContainer = new ScriptingContainer(
066 LocalContextScope.THREADSAFE);
067
068 LocalContextProvider localContextProvider =
069 _scriptingContainer.getProvider();
070
071 RubyInstanceConfig rubyInstanceConfig =
072 localContextProvider.getRubyInstanceConfig();
073
074 if (PropsValues.SCRIPTING_JRUBY_COMPILE_MODE.equals(
075 _COMPILE_MODE_FORCE)) {
076
077 rubyInstanceConfig.setCompileMode(CompileMode.FORCE);
078 }
079 else if (PropsValues.SCRIPTING_JRUBY_COMPILE_MODE.equals(
080 _COMPILE_MODE_JIT)) {
081
082 rubyInstanceConfig.setCompileMode(CompileMode.JIT);
083 }
084
085 rubyInstanceConfig.setJitThreshold(
086 PropsValues.SCRIPTING_JRUBY_COMPILE_THRESHOLD);
087 rubyInstanceConfig.setLoader(
088 PACLClassLoaderUtil.getPortalClassLoader());
089
090 _basePath = PortalUtil.getPortalLibDir();
091
092 _loadPaths = new ArrayList<String>(
093 PropsValues.SCRIPTING_JRUBY_LOAD_PATHS.length);
094
095 for (String gemLibPath : PropsValues.SCRIPTING_JRUBY_LOAD_PATHS) {
096 _loadPaths.add(gemLibPath);
097 }
098
099 rubyInstanceConfig.setLoadPaths(_loadPaths);
100
101 _scriptingContainer.setCurrentDirectory(_basePath);
102 }
103
104 @Override
105 public Map<String, Object> eval(
106 Set<String> allowedClasses, Map<String, Object> inputObjects,
107 Set<String> outputNames, File scriptFile,
108 ClassLoader... classLoaders)
109 throws ScriptingException {
110
111 return eval(
112 allowedClasses, inputObjects, outputNames, scriptFile, null,
113 classLoaders);
114 }
115
116 public Map<String, Object> eval(
117 Set<String> allowedClasses, Map<String, Object> inputObjects,
118 Set<String> outputNames, String script, ClassLoader... classLoaders)
119 throws ScriptingException {
120
121 return eval(
122 allowedClasses, inputObjects, outputNames, null, script,
123 classLoaders);
124 }
125
126 public String getLanguage() {
127 return LANGUAGE;
128 }
129
130 protected Map<String, Object> eval(
131 Set<String> allowedClasses, Map<String, Object> inputObjects,
132 Set<String> outputNames, File scriptFile, String script,
133 ClassLoader... classLoaders)
134 throws ScriptingException {
135
136 if (allowedClasses != null) {
137 throw new ExecutionException(
138 "Constrained execution not supported for Ruby");
139 }
140
141 try {
142 LocalContextProvider localContextProvider =
143 _scriptingContainer.getProvider();
144
145 RubyInstanceConfig rubyInstanceConfig =
146 localContextProvider.getRubyInstanceConfig();
147
148 rubyInstanceConfig.setCurrentDirectory(_basePath);
149
150 if ((classLoaders != null) && (classLoaders.length > 0)) {
151 ClassLoader aggregateClassLoader =
152 AggregateClassLoader.getAggregateClassLoader(
153 PACLClassLoaderUtil.getPortalClassLoader(),
154 classLoaders);
155
156 rubyInstanceConfig.setLoader(aggregateClassLoader);
157 }
158
159 rubyInstanceConfig.setLoadPaths(_loadPaths);
160
161 for (Map.Entry<String, Object> entry : inputObjects.entrySet()) {
162 String inputName = entry.getKey();
163 Object inputObject = entry.getValue();
164
165 if (!inputName.startsWith(StringPool.DOLLAR)) {
166 inputName = StringPool.DOLLAR + inputName;
167 }
168
169 _scriptingContainer.put(inputName, inputObject);
170 }
171
172 if (scriptFile != null) {
173 _scriptingContainer.runScriptlet(
174 new FileInputStream(scriptFile), scriptFile.toString());
175 }
176 else {
177 _scriptingContainer.runScriptlet(script);
178 }
179
180 if (outputNames == null) {
181 return null;
182 }
183
184 Map<String, Object> outputObjects = new HashMap<String, Object>();
185
186 for (String outputName : outputNames) {
187 outputObjects.put(
188 outputName, _scriptingContainer.get(outputName));
189 }
190
191 return outputObjects;
192 }
193 catch (RaiseException re) {
194 throw new ScriptingException(
195 re.getException().message.asJavaString() + "\n\n", re);
196 }
197 catch (FileNotFoundException fnfe) {
198 throw new ScriptingException(fnfe);
199 }
200 }
201
202 protected void initRubyGems() throws Exception {
203 File rubyGemsJarFile = new File(
204 PropsValues.LIFERAY_LIB_PORTAL_DIR, "ruby-gems.jar");
205
206 if (!rubyGemsJarFile.exists()) {
207 if (_log.isWarnEnabled()) {
208 _log.warn(rubyGemsJarFile + " does not exist");
209 }
210
211 return;
212 }
213
214 String tmpDir = SystemProperties.get(SystemProperties.TMP_DIR);
215
216 File rubyDir = new File(tmpDir + "/liferay/ruby");
217
218 if (!rubyDir.exists() ||
219 (rubyDir.lastModified() < rubyGemsJarFile.lastModified())) {
220
221 FileUtil.deltree(rubyDir);
222
223 rubyDir.mkdirs();
224
225 ZipUtil.unzip(rubyGemsJarFile, rubyDir);
226
227 rubyDir.setLastModified(rubyGemsJarFile.lastModified());
228 }
229 }
230
231 private static final String _COMPILE_MODE_FORCE = "force";
232
233 private static final String _COMPILE_MODE_JIT = "jit";
234
235 private static Log _log = LogFactoryUtil.getLog(RubyExecutor.class);
236
237 private String _basePath;
238 private List<String> _loadPaths;
239 private ScriptingContainer _scriptingContainer;
240
241 }