/* * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * bstefanescu */ package org.nuxeo.ecm.webengine.jaxrs; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.ws.rs.Path; import javax.ws.rs.core.Application; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.platform.rendering.api.RenderingEngine; import org.nuxeo.ecm.webengine.jaxrs.servlet.config.ResourceExtension; import org.nuxeo.ecm.webengine.jaxrs.views.BundleResource; import org.nuxeo.ecm.webengine.jaxrs.views.TemplateViewMessageBodyWriter; import org.nuxeo.ecm.webengine.jaxrs.views.ViewMessageBodyWriter; import org.osgi.framework.Bundle; /** * A composite JAX-RS application that can receive fragments from outside. * * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public class ApplicationHost extends Application { private static final Log log = LogFactory.getLog(ApplicationHost.class); protected final String name; protected final Map<String, Boolean> features = new HashMap<String, Boolean>(); protected final List<ApplicationFragment> apps; protected List<Reloadable> listeners; protected RenderingEngine rendering; /** * Sub-Resources extensions */ protected Map<String, ResourceExtension> extensions; /** * Root resource classes to owner bundles. This is a fall-back for FrameworkUtils.getBundle(class) since is not * supported in all OSGi like frameworks */ protected HashMap<Class<?>, Bundle> class2Bundles; public ApplicationHost(String name) { this.name = name; apps = new ArrayList<ApplicationFragment>(); class2Bundles = new HashMap<Class<?>, Bundle>(); listeners = new ArrayList<Reloadable>(); extensions = new HashMap<String, ResourceExtension>(); } public BundleResource getExtension(BundleResource target, String segment) { ResourceExtension xt = getExtension(target.getClass().getName() + "#" + segment); if (xt != null) { BundleResource res = target.getResource(xt.getResourceClass()); if (res != null && res.accept(target)) { res.getContext().pushBundle(xt.getBundle()); return res; } } return null; } public RenderingEngine getRendering() { return rendering; } public void setRendering(RenderingEngine rendering) { this.rendering = rendering; } public synchronized void addExtension(ResourceExtension xt) { extensions.put(xt.getId(), xt); class2Bundles.put(xt.getResourceClass(), xt.getBundle()); if (rendering != null) { rendering.flushCache(); } } public synchronized void removeExtension(ResourceExtension xt) { extensions.remove(xt.getId()); class2Bundles.remove(xt.getResourceClass()); if (rendering != null) { rendering.flushCache(); } } public synchronized ResourceExtension getExtension(String id) { return extensions.get(id); } public synchronized ResourceExtension[] getExtensions(ResourceExtension xt) { return extensions.values().toArray(new ResourceExtension[extensions.size()]); } public String getName() { return name; } public Map<String, Boolean> getFeatures() { return features; } public synchronized void add(ApplicationFragment app) { apps.add(app); } public synchronized void remove(ApplicationFragment app) { apps.remove(app); } public synchronized ApplicationFragment[] getApplications() { return apps.toArray(new ApplicationFragment[apps.size()]); } public synchronized void addReloadListener(Reloadable listener) { listeners.add(listener); } public synchronized void removeReloadListener(Reloadable listener) { listeners.remove(listener); } public synchronized void reload() { for (ApplicationFragment fragment : apps) { fragment.reload(); } // TODO this will not work with extension subresources - find a fix class2Bundles = new HashMap<Class<?>, Bundle>(); for (Reloadable listener : listeners) { listener.reload(); } if (rendering != null) { rendering.flushCache(); } } /** * Get the bundle declaring the given root class. This method is not synchronized since it is assumed to be called * after the application was created and before it was destroyed. <br> * When a bundle is refreshing this method may throw exceptions but it is not usual to refresh bundles at runtime * and making requests in same time. * * @param clazz */ public Bundle getBundle(Class<?> clazz) { return class2Bundles.get(clazz); } @Override public synchronized Set<Class<?>> getClasses() { HashSet<Class<?>> result = new HashSet<Class<?>>(); for (ApplicationFragment app : getApplications()) { try { for (Class<?> clazz : app.getClasses()) { if (clazz.isAnnotationPresent(Path.class)) { class2Bundles.put(clazz, app.getBundle()); } result.add(clazz); } } catch (java.lang.LinkageError e) { log.error(e); } } return result; } @Override public synchronized Set<Object> getSingletons() { HashSet<Object> result = new HashSet<Object>(); result.add(new TemplateViewMessageBodyWriter()); result.add(new ViewMessageBodyWriter()); for (ApplicationFragment app : getApplications()) { for (Object obj : app.getSingletons()) { if (obj.getClass().isAnnotationPresent(Path.class)) { class2Bundles.put(obj.getClass(), app.getBundle()); } result.add(obj); } } return result; } }