@Activate protected void activate(ComponentContext context) { this.propOpt = PropertiesUtil.toString( context.getProperties().get(PROPERTY_OPTIMIZATION), DEFAULT_OPTIMIZATION); this.optimization = ClosureOptimizationLevel.fromString(this.propOpt); }
private String compile(HtmlLibrary library, ClosureOptimizationLevel opt, InputStream js) throws IOException { CompilationLevel compilationLevel = opt.toCompilationLevel(); if (null == compilationLevel) { // return original input return IOUtils.toString(js); } SourceFile input = SourceFile.fromInputStream(getLibraryName(library), js); // TODO externs not supported, should avoid ADVANCED compilation SourceFile extern = SourceFile.fromCode("TODO", StringUtils.EMPTY); CompilerOptions options = new CompilerOptions(); compilationLevel.setOptionsForCompilationLevel(options); // ES5 assumption to allow getters/setters options.setLanguageIn(CompilerOptions.LanguageMode.ECMASCRIPT5); Compiler compiler = new Compiler(); compiler.compile(extern, input, options); return compiler.toSource(); }
@Component( label = "aemin Library Manager", description = "HTML Library Manager Delegate Service", name = "com.steeleforge.aem.aemin.HtmlLibraryManagerDelegate", immediate = true, metatype = true) @Service({com.steeleforge.aem.aemin.HtmlLibraryManagerDelegate.class}) public class HtmlLibraryManagerDelegateImpl implements HtmlLibraryManagerDelegate { private static final Logger log = LoggerFactory.getLogger(HtmlLibraryManagerDelegateImpl.class); private static final String CACHE_PATH = "/var/clientlibs"; private static final String DEFAULT_THEME = "default"; private static final String FMT_SCRIPT = "<script type=\"text/javascript\" src=\"{0}\"></script>"; // properties & defaults static final String DEFAULT_OPTIMIZATION = ClosureOptimizationLevel.NONE .name(); // note Enum#value#ordinal() does not appear to be valid here @Property( label = "Optimization Level", description = "None, Whitespace-only, Simple, Advanced: https://developers.google.com/closure/compiler/docs/compilation_optimizations", value = "NONE", options = { @PropertyOption(name = "NONE", value = "No Optimization"), @PropertyOption(name = "WHITESPACE", value = "Whitespace only"), @PropertyOption(name = "SIMPLE", value = "Simple"), @PropertyOption(name = "ADVANCED", value = "Advanced") }) static final String PROPERTY_OPTIMIZATION = "aemin.optimization.level"; private ClosureOptimizationLevel optimization = ClosureOptimizationLevel.NONE; protected String propOpt = optimization.name().toLowerCase(); @Reference(policy = ReferencePolicy.STATIC, cardinality = ReferenceCardinality.MANDATORY_UNARY) HtmlLibraryManager libraryManager; @Reference(policy = ReferencePolicy.STATIC, cardinality = ReferenceCardinality.MANDATORY_UNARY) ResourceResolverFactory resourceResolverFactory; ResourceResolver resolver = null; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private String compile(HtmlLibrary library, ClosureOptimizationLevel opt, InputStream js) throws IOException { CompilationLevel compilationLevel = opt.toCompilationLevel(); if (null == compilationLevel) { // return original input return IOUtils.toString(js); } SourceFile input = SourceFile.fromInputStream(getLibraryName(library), js); // TODO externs not supported, should avoid ADVANCED compilation SourceFile extern = SourceFile.fromCode("TODO", StringUtils.EMPTY); CompilerOptions options = new CompilerOptions(); compilationLevel.setOptionsForCompilationLevel(options); // ES5 assumption to allow getters/setters options.setLanguageIn(CompilerOptions.LanguageMode.ECMASCRIPT5); Compiler compiler = new Compiler(); compiler.compile(extern, input, options); return compiler.toSource(); } public void send( SlingHttpServletRequest request, SlingHttpServletResponse response, HtmlLibrary library) { InputStream libraryInputStream = null; // NOTE: HtmlLibraryManager#getLibrary should have prepared ClientLibraryImpl // and related binary stream should be ready try { Node node = JcrUtils.getNodeIfExists(getLibraryNode(request, library), JcrConstants.JCR_CONTENT); response.setDateHeader( "Last-Modified", JcrUtils.getLongProperty(node, JcrConstants.JCR_LASTMODIFIED, 0L)); response.setContentType(library.getType().contentType); response.setCharacterEncoding("utf-8"); libraryInputStream = JcrUtils.readFile(node); } catch (RepositoryException re) { log.debug("JCR issue retrieving library node at {}: ", library.getPath(), re.getMessage()); } try { if (libraryManager.isGzipEnabled()) { response.setHeader("Content-Encoding", "gzip"); GZIPOutputStream gzipOut = new GZIPOutputStream(response.getOutputStream()); IOUtils.copy(libraryInputStream, gzipOut); gzipOut.finish(); } else { IOUtils.copy(libraryInputStream, response.getOutputStream()); } } catch (IOException ioe) { log.debug("gzip IO issue for library {}: ", library.getPath(), ioe.getMessage()); } finally { IOUtils.closeQuietly(libraryInputStream); } } public Node getLibraryNode(SlingHttpServletRequest request, HtmlLibrary library) { Node node = null; try { // we want the non-minified version as the root path String cacheRoot = Text.getRelativeParent( (new StringBuilder(CACHE_PATH).append(library.getPath(false))).toString(), 1); String optPath = (new StringBuilder(cacheRoot).append("/").append(getLibraryName(library))).toString(); node = JcrUtils.getNodeIfExists(optPath, getAdminSession()); if (null == node) { // generate empty jcr:data to cache node = createEmptyCache(library, cacheRoot, getAdminSession()); } // lib was modified after last cache write if (!node.hasNode(JcrConstants.JCR_CONTENT) || library.getLastModified(false) > JcrUtils.getLongProperty( node.getNode(JcrConstants.JCR_CONTENT), JcrConstants.JCR_LASTMODIFIED, 0L)) { // generate new binary, if possible node = populateCache(library, node.getPath(), getAdminSession()); } // reassign with user session node = request.getResourceResolver().resolve(node.getPath()).adaptTo(Node.class); } catch (RepositoryException re) { log.debug(re.getMessage()); } finally { getResolver().close(); } return node; } private Node populateCache(HtmlLibrary library, String root, Session session) { Node cacheNode = null; try { String libPath = (new StringBuilder(CACHE_PATH).append(library.getPath(false))).toString(); Node src = JcrUtils.getNodeIfExists(libPath, session); cacheNode = session.getNode(root); if (null != src) { // this.lock.readLock().lock(); // produced closure compiled src String compiled = compile(library, this.optimization, JcrUtils.readFile(src)); // this.lock.readLock().unlock(); // this.lock.writeLock().lock(); // JcrUtils.putFile( cacheNode.getParent(), getLibraryName(library), library.getType().contentType, IOUtils.toInputStream(compiled, "UTF-8")); session.save(); // this.lock.writeLock().unlock(); } } catch (RepositoryException re) { log.debug(re.getMessage()); } catch (IOException ioe) { log.debug(ioe.getMessage()); } return cacheNode; } private Node createEmptyCache(HtmlLibrary library, String root, Session session) { Node node = null; // this.lock.writeLock().lock(); try { Node swap = JcrUtils.getOrCreateByPath( root, JcrResourceConstants.NT_SLING_FOLDER, JcrResourceConstants.NT_SLING_FOLDER, session, true); node = swap.addNode(getLibraryName(library), JcrConstants.NT_FILE); swap = node.addNode(JcrConstants.JCR_CONTENT, JcrConstants.NT_RESOURCE); swap.setProperty(JcrConstants.JCR_LASTMODIFIED, 0L); swap.setProperty(JcrConstants.JCR_MIMETYPE, library.getType().contentType); swap.setProperty( JcrConstants.JCR_DATA, session.getValueFactory().createBinary(new ByteArrayInputStream(new byte[0]))); session.save(); // this.lock.writeLock().unlock(); } catch (RepositoryException re) { log.debug(re.getMessage()); } return node; } private String getLibraryName(HtmlLibrary library) { return StringUtils.replace( library.getName(true), "min", StringUtils.lowerCase(this.optimization.name())); } private ResourceResolver getResolver() { if (null == resolver || !resolver.isLive()) { try { resolver = resourceResolverFactory.getAdministrativeResourceResolver(null); } catch (LoginException le) { log.debug(le.getMessage()); } } return resolver; } private Session getAdminSession() { return getResolver().adaptTo(Session.class); } @Activate protected void activate(ComponentContext context) { this.propOpt = PropertiesUtil.toString( context.getProperties().get(PROPERTY_OPTIMIZATION), DEFAULT_OPTIMIZATION); this.optimization = ClosureOptimizationLevel.fromString(this.propOpt); } }