001
014
015 package com.liferay.portal.tools.sass;
016
017 import com.liferay.portal.kernel.util.NamedThreadFactory;
018 import com.liferay.portal.kernel.util.StringUtil;
019 import com.liferay.portal.kernel.util.SystemProperties;
020 import com.liferay.portal.scripting.ruby.RubyExecutor;
021
022 import java.io.IOException;
023
024 import java.util.concurrent.ConcurrentHashMap;
025 import java.util.concurrent.ConcurrentMap;
026 import java.util.concurrent.ExecutorService;
027 import java.util.concurrent.Executors;
028 import java.util.concurrent.TimeUnit;
029
030 import org.jruby.RubyArray;
031 import org.jruby.RubyException;
032 import org.jruby.embed.ScriptingContainer;
033 import org.jruby.exceptions.RaiseException;
034 import org.jruby.runtime.builtin.IRubyObject;
035
036
040 public class SassExecutorUtil {
041
042 public static SassFile execute(String docrootDirName, String fileName) {
043 SassFile sassFile = _sassFileCache.get(fileName);
044
045 if (sassFile != null) {
046 return sassFile;
047 }
048
049 sassFile = new SassFile(docrootDirName, fileName);
050
051 SassFile previousSassFile = _sassFileCache.putIfAbsent(
052 fileName, sassFile);
053
054 if (previousSassFile != null) {
055 sassFile = previousSassFile;
056 }
057 else {
058 _executorService.submit(sassFile);
059 }
060
061 return sassFile;
062 }
063
064 public static void init(String docrootDirName, String portalCommonDirName)
065 throws IOException {
066
067 _executorService = Executors.newFixedThreadPool(
068 2,
069 new NamedThreadFactory(
070 "SassExecutor", Thread.NORM_PRIORITY,
071 SassExecutorUtil.class.getClassLoader()));
072
073 _mainThread = Thread.currentThread();
074
075 _docrootDirName = docrootDirName;
076 _portalCommonDirName = portalCommonDirName;
077
078 RubyExecutor rubyExecutor = new RubyExecutor();
079
080 rubyExecutor.setExecuteInSeparateThread(false);
081
082 _scriptingContainer = rubyExecutor.getScriptingContainer();
083
084 String rubyScript = StringUtil.read(
085 SassExecutorUtil.class.getClassLoader(),
086 "com/liferay/portal/servlet/filters/dynamiccss" +
087 "/dependencies/main.rb");
088
089 _scriptObject = _scriptingContainer.runScriptlet(rubyScript);
090 }
091
092 public static String parse(String fileName, String content) {
093 String filePath = _docrootDirName.concat(fileName);
094
095 String cssThemePath = filePath;
096
097 int pos = filePath.lastIndexOf("/css/");
098
099 if (pos >= 0) {
100 cssThemePath = filePath.substring(0, pos + 4);
101 }
102
103 try {
104 return _scriptingContainer.callMethod(
105 _scriptObject, "process",
106 new Object[] {
107 content, _portalCommonDirName, filePath, cssThemePath,
108 _TMP_DIR, false
109 },
110 String.class);
111 }
112 catch (Exception e) {
113 if (e instanceof RaiseException) {
114 RaiseException raiseException = (RaiseException)e;
115
116 RubyException rubyException = raiseException.getException();
117
118 System.err.println(
119 String.valueOf(rubyException.message.toJava(String.class)));
120
121 IRubyObject iRubyObject = rubyException.getBacktrace();
122
123 RubyArray rubyArray = (RubyArray)iRubyObject.toJava(
124 RubyArray.class);
125
126 for (int i = 0; i < rubyArray.size(); i++) {
127 Object object = rubyArray.get(i);
128
129 System.err.println(String.valueOf(object));
130 }
131 }
132 else {
133 e.printStackTrace();
134 }
135
136 _exception = new Exception("Unable to parse " + fileName, e);
137
138 _mainThread.interrupt();
139 }
140
141 return content;
142 }
143
144 public static void persist() throws Exception {
145 _executorService.shutdown();
146
147 try {
148 if (!_executorService.awaitTermination(
149 _WAIT_MINTUES, TimeUnit.MINUTES)) {
150
151 System.err.println(
152 "Abort processing Sass files after waiting " +
153 _WAIT_MINTUES + " minutes");
154 }
155 }
156 catch (InterruptedException ie) {
157 if (_exception == null) {
158 throw ie;
159 }
160
161 throw _exception;
162 }
163
164 for (SassFile sassFile : _sassFileCache.values()) {
165 sassFile.writeCacheFiles();
166
167 System.out.println(sassFile);
168 }
169 }
170
171 private static final String _TMP_DIR = SystemProperties.get(
172 SystemProperties.TMP_DIR);
173
174 private static final int _WAIT_MINTUES = 30;
175
176 private static String _docrootDirName;
177 private static Exception _exception;
178 private static ExecutorService _executorService;
179 private static Thread _mainThread;
180 private static String _portalCommonDirName;
181 private static ConcurrentMap<String, SassFile> _sassFileCache =
182 new ConcurrentHashMap<String, SassFile>();
183 private static ScriptingContainer _scriptingContainer;
184 private static Object _scriptObject;
185
186 }