juzu.impl.asset.AssetManager Maven / Gradle / Ivy
/*
* 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.asset;
import juzu.impl.common.Tools;
import juzu.impl.plugin.application.Application;
import juzu.impl.resource.ResourceResolver;
import javax.inject.Inject;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
/** @author Julien Viet */
public class AssetManager {
/** . */
private HashMap assets = new HashMap();
/** Graph saying which assets depends on which asset. */
private AssetGraph graph = new AssetGraph();
/** . */
protected final String prefix;
/** . */
protected final ResourceResolver applicationResolver;
@Inject
public AssetManager(Application application) {
this.prefix = "/" + application.getDescriptor().getPackageName().replace('.', '/') + "/assets/";
this.applicationResolver = application;
}
AssetManager(String prefix, ResourceResolver applicationResolver) {
this.prefix = prefix;
this.applicationResolver = applicationResolver;
}
public AssetDeployment createDeployment() {
return new AssetDeployment(this);
}
boolean deploy(AssetDeployment deployment) {
// Clone the state
AssetGraph graphClone = new AssetGraph(graph);
HashMap assetsClone = new HashMap(assets);
for (AssetNode asset : deployment.assets) {
// Check it was not previously deployed
if (!assetsClone.keySet().contains(asset.id)) {
for (AssetNode deployed : assetsClone.values()) {
if (deployed.iDependOn.contains(asset.id)) {
if (!graphClone.register(asset.id, deployed.id)) {
return false;
}
}
if (asset.iDependOn.contains(deployed.id)) {
if (!graphClone.register(deployed.id, asset.id)) {
return false;
}
}
}
assetsClone.put(asset.id, asset);
} else {
// log it ?
return false;
}
}
// Everything went fine we updated the manager
assets = assetsClone;
graph = graphClone;
return true;
}
void undeploy(AssetDeployment deployment) {
for (AssetNode asset : deployment.assets) {
for (AssetNode deployed : assets.values()) {
if (deployed.iDependOn.contains(asset.id)) {
graph.unregister(asset.id, deployed.id);
}
if (asset.iDependOn.contains(deployed.id)) {
graph.unregister(deployed.id, asset.id);
}
}
assets.remove(asset.id);
}
}
/**
* Resolve an application asset as a resource URL or return null if it cannot be found.
*
* @param path the path the path within the application
* @return the resource
*/
public AssetResource resolveApplicationAssetResource(String path) {
for (AssetNode asset : assets.values()) {
if (asset.value.equals(path) && asset.resource != null) {
Integer maxAge = asset.asset.getMaxAge();
return new AssetResource(asset.resource, maxAge);
}
}
if (path.startsWith(prefix)) {
URL resolved = applicationResolver.resolve(path);
return new AssetResource(resolved, null);
} else {
return null;
}
}
/**
* Find all assets of the specified type and returns a map of id -> Asset.
*
* @param type the asset type
* @return the asset map
*/
public Map getAssets(String type) {
Map ret = Collections.emptyMap();
for (AssetNode node : assets.values()) {
if (node.asset.getType().equals(type)) {
if (ret.isEmpty()) {
ret = new HashMap();
}
ret.put(node.id, node.asset);
}
}
return ret;
}
/**
* Returns the assets forthe specifid id.
*
* @param id the asset id
* @return the corresponding assets
* @throws NullPointerException
*/
public Asset getAsset(String id) throws NullPointerException {
if (id == null) {
throw new NullPointerException("No null id accepted");
}
AssetNode node = assets.get(id);
return node != null ? node.asset : null;
}
/**
* Perform a topological sort of the provided asset script values.
*
* @param ids the asset ids to resolve
* @return the resolved asset or null
* @throws NullPointerException if the asset ids argument is null
* @throws IllegalArgumentException when asset dependencies cannot be resolved
*/
public Iterable resolveAssets(Iterable ids) throws
NullPointerException,
IllegalArgumentException {
//
if (ids == null) {
throw new NullPointerException("No null asset ids accepted");
}
// Compute the closure of the assets we need
LinkedHashMap> sub = new LinkedHashMap>();
for (LinkedList queue = Tools.addAll(new LinkedList(), ids);!queue.isEmpty();) {
String id = queue.removeFirst();
AssetNode asset = this.assets.get(id);
if (asset != null) {
sub.put(asset.id, new HashSet(asset.iDependOn));
for (String depend : asset.iDependOn) {
if (!sub.containsKey(depend)) {
queue.addLast(depend);
}
}
}
else {
throw new IllegalArgumentException("Cannot resolve asset " + id);
}
}
// Perform the topological sort
LinkedList resolved = new LinkedList();
while (sub.size() > 0) {
boolean found = false;
for (Iterator>> i = sub.entrySet().iterator();i.hasNext();) {
Map.Entry> entry = i.next();
if (entry.getValue().isEmpty()) {
i.remove();
AssetNode asset = this.assets.get(entry.getKey());
resolved.add(asset.asset);
Set dependencies = graph.get(asset.id);
if (dependencies != null) {
for (String dependency : dependencies) {
HashSet foo = sub.get(dependency);
if (foo != null) {
foo.remove(entry.getKey());
}
}
}
found = true;
break;
}
}
if (!found) {
StringBuilder sb = new StringBuilder("Cannot satisfy asset dependencies:\n");
for (Map.Entry> entry : sub.entrySet()) {
sb.append(entry.getKey()).append(" -> ").append(entry.getValue());
}
throw new IllegalArgumentException(sb.toString());
}
}
//
return resolved;
}
}