golf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/.gitignore +10 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +21 -0
  4. data/Rakefile +12 -0
  5. data/bin/golf +8 -0
  6. data/golf-java/README.markdown +41 -0
  7. data/golf-java/THANKS.markdown +14 -0
  8. data/golf-java/build/classes.zip +0 -0
  9. data/golf-java/build/com/thinkminimo/golf/GolfResource$MimeMapping$MimeMappingSingleton.class +0 -0
  10. data/golf-java/build/com/thinkminimo/golf/GolfResource$MimeMapping.class +0 -0
  11. data/golf-java/build/com/thinkminimo/golf/GolfResource.class +0 -0
  12. data/golf-java/build/com/thinkminimo/golf/GolfServlet$1.class +0 -0
  13. data/golf-java/build/com/thinkminimo/golf/GolfServlet$GolfContext.class +0 -0
  14. data/golf-java/build/com/thinkminimo/golf/GolfServlet$GolfParams.class +0 -0
  15. data/golf-java/build/com/thinkminimo/golf/GolfServlet$GolfSession.class +0 -0
  16. data/golf-java/build/com/thinkminimo/golf/GolfServlet$PermanentRedirectException.class +0 -0
  17. data/golf-java/build/com/thinkminimo/golf/GolfServlet$RedirectException.class +0 -0
  18. data/golf-java/build/com/thinkminimo/golf/GolfServlet$StoredJSVM.class +0 -0
  19. data/golf-java/build/com/thinkminimo/golf/GolfServlet.class +0 -0
  20. data/golf-java/build/com/thinkminimo/golf/JsonpTunnel.class +0 -0
  21. data/golf-java/build/com/thinkminimo/golf/Main$1.class +0 -0
  22. data/golf-java/build/com/thinkminimo/golf/Main$RingList.class +0 -0
  23. data/golf-java/build/com/thinkminimo/golf/Main.class +0 -0
  24. data/golf-java/build/com/thinkminimo/golf/ProxyServlet.class +0 -0
  25. data/golf-java/build/com/yahoo/platform/yui/compressor/CssCompressor.class +0 -0
  26. data/golf-java/build/com/yahoo/platform/yui/compressor/JarClassLoader.class +0 -0
  27. data/golf-java/build/com/yahoo/platform/yui/compressor/JavaScriptCompressor.class +0 -0
  28. data/golf-java/build/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.class +0 -0
  29. data/golf-java/build/com/yahoo/platform/yui/compressor/JavaScriptToken.class +0 -0
  30. data/golf-java/build/com/yahoo/platform/yui/compressor/ScriptOrFnScope.class +0 -0
  31. data/golf-java/build/depends.zip +0 -0
  32. data/golf-java/build/golf +2 -0
  33. data/golf-java/build/org/json/JSONArray.class +0 -0
  34. data/golf-java/build/org/json/JSONException.class +0 -0
  35. data/golf-java/build/org/json/JSONObject$1.class +0 -0
  36. data/golf-java/build/org/json/JSONObject$Null.class +0 -0
  37. data/golf-java/build/org/json/JSONObject.class +0 -0
  38. data/golf-java/build/org/json/JSONString.class +0 -0
  39. data/golf-java/build/org/json/JSONStringer.class +0 -0
  40. data/golf-java/build/org/json/JSONTokener.class +0 -0
  41. data/golf-java/build/org/json/JSONWriter.class +0 -0
  42. data/golf-java/build/resources.zip +0 -0
  43. data/golf-java/build.xml +174 -0
  44. data/golf-java/dist/golf.zip +0 -0
  45. data/golf-java/lib/ant-launcher.jar +0 -0
  46. data/golf-java/lib/ant.jar +0 -0
  47. data/golf-java/lib/commons-codec-1.4.jar +0 -0
  48. data/golf-java/lib/commons-collections-3.2.1.jar +0 -0
  49. data/golf-java/lib/commons-fileupload-1.2.1.jar +0 -0
  50. data/golf-java/lib/commons-httpclient-3.1.jar +0 -0
  51. data/golf-java/lib/commons-io-1.4.jar +0 -0
  52. data/golf-java/lib/commons-lang-2.4.jar +0 -0
  53. data/golf-java/lib/commons-logging-1.1.1.jar +0 -0
  54. data/golf-java/lib/cssparser-0.9.5.jar +0 -0
  55. data/golf-java/lib/getopt-0.1-dev.jar +0 -0
  56. data/golf-java/lib/htmlunit-2.6.jar +0 -0
  57. data/golf-java/lib/htmlunit-core-js-2.6.jar +0 -0
  58. data/golf-java/lib/java-xmlbuilder-1.jar +0 -0
  59. data/golf-java/lib/jets3t-0.7.0.jar +0 -0
  60. data/golf-java/lib/jetty-6.1.15.jar +0 -0
  61. data/golf-java/lib/jetty-util-6.1.15.jar +0 -0
  62. data/golf-java/lib/log4j-1.2.15.jar +0 -0
  63. data/golf-java/lib/nekohtml-1.9.13.jar +0 -0
  64. data/golf-java/lib/sac-1.3.jar +0 -0
  65. data/golf-java/lib/serializer-2.7.1.jar +0 -0
  66. data/golf-java/lib/servlet-api-2.5-20081211.jar +0 -0
  67. data/golf-java/lib/xalan-2.7.1.jar +0 -0
  68. data/golf-java/lib/xercesImpl-2.9.1.jar +0 -0
  69. data/golf-java/lib/xml-apis-1.3.04.jar +0 -0
  70. data/golf-java/resources/app_error.html +60 -0
  71. data/golf-java/resources/error.html +29 -0
  72. data/golf-java/resources/forcebot.txt +0 -0
  73. data/golf-java/resources/forceclient.txt +0 -0
  74. data/golf-java/resources/forceproxy.txt +0 -0
  75. data/golf-java/resources/head.html +0 -0
  76. data/golf-java/resources/jquery.address.js +439 -0
  77. data/golf-java/resources/jquery.golf.js +945 -0
  78. data/golf-java/resources/jquery.js +4376 -0
  79. data/golf-java/resources/jsdetect.html +23 -0
  80. data/golf-java/resources/loading.gif +0 -0
  81. data/golf-java/resources/new.html +45 -0
  82. data/golf-java/resources/new.static.html +45 -0
  83. data/golf-java/resources/noscript.forceclient.html +11 -0
  84. data/golf-java/resources/noscript.html +18 -0
  85. data/golf-java/resources/noscript.static.html +15 -0
  86. data/golf-java/resources/project.xml +21 -0
  87. data/golf-java/resources/proxy_project.xml +11 -0
  88. data/golf-java/resources/proxy_web.xml +40 -0
  89. data/golf-java/resources/static_project.xml +37 -0
  90. data/golf-java/resources/version +1 -0
  91. data/golf-java/resources/web.xml +36 -0
  92. data/golf-java/resources/welcome2golf.html +50 -0
  93. data/golf-java/src/com/thinkminimo/golf/GolfResource.java +582 -0
  94. data/golf-java/src/com/thinkminimo/golf/GolfServlet.java +1055 -0
  95. data/golf-java/src/com/thinkminimo/golf/JsonpTunnel.java +86 -0
  96. data/golf-java/src/com/thinkminimo/golf/Main.java +1299 -0
  97. data/golf-java/src/com/thinkminimo/golf/ProxyServlet.java +577 -0
  98. data/golf-java/src/com/yahoo/platform/yui/compressor/CssCompressor.java +188 -0
  99. data/golf-java/src/com/yahoo/platform/yui/compressor/JarClassLoader.java +158 -0
  100. data/golf-java/src/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java +1281 -0
  101. data/golf-java/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java +55 -0
  102. data/golf-java/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java +28 -0
  103. data/golf-java/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java +160 -0
  104. data/golf-java/src/org/json/JSONArray.java +934 -0
  105. data/golf-java/src/org/json/JSONException.java +27 -0
  106. data/golf-java/src/org/json/JSONObject.java +1550 -0
  107. data/golf-java/src/org/json/JSONString.java +18 -0
  108. data/golf-java/src/org/json/JSONStringer.java +78 -0
  109. data/golf-java/src/org/json/JSONTokener.java +422 -0
  110. data/golf-java/src/org/json/JSONWriter.java +323 -0
  111. data/golf-java/test/client/golftest/README.markdown +3 -0
  112. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.css +114 -0
  113. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.html +17 -0
  114. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.js +81 -0
  115. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/asc.gif +0 -0
  116. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/bg.gif +0 -0
  117. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/desc.gif +0 -0
  118. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/test/test.html +1 -0
  119. data/golf-java/test/client/golftest/controller.js +131 -0
  120. data/golf-java/test/client/golftest/forceclient.txt +0 -0
  121. data/golf-java/test/client/golftest/forceproxy.txt +0 -0
  122. data/golf-java/test/client/golftest/head.html +1 -0
  123. data/golf-java/test/client/golftest/scripts/jquery.tablesort.js +75 -0
  124. data/golf-java/test/client/golftest/styles/style.css +18 -0
  125. data/golf-java/test/client/golftest/test/test.html +1 -0
  126. data/golf.gemspec +24 -0
  127. data/lib/golf/cli.rb +32 -0
  128. data/lib/golf/compiler.rb +12 -0
  129. data/lib/golf/rack.rb +17 -0
  130. data/lib/golf/version.rb +3 -0
  131. data/lib/golf.rb +6 -0
  132. data/template/Gemfile +5 -0
  133. data/template/config.ru +7 -0
  134. data/template/golfapp/components/Test/Test.html +22 -0
  135. data/template/golfapp/components/Test/Test.res/myfile.txt +1 -0
  136. data/template/golfapp/controller.js +10 -0
  137. data/template/golfapp/plugins/mylib.js +12 -0
  138. data/test/test_compiler.rb +10 -0
  139. data/test/test_helper.rb +6 -0
  140. data/test/test_rack.rb +17 -0
  141. metadata +208 -0
