/* * Copyright 2013 eXo Platform SAS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package juzu.impl.plugin.asset; import juzu.asset.AssetLocation; import juzu.impl.common.Name; import juzu.impl.common.Tools; import juzu.impl.compiler.CompilationException; import juzu.impl.compiler.ElementHandle; import juzu.impl.compiler.MessageCode; import juzu.impl.plugin.application.metamodel.ApplicationMetaModel; import juzu.impl.plugin.application.metamodel.ApplicationMetaModelPlugin; import juzu.impl.metamodel.AnnotationKey; import juzu.impl.metamodel.AnnotationState; import juzu.impl.common.JSON; import juzu.impl.compiler.ProcessingContext; import juzu.plugin.asset.Scripts; import juzu.plugin.asset.Stylesheets; import javax.tools.FileObject; import javax.tools.StandardLocation; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.lang.annotation.Annotation; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */ public class AssetMetaModelPlugin extends ApplicationMetaModelPlugin { /** . */ public static final MessageCode ASSET_NOT_FOUND = new MessageCode("ASSET_NOT_FOUND", "The asset %1$s cannot be resolved"); /** . */ public static final MessageCode DUPLICATE_ASSET_ID = new MessageCode("DUPLICATE_ASSET_ID", "The asset id %1$s must be used once"); /** . */ public static final MessageCode CANNOT_PROCESS_ASSET = new MessageCode("CANNOT_PROCESS_ASSET", "The asset id %1$s cannot be processed: %2d"); /** . */ private static final Set<Class<? extends java.lang.annotation.Annotation>> ANNOTATIONS; static { HashSet<Class<? extends Annotation>> tmp = new HashSet<Class<? extends Annotation>>(); tmp.add(Scripts.class); tmp.add(Stylesheets.class); ANNOTATIONS = Collections.unmodifiableSet(tmp); } public AssetMetaModelPlugin() { super("asset"); } @Override public Set<Class<? extends java.lang.annotation.Annotation>> init(ProcessingContext env) { return ANNOTATIONS; } @Override public void init(ApplicationMetaModel metaModel) { metaModel.addChild(AssetsMetaModel.KEY, new AssetsMetaModel(metaModel.getHandle())); } @Override public void processAnnotationAdded(ApplicationMetaModel metaModel, AnnotationKey key, AnnotationState added) { if (metaModel.getHandle().equals(key.getElement())) { AssetsMetaModel assetsMetaModel = metaModel.getChild(AssetsMetaModel.KEY); Integer maxAge = (Integer)added.get("maxAge"); String type; String identifier = key.getType().getIdentifier(); if (identifier.equals(Scripts.class.getSimpleName())) { type = "script"; } else { type = "stylesheet"; } List<ElementHandle.Type> minifiers = (List<ElementHandle.Type>)added.get("minifier"); for (Asset asset : getAssets(type, added, maxAge, minifiers)) { assetsMetaModel.addAsset(asset); } } } @Override public void processAnnotationRemoved(ApplicationMetaModel metaModel, AnnotationKey key, AnnotationState removed) { if (metaModel.getHandle().equals(key.getElement())) { AssetsMetaModel assetsMetaModel = metaModel.getChild(AssetsMetaModel.KEY); for (Asset asset : getAssets(null, removed, null, null)) { assetsMetaModel.removeAsset(asset); } } } private Iterable<Asset> getAssets( String type, AnnotationState annotation, Integer maxAge, List<ElementHandle.Type> minifier) { ArrayList<Asset> assets = new ArrayList<Asset>(); String location = (String)annotation.get("location"); if (location == null) { location = AssetLocation.APPLICATION.name(); } List<AnnotationState> value = (List<AnnotationState>)annotation.get("value"); for (AnnotationState asset : value) { Map<String, Serializable> state = new HashMap<String, Serializable>(asset); if (state.get("location") == null) { state.put("location", location); } if (maxAge != null && state.get("maxAge") == null) { state.put("maxAge", maxAge); } if (minifier != null && state.get("minifier") == null) { state.put("minifier", (Serializable)minifier); } if (state.get("id") == null) { state.put("id", state.get("value")); } assets.add(new Asset(type, state)); } return assets; } @Override public void prePassivate(ApplicationMetaModel metaModel) { ProcessingContext context = metaModel.getProcessingContext(); AssetsMetaModel assetMetaMode = metaModel.getChild(AssetsMetaModel.KEY); // Check duplicate ids HashSet<String> ids = new HashSet<String>(); for (Asset asset : assetMetaMode.getAssets()) { if (!ids.add(asset.id)) { throw DUPLICATE_ASSET_ID.failure(asset.id); } } // Name qn = metaModel.getHandle().getPackageName().append("assets"); if(!context.isCopyFromSourcesExternallyManaged()) { // HashMap<String, URL> bilta = new HashMap<String, URL>(); HashMap<URL, Asset> bilto = new HashMap<URL, Asset>(); for (Asset asset : assetMetaMode.getAssets()) { if (asset.isApplication()) { for (Map.Entry<String, String> entry : asset.getSources().entrySet()) { String source = entry.getValue(); if (!source.startsWith("/")) { URL resource = assetMetaMode.getResources().get(source); if (resource == null) { resource = assetMetaMode.resolveResource(source); } if (resource != null) { bilto.put(resource, asset); bilta.put(entry.getKey(), resource); } else { throw ASSET_NOT_FOUND.failure(source); } } } } } bilta.putAll(assetMetaMode.getResources()); // Process all resources for (Map.Entry<String, URL> entry : bilta.entrySet()) { InputStream in = null; OutputStream out = null; try { URL src = entry.getValue(); URLConnection conn = src.openConnection(); FileObject dst = context.getResource(StandardLocation.CLASS_OUTPUT, qn, entry.getKey()); if (dst == null || dst.getLastModified() < conn.getLastModified()) { dst = context.createResource(StandardLocation.CLASS_OUTPUT, qn, entry.getKey(), context.get(metaModel.getHandle())); context.info("Copying asset from source path " + src + " to class output " + dst.toUri()); Asset r = bilto.get(entry.getValue()); if (r != null) { in = r.open(entry.getKey(), conn); } else { in = conn.getInputStream(); } out = dst.openOutputStream(); Tools.copy(in, out); } else { context.info("Found up to date related asset in class output for " + src); } } catch (IOException e) { throw CANNOT_PROCESS_ASSET.failure(entry.getKey(), e.getMessage()); } finally { Tools.safeClose(in); Tools.safeClose(out); } } } } @Override public JSON getDescriptor(ApplicationMetaModel application) { AssetsMetaModel assetsMetaModel = application.getChild(AssetsMetaModel.KEY); Iterator<Asset> assetsIterator = assetsMetaModel.getAssets().iterator(); if (assetsIterator.hasNext()) { JSON descriptor = new JSON(); JSON assets = new JSON(); while (assetsIterator.hasNext()) { Asset asset = assetsIterator.next(); assets.set(asset.id, asset.getJSON()); } descriptor.set("assets", assets); descriptor.set("package", "assets"); return descriptor; } else { return null; } } }