CompilerOptions options = defaultCompilerOptions();
return options;
}
public JsResponse compile(JsUri jsUri, Iterable<JsContent> content, String externs) {
JsResponseBuilder builder = new JsResponseBuilder();
CompilerOptions options = getCompilerOptions(jsUri);
StringBuilder compiled = new StringBuilder();
StringBuilder exports = new StringBuilder();
boolean useExterns = compileLevel.equals("advanced");
if (!useExterns) {
/*
* Kicking the can down the road. Advanced optimizations doesn't currently work with the closure compiler in shindig.
* When it's fixed, we need to make sure all externs are included (not just externs for what was requested) otherwise
* the cache key will fluctuate with the url hit, and we will get massive cache churn and possible DDOS scenarios
* when we recompile all requested modules on the fly because the cache key was different.
*/
externs = "";
}
// Add externs export to the list if set in options.
if (options.isExternExportsEnabled()) {
List<JsContent> allContent = Lists.newLinkedList(content);
allContent.add(EXPORTSYMBOL_CODE);
content = allContent;
}
try {
List<Future<CompileResult>> futures = Lists.newLinkedList();
// Process each content for work
for (JsContent code : content) {
JsResponse defaultCompiled = defaultCompiler.compile(jsUri, Lists.newArrayList(code), externs);
Future<CompileResult> future = null;
boolean compile = !code.isNoCompile() && !compileLevel.equals("none");
/*
* isDebug usually will turn off all compilation, however, setting
* isExternExportsEnabled and specifying an export path will keep the
* closure compiler on and export the externs for debugging.
*/
compile = compile && (!jsUri.isDebug() || options.isExternExportsEnabled());
if (compile) { // We should compile this code segment.
String cacheKey = makeCacheKey(defaultCompiled.toJsString(), externs, jsUri, options);
synchronized (compiling) {
CompileResult cached = cache.getElement(cacheKey);
if (cached == null) {
future = compiling.get(cacheKey);
if (future == null) {
// Don't pound on the compiler. Let the first thread queue the work,
// the rest of them will just wait on the futures later.
future = getCompileFuture(cacheKey, code, jsUri, externs);
compiling.put(cacheKey, future);
}
} else {
future = ImmediateFuture.newInstance(cached);
}
}
}
if (future == null) {
future = ImmediateFuture.newInstance(new CompileResult(code.get()));
}
futures.add(future);
}
// Wait on all work to be done.
for (Future<CompileResult> future : futures) {
CompileResult result = future.get();
compiled.append(result.getContent());
if (useExterns) {
String export = result.getExternExport();
if (export != null) {
exports.append(export);
}
}
}
} catch (Exception e) {
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, e.getMessage(), e);
}
Throwable cause = e.getCause();
if (cause instanceof CompilerException) {
return returnErrorResult(builder, HttpResponse.SC_NOT_FOUND, ((CompilerException)cause).getErrors());
} else {
return returnErrorResult(builder, HttpResponse.SC_NOT_FOUND, Lists.newArrayList(e.getMessage()));
}
}
builder.appendJs(compiled.toString(), "[compiled]");
builder.clearExterns().appendRawExtern(exports.toString());
return builder.build();
}