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