1 package org_scala_tools_maven_cs;
2
3
4 import java.io.BufferedReader;
5 import java.io.File;
6 import java.io.InputStream;
7 import java.io.OutputStream;
8 import java.io.RandomAccessFile;
9 import java.io.StringReader;
10 import java.io.StringWriter;
11 import java.net.URL;
12 import java.net.URLConnection;
13 import java.util.HashSet;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.Properties;
17 import java.util.Set;
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
20
21 import org.apache.maven.plugin.logging.Log;
22 import org.codehaus.plexus.util.FileUtils;
23 import org.codehaus.plexus.util.IOUtil;
24 import org.codehaus.plexus.util.StringUtils;
25
26 import org_scala_tools_maven.ScalaMojoSupport;
27 import org_scala_tools_maven_executions.JavaMainCaller;
28 import org_scala_tools_maven_executions.JavaMainCallerByFork;
29 import org_scala_tools_maven_executions.MainHelper;
30 import org_scala_tools_maven_executions.SpawnMonitor;
31
32
33
34
35
36
37 public class ScalacsClient {
38 public static final String BOOT_PROP_RSRC = "scalacs.boot.properties";
39 public static Pattern linePattern = Pattern.compile("^-(INFO|WARN|ERROR)\t([^\t]*)\t([^\t]*)\t(.*)$");
40 public static Pattern locationPattern = Pattern.compile("([^#]*)#(\\d+),(\\d+),(\\d+),(\\d+)");
41
42 public enum Level {INFO, WARN, ERROR};
43
44 public static class LogEvent {
45 public Level level = Level.INFO;
46 public String category = "";
47 public File file = null;
48 public int line = 0;
49 public int column = 0;
50 public int offset = 0;
51 public int length = 0;
52 public CharSequence text = "";
53
54 @Override
55 public String toString() {
56 return level + "*" + category + "*" + file + "*" + line + "*" + column + "*" + offset + "*"+ length + "*"+ text+ "*";
57 }
58 }
59
60 private Log _log;
61 private ScalaMojoSupport _mojo;
62 private String[] _jvmArgs;
63 private String _csGroupId;
64 private String _csArtifactId;
65 private String _csVersion;
66
67 public ScalacsClient(ScalaMojoSupport mojo, String csGroupId, String csArtifactId, String csVersion, String[] jvmArgs) {
68 _log = mojo.getLog();
69 _mojo = mojo;
70 _csGroupId = csGroupId;
71 _csArtifactId = csArtifactId;
72 _csVersion = csVersion;
73 _jvmArgs = jvmArgs;
74 }
75
76 public List<LogEvent> parse(String response) throws Exception {
77 List<LogEvent> back = new LinkedList<LogEvent>();
78 BufferedReader in = new BufferedReader(new StringReader(response));
79 try {
80 for(String l = in.readLine(); l != null; l =in.readLine()){
81 Matcher m = linePattern.matcher(l);
82 if (m.matches()) {
83 LogEvent e = new LogEvent();
84 e.level = Level.valueOf(m.group(1).toUpperCase());
85 e.category = m.group(2);
86 e.text = m.group(4).replace('$', '\n');
87 Matcher ml = locationPattern.matcher(m.group(3));
88 if (ml.matches()) {
89 e.file = new File(ml.group(1));
90 e.line = Integer.parseInt(m.group(2));
91 e.column = Integer.parseInt(m.group(3));
92 e.offset = Integer.parseInt(m.group(4));
93 e.length = Integer.parseInt(m.group(5));
94 }
95 back.add(e);
96 }
97 }
98 } finally {
99 IOUtil.close(in);
100 }
101 return back;
102 }
103
104
105
106
107
108
109 public String sendRequestCreateOrUpdate(String yamlDef) throws Exception {
110 String back = "";
111 try {
112 back = sendRequest("createOrUpdate", yamlDef);
113 } catch (java.net.ConnectException exc) {
114 startNewServer();
115 back = sendRequest("createOrUpdate", yamlDef);
116 }
117 return back;
118 }
119
120
121
122
123
124 public String sendRequestRemove(String projectName) throws Exception {
125 return sendRequest("remove?p=" + projectName, null);
126 }
127
128
129
130
131
132
133 public String sendRequestCompile(String projectName, boolean withDependencies, boolean withDependent) throws Exception {
134 StringBuilder query = new StringBuilder("compile");
135 if (StringUtils.isNotEmpty(projectName)) {
136 query.append("?p=").append(projectName);
137 if (!withDependencies) {
138 query.append("&noDependencies=true");
139 }
140
141 if (!withDependent) {
142 query.append("&noDependent=true");
143 }
144 }
145 return sendRequest(query.toString(), null);
146 }
147
148
149
150
151
152
153 public String sendRequestClean() throws Exception {
154 return sendRequest("clean", null);
155 }
156
157
158
159
160
161
162 public String sendRequestStop() throws Exception {
163 return sendRequest("stop", null);
164 }
165
166 protected String sendRequest(String action, String data) throws Exception {
167 URL url = new URL("http://127.0.0.1:27616/" + action);
168 traceUrl(url);
169 URLConnection cnx = url.openConnection();
170 cnx.setDoOutput(StringUtils.isNotEmpty(data));
171 cnx.setDoInput(true);
172 if (StringUtils.isNotEmpty(data)) {
173 OutputStream os = cnx.getOutputStream();
174 try {
175 IOUtil.copy(new StringReader(data), os);
176 } finally {
177 IOUtil.close(os);
178 }
179 }
180 InputStream is = cnx.getInputStream();
181 try {
182 String back = IOUtil.toString(is);
183 return back;
184 } finally {
185 IOUtil.close(is);
186 }
187 }
188
189
190
191
192
193
194 public void traceUrl(URL url) throws Exception {
195 String msg = "request : " + url;
196 if (_mojo.displayCmd) {
197 _log.info(msg);
198 } else {
199 _log.debug(msg);
200 }
201 }
202
203
204
205
206
207
208 public void startNewServer() throws Exception{
209 _log.info("start scala-tools-server...");
210 Set<String> classpath = new HashSet<String>();
211
212
213
214 _mojo.addToClasspath("org.scala-tools.sbt", "sbt-launch", "0.7.2", classpath, true);
215 String[] jvmArgs = new String[(_jvmArgs == null)?1:_jvmArgs.length + 1];
216 File installDir = new File(System.getProperty("user.home"), ".sbt-launch");
217 jvmArgs[0] = "-Dsbt.boot.properties="+ installConf(new File(installDir, _csArtifactId + "-"+ _csVersion +".boot.properties")).getCanonicalPath();
218 if (_jvmArgs != null) {
219 System.arraycopy(_jvmArgs, 0, jvmArgs, 1, _jvmArgs.length);
220 }
221 FileTailer tailer = new FileTailer(new File(installDir, "update.log"));
222 boolean started = false;
223 try {
224 JavaMainCaller jcmd = new JavaMainCallerByFork(_mojo, "xsbt.boot.Boot", MainHelper.toMultiPath(classpath.toArray(new String[classpath.size()])), jvmArgs, null, false);
225 SpawnMonitor mon = jcmd.spawn(_mojo.displayCmd);
226 for(int i = 60; i>0 && !started && mon.isRunning(); i--) {
227 try {
228 if (_mojo.displayCmd) {
229 System.out.print(tailer.whatNew());
230 } else {
231 System.out.print(".");
232 }
233 Thread.sleep(1000);
234 sendRequest("ping", null);
235 started = true;
236 } catch (java.net.ConnectException exc) {
237 started = false;
238 }
239 }
240 if (_mojo.displayCmd) {
241 System.out.print(tailer.whatNew());
242 }
243 System.out.println("");
244 } finally {
245 tailer.close();
246 }
247 if (!started) {
248 throw new IllegalStateException("can't start and connect to scalacs");
249 }
250 _mojo.getLog().info("scalacs connected");
251 }
252
253 private File installConf(File scalaCsBootConf) throws Exception {
254 if (!scalaCsBootConf.isFile()) {
255 scalaCsBootConf.getParentFile().mkdirs();
256 InputStream is = null;
257 StringWriter sw = new StringWriter();
258 try {
259 is = this.getClass().getResourceAsStream(BOOT_PROP_RSRC);
260 if (is == null) {
261 is = Thread.currentThread().getContextClassLoader().getResourceAsStream(BOOT_PROP_RSRC);
262 }
263 if (is == null) {
264 String abspath = "/" + this.getClass().getPackage().getName().replace('.', '/') + "/" + BOOT_PROP_RSRC;
265 is = Thread.currentThread().getContextClassLoader().getResourceAsStream(abspath);
266 if (is == null) {
267 throw new IllegalStateException("can't find " + abspath + " in the classpath");
268 }
269 }
270 IOUtil.copy(is, sw);
271 } finally {
272 IOUtil.close(is);
273 IOUtil.close(sw);
274 }
275 Properties p = new Properties(System.getProperties());
276 p.setProperty("scalacs.groupId", _csGroupId);
277 p.setProperty("scalacs.artifactId", _csArtifactId);
278 p.setProperty("scalacs.version", _csVersion);
279 p.setProperty("scalacs.directory", scalaCsBootConf.getParentFile().getCanonicalPath());
280 String cfg = StringUtils.interpolate(sw.toString(), p);
281 FileUtils.fileWrite(scalaCsBootConf.getCanonicalPath(), "UTF-8", cfg);
282 }
283 return scalaCsBootConf;
284 }
285
286 private static class FileTailer {
287 private long _filePointer;
288 private RandomAccessFile _raf;
289 private File _file;
290 public FileTailer(File f) throws Exception {
291 _file = f;
292 _filePointer = f.length();
293 _raf = null;
294 }
295
296 public CharSequence whatNew() throws Exception {
297 StringBuilder back = new StringBuilder();
298 if (_raf == null && _file.isFile()) {
299 _raf = new RandomAccessFile(_file, "r" );
300 }
301 if (_raf != null) {
302
303 long fileLength = _file.length();
304 if( fileLength < _filePointer ) {
305
306
307 close();
308 _raf = new RandomAccessFile(_file, "r" );
309 _filePointer = 0;
310 }
311
312 if( fileLength > _filePointer ) {
313
314 _raf.seek( _filePointer );
315
316
317 String line = null;
318 while( (line = _raf.readLine())!= null ) {
319 back.append( line ).append('\n');
320 }
321 _filePointer = _raf.getFilePointer();
322 }
323 }
324 return back;
325 }
326 public void close() {
327 try {
328 if (_raf != null) {
329 _raf.close();
330 _raf = null;
331 }
332 } catch(Exception e) {
333
334 }
335 }
336 }
337 }