@@ -0,0 +1,1299 @@
1
+ package com.thinkminimo.golf;
2
+
3
+ import com.thinkminimo.getopt.GetOpt;
4
+
5
+ import org.json.*;
6
+
7
+ import java.io.*;
8
+ import java.util.*;
9
+ import java.util.regex.Pattern;
10
+ import java.net.URL;
11
+ import java.net.URI;
12
+ import java.net.URISyntaxException;
13
+ import java.net.URLClassLoader;
14
+ import java.rmi.server.UID;
15
+ import java.security.NoSuchAlgorithmException;
16
+
17
+ import java.lang.reflect.Method;
18
+ import java.lang.reflect.Constructor;
19
+
20
+ import net.sourceforge.htmlunit.corejs.javascript.ErrorReporter;
21
+ import net.sourceforge.htmlunit.corejs.javascript.EvaluatorException;
22
+ import com.yahoo.platform.yui.compressor.CssCompressor;
23
+ import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
24
+
25
+ import javax.servlet.ServletException;
26
+ import javax.servlet.http.HttpServlet;
27
+ import javax.servlet.http.HttpServletRequest;
28
+ import javax.servlet.http.HttpServletResponse;
29
+
30
+ import org.jets3t.service.CloudFrontService;
31
+ import org.jets3t.service.S3Service;
32
+ import org.jets3t.service.S3ServiceException;
33
+ import org.jets3t.service.acl.AccessControlList;
34
+ import org.jets3t.service.acl.GroupGrantee;
35
+ import org.jets3t.service.acl.Permission;
36
+ import org.jets3t.service.model.S3Bucket;
37
+ import org.jets3t.service.model.S3Object;
38
+ import org.jets3t.service.model.cloudfront.Distribution;
39
+ import org.jets3t.service.model.cloudfront.DistributionConfig;
40
+ import org.jets3t.service.impl.rest.httpclient.RestS3Service;
41
+ import org.jets3t.service.security.AWSCredentials;
42
+
43
+ import org.apache.tools.ant.*;
44
+ import org.apache.tools.ant.taskdefs.*;
45
+ import org.apache.tools.ant.types.resources.*;
46
+
47
+ import org.mortbay.log.Log;
48
+ import org.mortbay.jetty.Handler;
49
+ import org.mortbay.jetty.HandlerContainer;
50
+ import org.mortbay.jetty.Server;
51
+ import org.mortbay.jetty.handler.HandlerList;
52
+ import org.mortbay.jetty.handler.ContextHandlerCollection;
53
+ import org.mortbay.jetty.handler.DefaultHandler;
54
+ import org.mortbay.jetty.handler.ResourceHandler;
55
+ import org.mortbay.jetty.handler.RequestLogHandler;
56
+ import org.mortbay.jetty.servlet.Context;
57
+ import org.mortbay.jetty.servlet.ServletHolder;
58
+ import org.mortbay.jetty.servlet.DefaultServlet;
59
+ import org.mortbay.jetty.webapp.WebAppContext;
60
+ import org.mortbay.thread.QueuedThreadPool;
61
+
62
+ public class Main
63
+ {
64
+ public static class RingList<T> extends ArrayList <T> {
65
+ private int next = 0;
66
+ public T next() {
67
+ return get(next++ % size());
68
+ }
69
+ public void reset() {
70
+ next = 0;
71
+ }
72
+ }
73
+
74
+ public static final String AWS_URL = "s3.amazonaws.com";
75
+ public static final int NUM_CFDOMAINS = 1;
76
+ public static final int NUM_VMPOOL = 20;
77
+ public static final int NUM_VMEXPIRE = 10;
78
+ public static final int JETTY_PORT = 4653;
79
+ private static final int BUF_SIZE = 1024;
80
+
81
+ public static final String NEW_HTML = "new.html";
82
+ public static final String NEW_FC_HTML = "new.fc.html";
83
+ public static final String NEW_ST_HTML = "new.static.html";
84
+ public static final String ERROR_HTML = "error.html";
85
+ public static final String HEAD_HTML = "head.html";
86
+ public static final String JSDETECT_HTML = "jsdetect.html";
87
+ public static final String COMPONENTS_JS = "components.js";
88
+ public static final String CONTROLLER_JS = "controller.js";
89
+ public static final String JQUERY_JS = "jquery.js";
90
+ public static final String JQUERY_GOLF_JS = "jquery.golf.js";
91
+ public static final String JQUERY_HIST_JS = "jquery.address.js";
92
+ public static final String FORCEPROXY_TXT = "forceproxy.txt";
93
+ public static final String FORCECLIENT_TXT = "forceclient.txt";
94
+ public static final String FORCEBOT_TXT = "forcebot.txt";
95
+ public static final String NOSCRIPT_HTML = "noscript.html";
96
+ public static final String NOSCRIPT_FC_HTML= "noscript.forceclient.html";
97
+ public static final String NOSCRIPT_ST_HTML= "noscript.static.html";
98
+ public static final String LOADING_GIF = "loading.gif";
99
+
100
+ public static final String DIR_COMPONENTS = "components";
101
+ public static final String DIR_MODULES = "plugins";
102
+ public static final String DIR_SCRIPTS = "scripts";
103
+ public static final String DIR_STYLES = "styles";
104
+
105
+ private static GetOpt o = null;
106
+ private static RingList<String> mCfDomains = null;
107
+ private static String mNewHtml = null;
108
+
109
+ private static String mAppName = null;
110
+ private static String mAppVersion = null;
111
+
112
+ private static HashMap<String, String> mApps = null;
113
+ private static HashMap<String, String> mBackends = null;
114
+
115
+ private AWSCredentials mAwsKeys = null;
116
+ private RestS3Service mS3svc = null;
117
+ private CloudFrontService mCfsvc = null;
118
+ private S3Bucket mBucket = null;
119
+ private AccessControlList mAcl = null;
120
+
121
+ public Main(String[] argv) throws Exception {
122
+ mApps = new HashMap<String, String>();
123
+ mBackends = new HashMap<String, String>();
124
+ mCfDomains = new RingList<String>();
125
+ mAppVersion = getResourceAsString("version").replaceFirst("\n", "");
126
+
127
+ // process single flag command lines
128
+
129
+ if (argv[0].equals("--version")) {
130
+ System.out.println(mAppVersion);
131
+ System.exit(0);
132
+ }
133
+
134
+ // Command line parser setup
135
+
136
+ o = new GetOpt("golf", argv);
137
+
138
+ o.addFlag(
139
+ "version",
140
+ "Display golf application server version info and exit."
141
+ ).addSection(
142
+ "GENERAL OPTIONS",
143
+ "General configuration of the golf application server. These options "+
144
+ "will be rolled into the war file (where appropriate) if deploying to "+
145
+ "production, or used in the built-in servlet container for devmode "+
146
+ "operation."
147
+ ).addOpt(
148
+ "port",
149
+ "Set the port the server will listen on (optional, devmode only)."
150
+ ).addOpt(
151
+ "displayname",
152
+ "The display name to use for deploying as a war file into a servlet "+
153
+ "container (optional)."
154
+ ).addOpt(
155
+ "description",
156
+ "Description of app when deploying as a war file into a servlet "+
157
+ "container (optional)."
158
+ ).addOpt(
159
+ "pool-size",
160
+ "How many concurrent proxymode client virtual machines to allow."
161
+ ).addOpt(
162
+ "pool-expire",
163
+ "Minimum idle time (seconds) before a proxymode client virtual "+
164
+ "machine can be scavenged."
165
+ ).addOpt(
166
+ "static",
167
+ "Destination directory for a static app deployment. Static apps are "+
168
+ "compiled into pure HTML+JS. There is no proxy support with this option."
169
+ ).addSection(
170
+ "AMAZON WEB SERVICES CONFIGURATION OPTIONS",
171
+ "The awspublic and awsprivate options provide the golf server with "+
172
+ "your AWS credentials. This enables it to automatically upload the "+
173
+ "application to CloudFront when deploying to production. AWS is not "+
174
+ "used in devmode."
175
+ ).addOpt(
176
+ "awspublic",
177
+ "The amazon aws access key ID to use for cloudfront caching (required "+
178
+ "when using AWS)."
179
+ ).addOpt(
180
+ "awsprivate",
181
+ "The amazon aws secret access key corresponding to the aws access "+
182
+ "key ID specified with the --awspublic option (required when using AWS)."
183
+ ).addOpt(
184
+ "cloudfronts",
185
+ "How many CloudFront distributions to create (optional). This may be "+
186
+ "useful for getting browsers to load things in parallel rather than "+
187
+ "one at a time. On the other hand, it may be useless."
188
+ ).addSection(
189
+ "HTTP PROXY CONFIGURATION OPTIONS",
190
+ "The golf application server ships with a built-in HTTP proxy servlet "+
191
+ "that can be used to provide access to backend web services without "+
192
+ "needing to resort to using JSONP or the 'window.name' hack."
193
+ ).addOpt(
194
+ "proxyhost",
195
+ "The remote URI that the HTTP proxy will relay requests to. This will "+
196
+ "produce a war file containing the configured proxy servlet and exit, "+
197
+ "instead of starting the embedded servlet container."
198
+ ).addOpt(
199
+ "proxyparams",
200
+ "Parameters to add to the query string of every request sent by the "+
201
+ "HTTP proxy to the remote host. This can be used to pass tokens that "+
202
+ "the client shouldn't have access to, and things like that."
203
+ ).addOpt(
204
+ "proxymaxupload",
205
+ "The maximum file upload size for HTTP proxy requests (optional, in "+
206
+ "bytes)."
207
+ ).addSection(
208
+ "WAR FILE CONFIGURATION OPTIONS",
209
+ "The golf application server jar file is able to roll a golf "+
210
+ "application into a war file for deployment to a servlet container "+
211
+ "for production. These options govern how this is done."
212
+ ).addFlag(
213
+ "compress-js",
214
+ "Whether to yuicompress javascript resource files (production only)."
215
+ ).addFlag(
216
+ "compress-css",
217
+ "Whether to yuicompress css resource files (production only)."
218
+ ).addFlag(
219
+ "war",
220
+ "If present, create war file instead of starting embedded servlet "+
221
+ "container."
222
+ ).addArg(
223
+ "approot|proxypath",
224
+ "The location of the golf app root directory, or when building HTTP "+
225
+ "proxy, the desired name of the resulting war file (.war extension "+
226
+ "will be added). When this argument specifies a golf approot, the "+
227
+ "application context path (or war file name, in the case of the --war "+
228
+ "option) is derived from the basename of the specified directory."
229
+ ).addVarArg(
230
+ "backend [backend...]",
231
+ "The backend webapp war file or approot (not used when building HTTP "+
232
+ "proxy war files). Zero or more war files or directories may be "+
233
+ "specified here. The context path they are deployed to is taken from "+
234
+ "the basename of the war file or directory."
235
+ ).addExample(
236
+ "RUN LOCAL DEVMODE SERVER WITH NO BACKEND",
237
+ "java -jar golf.jar ./apps/demo",
238
+ "This starts the golf application server, running the golf application "+
239
+ "locally from the embedded servlet container. The application will be "+
240
+ "accessible at the URL http://localhost:4653/demo/, and the approot is "+
241
+ "set to the ./apps/demo/ directory."
242
+ ).addExample(
243
+ "RUN LOCAL DEVMODE SERVER WITH BACKEND",
244
+ "java -jar golf.jar ./apps/demo data1.war data2.war",
245
+ "This starts the golf application server, locally, as in the previous "+
246
+ "example, accessible at the URL http://localhost:4653/demo/, etc. "+
247
+ "Additionally, data1.war and data2.war (backend applications) will be "+
248
+ "deployed to the /data1/ and /data2/ context paths."
249
+ ).addExample(
250
+ "PREPARE WAR FILE FOR DEPLOYMENT TO PRODUCTION",
251
+ "java -jar golf.jar --war ./apps/demo",
252
+ "Instead of starting the local golf application server, a war file is "+
253
+ "produced containing the golf application, in this case 'demo.war' is "+
254
+ "the resulting file. This war file can then be deployed to the "+
255
+ "production servlet container."
256
+ ).addExample(
257
+ "PREPARE WAR FILE FOR DEPLOYMENT TO PRODUCTION WITH AWS",
258
+ "java -jar golf.jar --displayname 'My Golf App' \\\n"+
259
+ " --awspublic GKI69AJ344JLNT92X1QQ \\\n"+
260
+ " --awsprivate ke9S3CwVzLW9B21/HrkLiQfXEpoeGHwNDTlfBW5J \\\n"+
261
+ " --war ./apps/demo",
262
+ "As in the previous example, a war file is produced instead of starting "+
263
+ "the local golf application server. Additionally, the golf application "+
264
+ "is uploaded to Amazon's s3 service, and a CloudFront distribution is "+
265
+ "created. The golf app in the resulting war file will automatically "+
266
+ "load the frontend from CloudFront, rather than from the golf server."
267
+ ).addExample(
268
+ "CREATE A HTTP PROXY",
269
+ "java -jar golf.jar --proxyhost www.example.com:8080/doit/ \\\n"+
270
+ " --proxyparams 'user=myname&pass=secret' data",
271
+ "This produces a HTTP proxy servlet configured to proxy HTTP requests "+
272
+ "to the specified remote URI, instead of starting the local embedded "+
273
+ "servlet container. The resulting war file will be saved to 'data.war'."
274
+ );
275
+
276
+ // default values for command line options
277
+
278
+ o.setOpt("port", String.valueOf(JETTY_PORT));
279
+ o.setOpt("displayname", "untitled web application");
280
+ o.setOpt("description", "powered by golf: http://thinkminimo.com");
281
+ o.setOpt("devmode", "false");
282
+ o.setOpt("awspublic", null);
283
+ o.setOpt("awsprivate", null);
284
+ o.setOpt("proxyhost", null);
285
+ o.setOpt("proxyparams", "");
286
+ o.setOpt("proxymaxupload",String.valueOf(10*1024*1024));
287
+ o.setOpt("pool-size", String.valueOf(NUM_VMPOOL));
288
+ o.setOpt("pool-expire", String.valueOf(NUM_VMEXPIRE));
289
+ o.setOpt("cloudfronts", String.valueOf(NUM_CFDOMAINS));
290
+ o.setOpt("cfdomains", "[]");
291
+ o.setOpt("compress-js", "false");
292
+ o.setOpt("compress-css", "false");
293
+ o.setOpt("war", "false");
294
+
295
+ // parse command line
296
+ try {
297
+ o.go();
298
+ } catch (Exception e) {
299
+ System.exit(1);
300
+ }
301
+
302
+ // command line option validation
303
+
304
+ if (o.getOpt("port") != null) {
305
+ try {
306
+ Integer.parseInt(o.getOpt("port"));
307
+ } catch (NumberFormatException e) {
308
+ usage(e.getMessage());
309
+ }
310
+ }
311
+
312
+ // start work
313
+
314
+ mAppName = (new File(o.getOpt("approot|proxypath")))
315
+ .getCanonicalFile().getName().replaceFirst("\\.war$", "");
316
+ mApps.put(mAppName, o.getOpt("approot|proxypath"));
317
+
318
+ while (o.getExtra().size() > 0) {
319
+ String path = o.getExtra().remove(0);
320
+ File f = new File(path);
321
+ String name = f.getCanonicalFile().getName().replaceFirst("\\.war$", "");
322
+ mBackends.put(name, path);
323
+ }
324
+
325
+ try {
326
+ if (o.getOpt("proxyhost") != null)
327
+ doProxyWarfile();
328
+ else if (o.getFlag("war"))
329
+ doWarfile();
330
+ else if (o.getOpt("static") != null)
331
+ doStatic();
332
+ else
333
+ doServer();
334
+ } catch (Exception e) {
335
+ System.err.println("golf: "+e.getMessage());
336
+ System.exit(1);
337
+ }
338
+
339
+ System.exit(0);
340
+ }
341
+
342
+ public static void main(String[] argv) {
343
+ try {
344
+ Main m = new Main(argv);
345
+ } catch (Exception e) {
346
+ System.exit(1);
347
+ }
348
+ }
349
+
350
+ private void prepareAws() throws Exception {
351
+ mAwsKeys =
352
+ new AWSCredentials(o.getOpt("awspublic"), o.getOpt("awsprivate"));
353
+ mS3svc = new RestS3Service(mAwsKeys);
354
+
355
+ while (true) {
356
+ mBucket = mS3svc.getOrCreateBucket(randName(mAppName));
357
+ long nowTime = (new Date()).getTime();
358
+ long bktTime = mBucket.getCreationDate().getTime();
359
+ long oneMin = 1L * 60L * 1000L;
360
+ if (nowTime - bktTime < oneMin)
361
+ break;
362
+ }
363
+
364
+ mAcl = mS3svc.getBucketAcl(mBucket);
365
+ mAcl.grantPermission(
366
+ GroupGrantee.ALL_USERS,
367
+ Permission.PERMISSION_READ
368
+ );
369
+
370
+ mBucket.setAcl(mAcl);
371
+ mS3svc.putBucketAcl(mBucket);
372
+ }
373
+
374
+ private void doCloudFront() throws Exception {
375
+ mCfsvc = new CloudFrontService(mAwsKeys);
376
+
377
+ String orig = mBucket.getName() + "." + AWS_URL;
378
+ String cmnt;
379
+ if (o.getOpt("description") != null)
380
+ cmnt = o.getOpt("description");
381
+ else if (o.getOpt("displayname") != null)
382
+ cmnt = o.getOpt("displayname");
383
+ else
384
+ cmnt = mAppName;
385
+
386
+ JSONArray json = new JSONArray();
387
+
388
+ for (int i=0; i<Integer.valueOf(o.getOpt("cloudfronts")); i++) {
389
+ Distribution dist =
390
+ mCfsvc.createDistribution(orig, null, null, cmnt, true);
391
+ String domain = "http://"+dist.getDomainName()+"/";
392
+ mCfDomains.add(domain);
393
+ json.put(domain);
394
+ }
395
+
396
+ o.setOpt("cfdomains", json.toString());
397
+ }
398
+
399
+ private void doAws() throws Exception {
400
+ try {
401
+ System.err.print("Preparing S3 bucket....................");
402
+ if (o.getOpt("awspublic") != null && o.getOpt("awsprivate") != null) {
403
+ prepareAws();
404
+ System.err.println("done.");
405
+ } else {
406
+ System.err.println("skipped.");
407
+ }
408
+
409
+ System.err.print("Creating CloudFront distributions..");
410
+ if (o.getOpt("awspublic") != null && o.getOpt("awsprivate") != null) {
411
+ doCloudFront();
412
+ System.err.println("done.");
413
+ } else {
414
+ System.err.println("skipped.");
415
+ }
416
+
417
+ System.err.print("Compiling components...................");
418
+ cacheComponentsFile();
419
+ System.err.println("done.");
420
+
421
+ System.err.print("Creating new.html template file........");
422
+ cacheNewDotHtmlFile();
423
+ System.err.println("done.");
424
+
425
+ System.err.print("Uploading jar resources................");
426
+ if (o.getOpt("awspublic") != null && o.getOpt("awsprivate") != null) {
427
+ cacheJarResourcesAws();
428
+ System.err.println("done.");
429
+ } else {
430
+ System.err.println("skipped.");
431
+ }
432
+
433
+ System.err.print("Uploading resource files...............");
434
+ if (o.getOpt("awspublic") != null && o.getOpt("awsprivate") != null) {
435
+ cacheResourcesAws(new File(o.getOpt("approot|proxypath")), "");
436
+ System.err.println("done.");
437
+ } else {
438
+ System.err.println("skipped.");
439
+ }
440
+ } catch (Exception e) {
441
+ System.err.println("fail.");
442
+ throw new Exception(e);
443
+ }
444
+ }
445
+
446
+ private void doProxyWarfile() throws Exception {
447
+ String name = mAppName;
448
+ String host = o.getOpt("proxyhost");
449
+ int port = 80;
450
+ String path = "";
451
+
452
+ if (!host.startsWith("http://"))
453
+ host = "http://"+host;
454
+
455
+ URI uri = new URI(host);
456
+
457
+ host = uri.getHost();
458
+ port = uri.getPort() == -1 ? port : uri.getPort();
459
+ path = uri.getPath() == null ? path : uri.getPath();
460
+
461
+ doProxyAnt(name, host, port, path);
462
+ }
463
+
464
+ private void doWarfile() throws Exception {
465
+ o.setOpt("devmode", "false");
466
+ doAws();
467
+ doAnt();
468
+ }
469
+
470
+ private void doStatic() throws Exception {
471
+ cacheComponentsFile();
472
+ cacheNewDotHtmlFile();
473
+ doStaticAnt();
474
+ }
475
+
476
+ private void doServer() throws Exception {
477
+ o.setOpt("devmode", "true");
478
+ Server server = new Server(Integer.valueOf(o.getOpt("port")));
479
+
480
+ cacheComponentsFile();
481
+ cacheNewDotHtmlFile();
482
+
483
+ QueuedThreadPool threadPool = new QueuedThreadPool();
484
+ threadPool.setMaxThreads(100);
485
+ server.setThreadPool(threadPool);
486
+
487
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
488
+ HandlerList handlers = new HandlerList();
489
+
490
+ for (String app: mApps.keySet()) {
491
+ Log.info("Starting app `" + app + "'");
492
+
493
+ String docRoot = mApps.get(app);
494
+
495
+ String golfPath = "/" + app;
496
+ String golfRoot = docRoot;
497
+
498
+ Context cx1 = new Context(contexts, golfPath, Context.SESSIONS);
499
+ cx1.setResourceBase(golfRoot);
500
+ cx1.setDisplayName(o.getOpt("displayname"));
501
+ ServletHolder sh1 = new ServletHolder(new GolfServlet());
502
+
503
+ // manually set init parameters
504
+ sh1.setInitParameter("devmode", o.getOpt("devmode"));
505
+ sh1.setInitParameter("poolsize", o.getOpt("pool-size"));
506
+ sh1.setInitParameter("poolexpire", o.getOpt("pool-expire"));
507
+ sh1.setInitParameter("version", mAppVersion);
508
+
509
+ cx1.addServlet(sh1, "/*");
510
+ }
511
+
512
+ for (String app: mBackends.keySet()) {
513
+ Log.info("Starting app `" + app + "'");
514
+
515
+ String docRoot = mBackends.get(app);
516
+
517
+ String golfPath = "/" + app;
518
+ String golfRoot = docRoot;
519
+
520
+ WebAppContext wac = new WebAppContext();
521
+ wac.setContextPath(golfPath);
522
+ wac.setWar(docRoot);
523
+
524
+ handlers.addHandler(wac);
525
+ }
526
+
527
+ handlers.addHandler(contexts);
528
+ handlers.addHandler(new DefaultHandler());
529
+ //handlers.addHandler(new RequestLogHandler());
530
+
531
+ server.setHandler(handlers);
532
+ server.setStopAtShutdown(true);
533
+ server.setSendServerVersion(true);
534
+
535
+ server.start();
536
+ server.join();
537
+ }
538
+
539
+ public void doProxyAnt(String name, String host, int port, String path)
540
+ throws Exception {
541
+ try {
542
+ System.err.print("Building proxy warfile.................");
543
+
544
+ File dep = cacheResourceFile("depends.zip", ".zip", null);
545
+ File cls = cacheResourceFile("classes.zip", ".zip", null);
546
+ File web = getTmpFile(".xml");
547
+ File ant = getTmpFile(".xml", new File("."));
548
+
549
+ String webStr = getResourceAsString("proxy_web.xml");
550
+ String antStr = getResourceAsString("proxy_project.xml");
551
+
552
+ String dname = "HTTP proxy servlet ("+host+":"+port+"/"+path+")";
553
+ String ddesc = "HTTP proxy servlet ("+host+":"+port+"/"+path+")";
554
+
555
+ // set init parameters in the web.xml file
556
+ webStr = webStr.replaceAll("__DISPLAYNAME__", dname)
557
+ .replaceAll("__DESCRIPTION__", ddesc)
558
+ .replaceAll("__PROXY_HOST__", host)
559
+ .replaceAll("__PROXY_PORT__", String.valueOf(port))
560
+ .replaceAll("__PROXY_PATH__", path)
561
+ .replaceAll("__PROXY_QUERY__", o.getOpt("proxyparams"))
562
+ .replaceAll("__MAX_FILE_UPLOAD_SIZE__",
563
+ o.getOpt("proxymaxupload"));
564
+
565
+ // setup the ant build file
566
+ antStr = antStr.replaceAll("__OUTFILE__", name + ".war")
567
+ .replaceAll("__WEB.XML__", web.getAbsolutePath())
568
+ .replaceAll("__DEPENDENCIES.ZIP__", dep.getAbsolutePath())
569
+ .replaceAll("__CLASSES.ZIP__", cls.getAbsolutePath());
570
+
571
+ cacheStringFile(webStr, "", web);
572
+ cacheStringFile(antStr, "", ant);
573
+
574
+ Project project = new Project();
575
+ project.init();
576
+ project.setUserProperty("ant.file" , ant.getAbsolutePath());
577
+ ProjectHelper.configureProject(project, ant);
578
+ project.executeTarget("war");
579
+
580
+ System.err.println("done.");
581
+ } catch (Exception e) {
582
+ System.err.println("fail.");
583
+ throw new Exception(e);
584
+ }
585
+ }
586
+
587
+ public void doStaticAnt() throws Exception {
588
+ try {
589
+ System.err.print("Building static............................");
590
+
591
+ File res = cacheResourceFile("resources.zip", ".zip", null);
592
+ File ant = getTmpFile(".xml", new File("."));
593
+
594
+ String antStr = getResourceAsString("static_project.xml");
595
+
596
+ // setup the ant build file
597
+ antStr = antStr.replaceAll("__OUTFILE__", o.getOpt("static"))
598
+ .replaceAll("__RESOURCES.ZIP__", res.getAbsolutePath())
599
+ .replaceAll("__APPROOT__",
600
+ o.getOpt("approot|proxypath"));
601
+
602
+ cacheStringFile(antStr, "", ant);
603
+
604
+ Project project = new Project();
605
+ project.init();
606
+ project.setUserProperty("ant.file" , ant.getAbsolutePath());
607
+ ProjectHelper.configureProject(project, ant);
608
+ project.executeTarget("static");
609
+
610
+ System.err.println("done.");
611
+ } catch (Exception e) {
612
+ System.err.println("fail.");
613
+ throw new Exception(e);
614
+ }
615
+ }
616
+
617
+ public void doAnt() throws Exception {
618
+ try {
619
+ System.err.print("Building warfile...........................");
620
+
621
+ File dep = cacheResourceFile("depends.zip", ".zip", null);
622
+ File res = cacheResourceFile("resources.zip", ".zip", null);
623
+ File cls = cacheResourceFile("classes.zip", ".zip", null);
624
+ File web = getTmpFile(".xml");
625
+ File ant = getTmpFile(".xml", new File("."));
626
+
627
+ String webStr = getResourceAsString("web.xml");
628
+ String antStr = getResourceAsString("project.xml");
629
+
630
+ // set init parameters in the web.xml file
631
+ webStr = webStr.replaceAll("__DISPLAYNAME__", o.getOpt("displayname"))
632
+ .replaceAll("__DESCRIPTION__", o.getOpt("description"))
633
+ .replaceAll("__POOLSIZE__", o.getOpt("pool-size"))
634
+ .replaceAll("__POOLEXPIRE__", o.getOpt("pool-expire"))
635
+ .replaceAll("__GOLF_VERSION__", mAppVersion)
636
+ .replaceAll("__DEVMODE__", o.getOpt("devmode"));
637
+
638
+ // setup the ant build file
639
+ antStr = antStr.replaceAll("__OUTFILE__", mAppName + ".war")
640
+ .replaceAll("__WEB.XML__", web.getAbsolutePath())
641
+ .replaceAll("__RESOURCES.ZIP__", res.getAbsolutePath())
642
+ .replaceAll("__APPROOT__",
643
+ o.getOpt("approot|proxypath"))
644
+ .replaceAll("__DEPENDENCIES.ZIP__", dep.getAbsolutePath())
645
+ .replaceAll("__CLASSES.ZIP__", cls.getAbsolutePath());
646
+
647
+ cacheStringFile(webStr, "", web);
648
+ cacheStringFile(antStr, "", ant);
649
+
650
+ Project project = new Project();
651
+ project.init();
652
+ project.setUserProperty("ant.file" , ant.getAbsolutePath());
653
+ ProjectHelper.configureProject(project, ant);
654
+ project.executeTarget("war");
655
+
656
+ System.err.println("done.");
657
+ } catch (Exception e) {
658
+ System.err.println("fail.");
659
+ throw new Exception(e);
660
+ }
661
+ }
662
+
663
+ public static File getTmpFile(String ext) throws IOException {
664
+ File f = File.createTempFile("golf_deploy.", ext);
665
+ f.deleteOnExit();
666
+ return f;
667
+ }
668
+
669
+ public static File getTmpFile(String ext, File dir) throws IOException {
670
+ File f = File.createTempFile("golf_deploy.", ext, dir);
671
+ f.deleteOnExit();
672
+ return f;
673
+ }
674
+
675
+ public String getResourceAsString(String name) throws IOException {
676
+ JavaResource res = new JavaResource(name, null);
677
+ BufferedReader in =
678
+ new BufferedReader(new InputStreamReader(res.getInputStream()));
679
+ StringBuilder s = new StringBuilder();
680
+
681
+ String tmp;
682
+ while((tmp = in.readLine()) != null)
683
+ s.append(tmp).append("\n");
684
+
685
+ return s.toString();
686
+ }
687
+
688
+ public File cacheResourceFile(String name, String ext, File f)
689
+ throws IOException {
690
+ name = name.replaceFirst("^/+", "");
691
+ byte[] b = new byte[BUF_SIZE];
692
+
693
+ if (f == null)
694
+ f = getTmpFile(ext);
695
+
696
+ BufferedInputStream in = new BufferedInputStream(
697
+ getClass().getClassLoader().getResourceAsStream(name));
698
+ BufferedOutputStream out =
699
+ new BufferedOutputStream(new FileOutputStream(f));
700
+
701
+ int nread;
702
+ while ((nread = in.read(b)) != -1)
703
+ out.write(b, 0, nread);
704
+ out.close();
705
+
706
+ return f;
707
+ }
708
+
709
+ public static File cacheStringFile(String text, String ext, File f)
710
+ throws IOException {
711
+ if (f == null)
712
+ f = getTmpFile(ext);
713
+
714
+ PrintWriter out = new PrintWriter(new FileOutputStream(f));
715
+ out.print(text);
716
+ out.close();
717
+
718
+ return f;
719
+ }
720
+
721
+ private void cacheStringAws(String str, String key) throws Exception {
722
+ cacheStringAws(str, key, null);
723
+ }
724
+
725
+ private void cacheStringAws(String str, String key, String type)
726
+ throws Exception {
727
+ key = key.replaceFirst("^/+", "");
728
+ File f = cacheStringFile(str, key.replaceFirst("^.*/", ""), null);
729
+ cacheFileAws(f, key, type);
730
+ }
731
+
732
+ private void cacheFileAws(File file, String key) throws Exception {
733
+ cacheFileAws(file, key, null);
734
+ }
735
+
736
+ private void cacheFileAws(File file, String key, String type)
737
+ throws Exception {
738
+ key = key.replaceFirst("^/+", "");
739
+
740
+ if (key.endsWith(".js") || key.endsWith(".css") || key.endsWith(".html")) {
741
+ BufferedReader inBr = new BufferedReader(new FileReader(file));
742
+ StringBuilder sb = new StringBuilder();
743
+
744
+ String s;
745
+ while ((s = inBr.readLine()) != null)
746
+ sb.append(s).append("\n");
747
+
748
+ String src = sb.toString();
749
+
750
+ if (key.endsWith(".js"))
751
+ src = injectCloudfrontUrl(compressJs(src, key));
752
+ else if (key.endsWith(".css"))
753
+ src = injectCloudfrontUrl(compressCss(src, key));
754
+ else if (key.endsWith(".html"))
755
+ src = injectCloudfrontUrl(src);
756
+
757
+ file.delete();
758
+
759
+ PrintWriter out = new PrintWriter(new FileWriter(file));
760
+ out.print(src);
761
+ out.close();
762
+ }
763
+
764
+ S3Object obj = new S3Object(mBucket, file);
765
+ obj.setKey(key);
766
+ if (type == null)
767
+ obj.setContentType(GolfResource.MimeMapping.lookup(key));
768
+ else
769
+ obj.setContentType(type);
770
+ obj.setAcl(mAcl);
771
+ mS3svc.putObject(mBucket, obj);
772
+ }
773
+
774
+ private void cacheJarResourcesAws() throws Exception {
775
+ String[] resources = {
776
+ JSDETECT_HTML,
777
+ JQUERY_JS,
778
+ JQUERY_HIST_JS,
779
+ JQUERY_GOLF_JS,
780
+ LOADING_GIF
781
+ };
782
+ for (String res : resources)
783
+ cacheJarResourceAws("/"+res);
784
+ }
785
+
786
+ private void cacheJarResourceAws(String name) throws Exception {
787
+ File f = cacheResourceFile(name, name.replaceAll("^.*/", ""), null);
788
+ cacheFileAws(f, name.replaceFirst("^/+", ""));
789
+ }
790
+
791
+ public static void cacheNewDotHtmlFile() throws Exception {
792
+ String newHtmlStr = getNewDotHtmlString(false);
793
+ File f = new File(o.getOpt("approot|proxypath"), NEW_HTML);
794
+ if (f.exists())
795
+ f.delete();
796
+ f.deleteOnExit();
797
+ PrintWriter out = new PrintWriter(new FileWriter(f));
798
+ out.print(newHtmlStr);
799
+ out.close();
800
+
801
+ newHtmlStr = getNewDotHtmlString(true);
802
+ f = new File(o.getOpt("approot|proxypath"), NEW_FC_HTML);
803
+ if (f.exists())
804
+ f.delete();
805
+ f.deleteOnExit();
806
+ out = new PrintWriter(new FileWriter(f));
807
+ out.print(newHtmlStr);
808
+ out.close();
809
+ }
810
+
811
+ public static String getNewDotHtmlString(boolean fc) throws Exception {
812
+ File cwd = new File(o.getOpt("approot|proxypath"));
813
+ boolean st = o.getOpt("static") != null;
814
+
815
+ String newHtmlSrc = st ? NEW_ST_HTML : NEW_HTML;
816
+
817
+ GolfResource newHtml = new GolfResource(cwd, newHtmlSrc);
818
+ GolfResource headHtml = new GolfResource(cwd, HEAD_HTML);
819
+ GolfResource noscriptHtml =
820
+ new GolfResource(cwd,
821
+ (fc ? NOSCRIPT_FC_HTML : (st ? NOSCRIPT_ST_HTML : NOSCRIPT_HTML)));
822
+
823
+ if (mNewHtml == null)
824
+ mNewHtml = newHtml.toString();
825
+
826
+ String newStr = mNewHtml;
827
+ String headStr = headHtml.toString();
828
+ String noscriptStr = noscriptHtml.toString();
829
+
830
+ JSONArray backends = new JSONArray(mBackends.keySet());
831
+
832
+ String result = newStr;
833
+ result = result.replaceFirst("\n *__HEAD_HTML__ *\n *",
834
+ " custom head section -->\n"+headStr+
835
+ " <!-- end custom head section ");
836
+ result = result.replaceFirst("\n *__NOSCRIPT_HTML__ *\n *",
837
+ " custom noscript section -->\n"+noscriptStr+
838
+ " <!-- end custom noscript section ");
839
+ result = result.replaceFirst("__DEVMODE__",
840
+ Boolean.toString(o.getFlag("devmode")));
841
+ result = result.replaceFirst("__RESTBACKENDS__", backends.toString());
842
+ result = result.replaceFirst("__CLOUDFRONTDOMAIN__", o.getOpt("cfdomains"));
843
+
844
+ if (st)
845
+ result = result.replaceAll("\\?path=/*", "");
846
+
847
+ return result;
848
+ }
849
+
850
+ public static void cacheComponentsFile() throws Exception {
851
+ File f = new File(o.getOpt("approot|proxypath"), COMPONENTS_JS);
852
+ if (f.exists())
853
+ f.delete();
854
+ f.deleteOnExit();
855
+ PrintWriter out = new PrintWriter(new FileWriter(f));
856
+ out.print(getComponentsString());
857
+ out.close();
858
+ }
859
+
860
+ private void cacheComponentsAws() throws Exception {
861
+ cacheStringAws(getComponentsString(), COMPONENTS_JS, "text/javascript");
862
+ }
863
+
864
+ private static String getComponentsString() throws Exception {
865
+ String ret =
866
+ "jQuery.golf.components=" + getComponentsJSON(null, null) + ";" +
867
+ "jQuery.golf.res=" + getResourcesJSON(null, null) + ";" +
868
+ "jQuery.golf.plugins=" + getScriptsJSON(DIR_MODULES, null) + ";" +
869
+ "jQuery.golf.scripts=" + getScriptsJSON(DIR_SCRIPTS, null) + ";" +
870
+ "jQuery.golf.styles=" + getStylesJSON(DIR_STYLES, null) + ";" +
871
+ "jQuery.golf.setupComponents();";
872
+ if (o.getOpt("static") != null)
873
+ ret = ret.replaceAll("\\?path=/*", "");
874
+ return ret;
875
+ }
876
+
877
+ private static String getResourcesJSON(String path, JSONObject json)
878
+ throws Exception {
879
+ boolean isNew = false;
880
+
881
+ if (path == null) path = "";
882
+ if (json == null) {
883
+ json = new JSONObject();
884
+ isNew = true;
885
+ }
886
+
887
+ File file = new File(new File(o.getOpt("approot|proxypath")), path);
888
+
889
+ if (!file.getName().startsWith(".") || file.getName().equals(".")
890
+ || file.getName().equals("..")) {
891
+ if (file.isFile()) {
892
+ String keyName = path.replaceFirst("^/+", "");
893
+ json.put(keyName.replaceFirst("^.*/", ""), "?path=" + keyName);
894
+ } else if (file.isDirectory()) {
895
+ JSONObject dir;
896
+ if (!isNew) {
897
+ dir = new JSONObject();
898
+ json.put(file.getName(), dir);
899
+ } else {
900
+ dir = json;
901
+ }
902
+ for (String f : file.list()) {
903
+ String ppath = path + "/" + f;
904
+ getResourcesJSON(path+"/"+f, dir);
905
+ }
906
+ }
907
+ }
908
+
909
+ // these are system files that are of no use to a golf app
910
+ for (String s : new String[] {
911
+ DIR_COMPONENTS,
912
+ DIR_SCRIPTS,
913
+ DIR_STYLES,
914
+ ERROR_HTML,
915
+ HEAD_HTML,
916
+ NEW_HTML,
917
+ NEW_FC_HTML,
918
+ NOSCRIPT_FC_HTML,
919
+ NOSCRIPT_HTML,
920
+ FORCEPROXY_TXT,
921
+ FORCECLIENT_TXT,
922
+ FORCEBOT_TXT,
923
+ COMPONENTS_JS,
924
+ CONTROLLER_JS
925
+ }) json.remove(s);
926
+
927
+ return json.toString();
928
+ }
929
+
930
+ private static String getComponentsJSON(String path, JSONObject json)
931
+ throws Exception {
932
+ if (path == null) path = "";
933
+ if (json == null) json = new JSONObject();
934
+
935
+ File file =
936
+ new File(new File(o.getOpt("approot|proxypath"), DIR_COMPONENTS), path);
937
+
938
+ if (!file.getName().startsWith(".")) {
939
+ if (file.isFile()) {
940
+ if (path.endsWith(".html")) {
941
+ String cmpName = path.replaceFirst("\\.html$", "");
942
+ JSONObject cmpJson = processComponent(cmpName);
943
+ json.put((String) cmpJson.get("name"), cmpJson);
944
+ }
945
+ } else if (file.isDirectory() && !file.getName().endsWith(".res")) {
946
+ for (String f : file.list()) {
947
+ String ppath = path + "/" + f;
948
+ getComponentsJSON(path+"/"+f, json);
949
+ }
950
+ }
951
+ }
952
+
953
+ return json.toString();
954
+ }
955
+
956
+ private static String getScriptsJSON(String path, JSONObject json)
957
+ throws Exception {
958
+ if (path == null) path = "";
959
+ if (json == null) json = new JSONObject();
960
+
961
+ File file = new File(o.getOpt("approot|proxypath"), path);
962
+
963
+ if (!file.getName().startsWith(".")) {
964
+ if (file.isFile()) {
965
+ if (path.endsWith(".js")) {
966
+ String cmpName = path.replaceFirst("\\.js$", "");
967
+ String keyName =
968
+ cmpName.replaceFirst("^[a-z]+/+", "");
969
+ json.put(keyName, processScript(cmpName).put("name", keyName));
970
+ }
971
+ } else if (file.isDirectory()) {
972
+ for (String f : file.list()) {
973
+ String ppath = path + "/" + f;
974
+ getScriptsJSON(path+"/"+f, json);
975
+ }
976
+ }
977
+ }
978
+
979
+ return json.toString();
980
+ }
981
+
982
+ private static String getStylesJSON(String path, JSONObject json)
983
+ throws Exception {
984
+ if (path == null) path = "";
985
+ if (json == null) json = new JSONObject();
986
+
987
+ File file = new File(o.getOpt("approot|proxypath"), path);
988
+
989
+ if (!file.getName().startsWith(".")) {
990
+ if (file.isFile()) {
991
+ if (path.endsWith(".css")) {
992
+ String cmpName = path.replaceFirst("\\.css$", "");
993
+ String keyName =
994
+ cmpName.replaceFirst("^[a-z]+/+", "").replace("/", ".");
995
+ json.put(keyName, processStyle(cmpName).put("name", keyName));
996
+ }
997
+ } else if (file.isDirectory()) {
998
+ for (String f : file.list()) {
999
+ String ppath = path + "/" + f;
1000
+ getStylesJSON(path+"/"+f, json);
1001
+ }
1002
+ }
1003
+ }
1004
+
1005
+ return json.toString();
1006
+ }
1007
+
1008
+ public static JSONObject processComponent(String name) throws Exception {
1009
+ name = name.replaceFirst("^/+", "");
1010
+ String shortName = name.replaceFirst("(^|/)[^/]+/([^/]+)$", "$1$2");
1011
+ String className = shortName.replace('/', '-');
1012
+ String classPath = shortName.replace('/', '.');
1013
+ File cwd = new File(o.getOpt("approot|proxypath"), DIR_COMPONENTS);
1014
+
1015
+ String html = name + ".html";
1016
+ String res = name + ".res";
1017
+
1018
+ GolfResource htmlRes = new GolfResource(cwd, html);
1019
+ File resDir = new File(cwd, res);
1020
+
1021
+ JSONObject resObj =
1022
+ processComponentRes(resDir, cwd, resDir, null);
1023
+
1024
+ String resUriPath = "?path=components/"+getRelativePath(resDir, cwd);
1025
+
1026
+ String htmlStr = htmlRes.toString().replaceAll("\\?resource=", resUriPath);
1027
+
1028
+ String pat1 = "</script>";
1029
+ String pat2 = "<script +([^>]* +)*type *= *\"text/golf\"( [^>]*)*>";
1030
+
1031
+ String cmpJs1 = Pattern
1032
+ .compile("^(.*"+pat2+").*$", Pattern.DOTALL)
1033
+ .matcher(htmlStr)
1034
+ .replaceFirst("$1");
1035
+
1036
+ String cmpJs2 = Pattern
1037
+ .compile(pat1+".*$", Pattern.DOTALL)
1038
+ .matcher(
1039
+ Pattern
1040
+ .compile("^.*"+pat2, Pattern.DOTALL)
1041
+ .matcher(htmlStr)
1042
+ .replaceFirst(""))
1043
+ .replaceFirst("");
1044
+
1045
+ String cmpJs3 = Pattern
1046
+ .compile("^.*("+pat1+".*)$", Pattern.DOTALL)
1047
+ .matcher(
1048
+ Pattern
1049
+ .compile("^.*"+pat2, Pattern.DOTALL)
1050
+ .matcher(htmlStr)
1051
+ .replaceFirst(""))
1052
+ .replaceFirst("$1");
1053
+
1054
+ File jsFile = cacheStringFile(cmpJs2, "js", null);
1055
+ cmpJs2 = processComponentJs(cmpJs2, jsFile.getCanonicalPath());
1056
+
1057
+ htmlStr = cmpJs1 + "\n" + cmpJs2 + "\n" + cmpJs3;
1058
+
1059
+ JSONObject json = new JSONObject()
1060
+ .put("name", classPath)
1061
+ .put("html", htmlStr)
1062
+ .put("res", resObj);
1063
+
1064
+ return json;
1065
+ }
1066
+
1067
+ public static JSONObject processScript(String name) throws Exception {
1068
+ String dir = name.replaceAll("/.*$", "");
1069
+ name = name.replaceFirst("^[a-z]+/+", "");
1070
+
1071
+ File cwd = new File(o.getOpt("approot|proxypath"), dir);
1072
+ String js = name + ".js";
1073
+ GolfResource jsRes = new GolfResource(cwd, js);
1074
+ String jsStr = processComponentJs(jsRes.toString(), (new File(cwd, js)).getCanonicalPath());
1075
+ JSONObject json = new JSONObject().put("js", jsStr);
1076
+
1077
+ return json;
1078
+ }
1079
+
1080
+ public static JSONObject processStyle(String name) throws Exception {
1081
+ String dir = name.replaceAll("/.*$", "");
1082
+ name = name.replaceFirst("^[a-z]+/+", "");
1083
+
1084
+ File cwd = new File(o.getOpt("approot|proxypath"), dir);
1085
+ // FIXME: only in non-devmode should css be messed with like this
1086
+ //String css = (new GolfResource(cwd, name+".css")).toString()
1087
+ // .replaceAll(" *\n *", " ");
1088
+ String css = (new GolfResource(cwd, name+".css")).toString();
1089
+ JSONObject json = new JSONObject().put("css", css);
1090
+
1091
+ return json;
1092
+ }
1093
+
1094
+ public static String injectCloudfrontUrl(String text) {
1095
+ String result = text;
1096
+ if (mCfDomains.size() > 0)
1097
+ while (! result.equals(result = result.replaceFirst("\\?path=/*", mCfDomains.next())));
1098
+ return result;
1099
+ }
1100
+
1101
+ public static JSONObject processComponentRes(File f, File uriBase,
1102
+ File refBase, JSONObject res) throws URISyntaxException, JSONException {
1103
+ boolean isNew = false;
1104
+
1105
+ if (!f.exists() || f.getName().startsWith("."))
1106
+ return null;
1107
+
1108
+ if (res == null) {
1109
+ res = new JSONObject();
1110
+ isNew = true;
1111
+ }
1112
+
1113
+ String ref = getRelativePath(f, refBase);
1114
+
1115
+ if (f.isDirectory()) {
1116
+ JSONObject dir;
1117
+ if (!isNew) {
1118
+ dir = new JSONObject();
1119
+ res.put(ref.replaceFirst("/+$", ""), dir);
1120
+ } else {
1121
+ dir = res;
1122
+ }
1123
+ for (String s : f.list())
1124
+ processComponentRes(new File(f, s), uriBase, refBase, dir);
1125
+ } else {
1126
+ String rel = getRelativePath(f, uriBase);
1127
+ res.put(ref.replaceFirst("^.*/", ""), "?path=components/"+rel);
1128
+ }
1129
+ return res;
1130
+ }
1131
+
1132
+ public static String processComponentCss(String text, String className,
1133
+ String fileName) {
1134
+ String result = text;
1135
+
1136
+ // remove newlines
1137
+ result = result.replaceAll("[\\r\\n\\s]+", " ");
1138
+ // remove comments
1139
+ result = result.replaceAll("/\\*.*\\*/", "");
1140
+ result = result.trim();
1141
+
1142
+ if (!o.getFlag("devmode"))
1143
+ result = compressCss(result, fileName);
1144
+
1145
+ return result;
1146
+ }
1147
+
1148
+ public static String compressCss(String in, String filename) {
1149
+ if (!o.getFlag("compress-css"))
1150
+ return in;
1151
+
1152
+ String result = in;
1153
+ try {
1154
+ CssCompressor css = new CssCompressor(new StringReader(in));
1155
+
1156
+ StringWriter out = new StringWriter();
1157
+ css.compress(out, -1);
1158
+ result = out.toString();
1159
+ } catch (Exception e) {
1160
+ System.err.println("golf: "+filename+" not compressed: "+e.toString());
1161
+ }
1162
+ return result;
1163
+ }
1164
+
1165
+ public static String processComponentJs(String text, String filename)
1166
+ throws Exception {
1167
+ String result = text;
1168
+
1169
+ if (text.startsWith("#!")) {
1170
+ result = "";
1171
+
1172
+ ArrayList<String> shbang = parseAsShell(
1173
+ text.substring(0, text.indexOf("\n"))
1174
+ .replaceFirst("^#![\\s]*", "")
1175
+ .replaceFirst("[\\s]*$", " ")
1176
+ );
1177
+
1178
+ shbang.add(filename);
1179
+
1180
+ Process p =
1181
+ Runtime.getRuntime().exec(shbang.toArray(new String[shbang.size()]));
1182
+
1183
+ int exitVal = p.waitFor();
1184
+
1185
+ BufferedReader br = new BufferedReader(new InputStreamReader(
1186
+ (exitVal == 0 ? p.getInputStream() : p.getErrorStream())));
1187
+
1188
+ while ((text = br.readLine()) != null)
1189
+ result += text + "\n";
1190
+
1191
+ if (exitVal != 0)
1192
+ throw new Exception(result);
1193
+ }
1194
+
1195
+ if (!o.getFlag("devmode"))
1196
+ result = compressJs(result, filename);
1197
+
1198
+ return result;
1199
+ }
1200
+
1201
+ private static String compressJs(String in, String filename) {
1202
+ if (!o.getFlag("compress-js"))
1203
+ return in;
1204
+
1205
+ String result = in;
1206
+
1207
+ try {
1208
+ final String cn = filename;
1209
+
1210
+ ErrorReporter errz = new ErrorReporter() {
1211
+ public void warning(String message, String sourceName,
1212
+ int line, String lineSource, int lineOffset) {
1213
+ if (line < 0) {
1214
+ System.err.println(cn+": warning:\n"+message+"\n");
1215
+ } else {
1216
+ System.err.println(cn+": warning:\n"+line+':'+lineOffset+
1217
+ ':'+message+"\n");
1218
+ }
1219
+ }
1220
+ public void error(String message, String sourceName,
1221
+ int line, String lineSource, int lineOffset) {
1222
+ if (line < 0) {
1223
+ System.err.println(cn+": error:\n"+message+"\n");
1224
+ } else {
1225
+ System.err.println(cn+": error:\n"+line+':'+lineOffset+
1226
+ ':'+message+"\n");
1227
+ }
1228
+ }
1229
+ public EvaluatorException runtimeError(String message,
1230
+ String sourceName, int line, String lineSource, int lineOffset) {
1231
+ error(message, sourceName, line, lineSource, lineOffset);
1232
+ return new EvaluatorException(message);
1233
+ }
1234
+ };
1235
+
1236
+ JavaScriptCompressor js =
1237
+ new JavaScriptCompressor(new StringReader(in), errz);
1238
+
1239
+ StringWriter out = new StringWriter();
1240
+ js.compress(out, -1, false, false, false, true);
1241
+ result = out.toString().replace("\n", "\\n");
1242
+ } catch (Exception e) {
1243
+ System.err.println("golf: "+filename+" not compressed: "+e.toString());
1244
+ }
1245
+ return result;
1246
+ }
1247
+
1248
+ private void cacheResourcesAws(File file, String path) throws Exception {
1249
+ if (path.startsWith("/.") ||
1250
+ path.equals("/"+HEAD_HTML) ||
1251
+ path.equals("/"+NOSCRIPT_HTML))
1252
+ return;
1253
+
1254
+ if (file.isFile()) {
1255
+ cacheFileAws(file, path);
1256
+ } else if (file.isDirectory()) {
1257
+ for (String f : file.list()) {
1258
+ String ppath = path + (path.endsWith("/") ? f : "/" + f);
1259
+ cacheResourcesAws(new File(file, f), ppath);
1260
+ }
1261
+ }
1262
+ }
1263
+
1264
+ private static String getRelativePath(File f, File base)
1265
+ throws URISyntaxException {
1266
+ URI u1 = new URI(base.toURI().toString());
1267
+ URI u2 = new URI(f.toURI().toString());
1268
+ return u1.relativize(u2).toString();
1269
+ }
1270
+
1271
+ public static ArrayList<String> parseAsShell(String line) throws Exception {
1272
+ ArrayList<String> ret = new ArrayList<String>();
1273
+
1274
+ StreamTokenizer st = new StreamTokenizer(new StringReader(line));
1275
+ st.resetSyntax();
1276
+ st.wordChars(33, 126);
1277
+ st.whitespaceChars(' ', ' ');
1278
+ st.quoteChar('"');
1279
+ st.quoteChar('\'');
1280
+
1281
+ while (st.nextToken() != StreamTokenizer.TT_EOF)
1282
+ ret.add(st.sval);
1283
+
1284
+ return ret;
1285
+ }
1286
+
1287
+ private String randName(String base) {
1288
+ String result =
1289
+ base + "-" + (new UID()).toString().replaceFirst("^.*:([^:]+):.*$", "$1");
1290
+ return result;
1291
+ }
1292
+
1293
+ private void usage(String error) {
1294
+ if (error != null)
1295
+ System.err.println("golf: "+error);
1296
+
1297
+ o.printUsage();
1298
+ }
1299
+ }