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.util.ClassLoaderUtil;
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(ClassLoaderUtil.getPortalClassLoader());
088
089 _basePath = PortalUtil.getPortalLibDir();
090
091 _loadPaths = new ArrayList<String>(
092 PropsValues.SCRIPTING_JRUBY_LOAD_PATHS.length);
093
094 for (String gemLibPath : PropsValues.SCRIPTING_JRUBY_LOAD_PATHS) {
095 _loadPaths.add(gemLibPath);
096 }
097
098 rubyInstanceConfig.setLoadPaths(_loadPaths);
099
100 _scriptingContainer.setCurrentDirectory(_basePath);
101 }
102
103 @Override
104 public Map<String, Object> eval(
105 Set<String> allowedClasses, Map<String, Object> inputObjects,
106 Set<String> outputNames, File scriptFile,
107 ClassLoader... classLoaders)
108 throws ScriptingException {
109
110 return eval(
111 allowedClasses, inputObjects, outputNames, scriptFile, null,
112 classLoaders);
113 }
114
115 public Map<String, Object> eval(
116 Set<String> allowedClasses, Map<String, Object> inputObjects,
117 Set<String> outputNames, String script, ClassLoader... classLoaders)
118 throws ScriptingException {
119
120 return eval(
121 allowedClasses, inputObjects, outputNames, null, script,
122 classLoaders);
123 }
124
125 public String getLanguage() {
126 return LANGUAGE;
127 }
128
129 protected Map<String, Object> eval(
130 Set<String> allowedClasses, Map<String, Object> inputObjects,
131 Set<String> outputNames, File scriptFile, String script,
132 ClassLoader... classLoaders)
133 throws ScriptingException {
134
135 if (allowedClasses != null) {
136 throw new ExecutionException(
137 "Constrained execution not supported for Ruby");
138 }
139
140 try {
141 LocalContextProvider localContextProvider =
142 _scriptingContainer.getProvider();
143
144 RubyInstanceConfig rubyInstanceConfig =
145 localContextProvider.getRubyInstanceConfig();
146
147 rubyInstanceConfig.setCurrentDirectory(_basePath);
148
149 if ((classLoaders != null) && (classLoaders.length > 0)) {
150 ClassLoader aggregateClassLoader =
151 AggregateClassLoader.getAggregateClassLoader(
152 ClassLoaderUtil.getPortalClassLoader(), classLoaders);
153
154 rubyInstanceConfig.setLoader(aggregateClassLoader);
155 }
156
157 rubyInstanceConfig.setLoadPaths(_loadPaths);
158
159 for (Map.Entry<String, Object> entry : inputObjects.entrySet()) {
160 String inputName = entry.getKey();
161 Object inputObject = entry.getValue();
162
163 if (!inputName.startsWith(StringPool.DOLLAR)) {
164 inputName = StringPool.DOLLAR + inputName;
165 }
166
167 _scriptingContainer.put(inputName, inputObject);
168 }
169
170 if (scriptFile != null) {
171 _scriptingContainer.runScriptlet(
172 new FileInputStream(scriptFile), scriptFile.toString());
173 }
174 else {
175 _scriptingContainer.runScriptlet(script);
176 }
177
178 if (outputNames == null) {
179 return null;
180 }
181
182 Map<String, Object> outputObjects = new HashMap<String, Object>();
183
184 for (String outputName : outputNames) {
185 outputObjects.put(
186 outputName, _scriptingContainer.get(outputName));
187 }
188
189 return outputObjects;
190 }
191 catch (RaiseException re) {
192 throw new ScriptingException(
193 re.getException().message.asJavaString() + "\n\n", re);
194 }
195 catch (FileNotFoundException fnfe) {
196 throw new ScriptingException(fnfe);
197 }
198 }
199
200 protected void initRubyGems() throws Exception {
201 File rubyGemsJarFile = new File(
202 PropsValues.LIFERAY_LIB_PORTAL_DIR, "ruby-gems.jar");
203
204 if (!rubyGemsJarFile.exists()) {
205 if (_log.isWarnEnabled()) {
206 _log.warn(rubyGemsJarFile + " does not exist");
207 }
208
209 return;
210 }
211
212 String tmpDir = SystemProperties.get(SystemProperties.TMP_DIR);
213
214 File rubyDir = new File(tmpDir + "/liferay/ruby");
215
216 if (!rubyDir.exists() ||
217 (rubyDir.lastModified() < rubyGemsJarFile.lastModified())) {
218
219 FileUtil.deltree(rubyDir);
220
221 rubyDir.mkdirs();
222
223 ZipUtil.unzip(rubyGemsJarFile, rubyDir);
224
225 rubyDir.setLastModified(rubyGemsJarFile.lastModified());
226 }
227 }
228
229 private static final String _COMPILE_MODE_FORCE = "force";
230
231 private static final String _COMPILE_MODE_JIT = "jit";
232
233 private static Log _log = LogFactoryUtil.getLog(RubyExecutor.class);
234
235 private String _basePath;
236 private List<String> _loadPaths;
237 private ScriptingContainer _scriptingContainer;
238
239 }