001/* 002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Anahide Tchertchian 016 */ 017package org.nuxeo.ecm.web.resources.core.service; 018 019import java.net.URL; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027import org.codehaus.plexus.util.dag.CycleDetectedException; 028import org.codehaus.plexus.util.dag.DAG; 029import org.codehaus.plexus.util.dag.TopologicalSorter; 030import org.nuxeo.ecm.web.resources.api.Processor; 031import org.nuxeo.ecm.web.resources.api.Resource; 032import org.nuxeo.ecm.web.resources.api.ResourceBundle; 033import org.nuxeo.ecm.web.resources.api.ResourceContext; 034import org.nuxeo.ecm.web.resources.api.ResourceType; 035import org.nuxeo.ecm.web.resources.api.service.WebResourceManager; 036import org.nuxeo.ecm.web.resources.core.ProcessorDescriptor; 037import org.nuxeo.ecm.web.resources.core.ResourceDescriptor; 038import org.nuxeo.runtime.model.ComponentContext; 039import org.nuxeo.runtime.model.ComponentInstance; 040import org.nuxeo.runtime.model.DefaultComponent; 041 042/** 043 * @since 7.3 044 */ 045public class WebResourceManagerImpl extends DefaultComponent implements WebResourceManager { 046 047 private static final Log log = LogFactory.getLog(WebResourceManagerImpl.class); 048 049 protected static final String RESOURCES_ENDPOINT = "resources"; 050 051 protected ResourceRegistry resources; 052 053 protected static final String RESOURCE_BUNDLES_ENDPOINT = "bundles"; 054 055 protected ResourceBundleRegistry resourceBundles; 056 057 protected static final String PROCESSORS_ENDPOINT = "processors"; 058 059 protected ProcessorRegistry processors; 060 061 // Runtime Component API 062 063 @Override 064 public void activate(ComponentContext context) { 065 super.activate(context); 066 resources = new ResourceRegistry(); 067 resourceBundles = new ResourceBundleRegistry(); 068 processors = new ProcessorRegistry(); 069 } 070 071 @Override 072 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 073 if (RESOURCES_ENDPOINT.equals(extensionPoint)) { 074 ResourceDescriptor resource = (ResourceDescriptor) contribution; 075 computeResourceUri(resource, contributor); 076 registerResource(resource); 077 } else if (RESOURCE_BUNDLES_ENDPOINT.equals(extensionPoint)) { 078 ResourceBundle bundle = (ResourceBundle) contribution; 079 registerResourceBundle(bundle); 080 } else if (PROCESSORS_ENDPOINT.equals(extensionPoint)) { 081 ProcessorDescriptor p = (ProcessorDescriptor) contribution; 082 log.info(String.format("Register processor '%s'", p.getName())); 083 processors.addContribution(p); 084 log.info(String.format("Done registering processor '%s'", p.getName())); 085 } else { 086 log.error(String.format("Unknown contribution to the service, extension point '%s': '%s", extensionPoint, 087 contribution)); 088 } 089 } 090 091 @Override 092 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 093 if (RESOURCES_ENDPOINT.equals(extensionPoint)) { 094 Resource resource = (Resource) contribution; 095 unregisterResource(resource); 096 } else if (RESOURCE_BUNDLES_ENDPOINT.equals(extensionPoint)) { 097 ResourceBundle bundle = (ResourceBundle) contribution; 098 unregisterResourceBundle(bundle); 099 } else if (PROCESSORS_ENDPOINT.equals(extensionPoint)) { 100 ProcessorDescriptor p = (ProcessorDescriptor) contribution; 101 log.info(String.format("Removing processor '%s'", p.getName())); 102 processors.removeContribution(p); 103 log.info(String.format("Done removing processor '%s'", p.getName())); 104 } else { 105 log.error(String.format("Unknown contribution to the service, extension point '%s': '%s", extensionPoint, 106 contribution)); 107 } 108 } 109 110 // service API 111 112 protected void computeResourceUri(ResourceDescriptor resource, ComponentInstance contributor) { 113 String uri = resource.getURI(); 114 if (uri == null) { 115 // build it from local classpath 116 // XXX: hacky wildcard support 117 String path = resource.getPath(); 118 if (path != null) { 119 boolean hasWildcard = false; 120 if (path.endsWith("*")) { 121 hasWildcard = true; 122 path = path.substring(0, path.length() - 1); 123 } 124 URL url = contributor.getContext().getLocalResource(path); 125 if (url == null) { 126 log.error(String.format("Cannot resolve local URL for resource '%s' with path '%s'", 127 resource.getName(), resource.getPath())); 128 } else { 129 String builtUri = url.toString(); 130 if (hasWildcard) { 131 builtUri += "*"; 132 } 133 resource.setURI(builtUri); 134 } 135 } 136 } 137 } 138 139 @Override 140 public Resource getResource(String name) { 141 return resources.getResource(name); 142 } 143 144 @Override 145 public ResourceBundle getResourceBundle(String name) { 146 return resourceBundles.getResourceBundle(name); 147 } 148 149 @Override 150 public List<ResourceBundle> getResourceBundles() { 151 return resourceBundles.getResourceBundles(); 152 } 153 154 @Override 155 public Processor getProcessor(String name) { 156 return processors.getProcessor(name); 157 } 158 159 @Override 160 public List<Processor> getProcessors() { 161 return processors.getProcessors(); 162 } 163 164 @Override 165 public List<Processor> getProcessors(String type) { 166 return processors.getProcessors(type); 167 } 168 169 @Override 170 public List<Resource> getResources(ResourceContext context, String bundleName, String type) { 171 List<Resource> res = new ArrayList<>(); 172 ResourceBundle rb = resourceBundles.getResourceBundle(bundleName); 173 if (rb == null) { 174 if (log.isDebugEnabled()) { 175 log.debug(String.format("Unknown bundle named '%s'", bundleName)); 176 } 177 return res; 178 } 179 180 Map<String, Resource> all = new HashMap<>(); 181 // retrieve deps + filter depending on type + detect cycles 182 DAG graph = new DAG(); 183 for (String rn : rb.getResources()) { 184 Resource r = getResource(rn); 185 if (r == null) { 186 log.error(String.format("Could not resolve resource '%s' on bundle '%s'", rn, bundleName)); 187 continue; 188 } 189 // resolve sub resources of given type before filtering 190 Map<String, Resource> subRes = getSubResources(graph, r, type); 191 if (ResourceType.matches(type, r) || !subRes.isEmpty()) { 192 graph.addVertex(rn); 193 all.put(rn, r); 194 all.putAll(subRes); 195 } 196 } 197 198 for (Object rn : TopologicalSorter.sort(graph)) { 199 Resource r = all.get(rn); 200 if (ResourceType.matches(type, r)) { 201 res.add(r); 202 } 203 } 204 205 return res; 206 } 207 208 protected Map<String, Resource> getSubResources(DAG graph, Resource r, String type) { 209 Map<String, Resource> res = new HashMap<String, Resource>(); 210 List<String> deps = r.getDependencies(); 211 if (deps != null) { 212 for (String dn : deps) { 213 Resource d = getResource(dn); 214 if (d == null) { 215 log.error(String.format("Unknown resource dependency named '%s'", dn)); 216 continue; 217 } 218 if (!ResourceType.matches(type, d)) { 219 continue; 220 } 221 res.put(dn, d); 222 try { 223 graph.addEdge(r.getName(), dn); 224 } catch (CycleDetectedException e) { 225 log.error("Cycle detected in resource dependencies: ", e); 226 break; 227 } 228 res.putAll(getSubResources(graph, d, type)); 229 } 230 } 231 return res; 232 } 233 234 @Override 235 public void registerResourceBundle(ResourceBundle bundle) { 236 log.info(String.format("Register resource bundle '%s'", bundle.getName())); 237 resourceBundles.addContribution(bundle); 238 log.info(String.format("Done registering resource bundle '%s'", bundle.getName())); 239 } 240 241 @Override 242 public void unregisterResourceBundle(ResourceBundle bundle) { 243 log.info(String.format("Removing resource bundle '%s'", bundle.getName())); 244 resourceBundles.removeContribution(bundle); 245 log.info(String.format("Done removing resource bundle '%s'", bundle.getName())); 246 } 247 248 @Override 249 public void registerResource(Resource resource) { 250 log.info(String.format("Register resource '%s'", resource.getName())); 251 resources.addContribution(resource); 252 log.info(String.format("Done registering resource '%s'", resource.getName())); 253 } 254 255 @Override 256 public void unregisterResource(Resource resource) { 257 log.info(String.format("Removing resource '%s'", resource.getName())); 258 resources.removeContribution(resource); 259 log.info(String.format("Done removing resource '%s'", resource.getName())); 260 } 261 262}