001/* 002 * Copyright (c) 2006-2012 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * bstefanescu 011 */ 012package org.nuxeo.ecm.webengine.jaxrs; 013 014import java.util.ArrayList; 015import java.util.HashMap; 016import java.util.HashSet; 017import java.util.List; 018import java.util.Map; 019import java.util.Set; 020 021import javax.ws.rs.Path; 022import javax.ws.rs.core.Application; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.ecm.platform.rendering.api.RenderingEngine; 027import org.nuxeo.ecm.webengine.jaxrs.servlet.config.ResourceExtension; 028import org.nuxeo.ecm.webengine.jaxrs.views.BundleResource; 029import org.nuxeo.ecm.webengine.jaxrs.views.TemplateViewMessageBodyWriter; 030import org.nuxeo.ecm.webengine.jaxrs.views.ViewMessageBodyWriter; 031import org.osgi.framework.Bundle; 032 033/** 034 * A composite JAX-RS application that can receive fragments from outside. 035 * 036 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 037 */ 038public class ApplicationHost extends Application { 039 040 private static final Log log = LogFactory.getLog(ApplicationHost.class); 041 042 protected final String name; 043 044 protected final Map<String, Boolean> features = new HashMap<String, Boolean>(); 045 046 protected final List<ApplicationFragment> apps; 047 048 protected List<Reloadable> listeners; 049 050 protected RenderingEngine rendering; 051 052 /** 053 * Sub-Resources extensions 054 */ 055 protected Map<String, ResourceExtension> extensions; 056 057 /** 058 * Root resource classes to owner bundles. This is a fall-back for FrameworkUtils.getBundle(class) since is not 059 * supported in all OSGi like frameworks 060 */ 061 protected HashMap<Class<?>, Bundle> class2Bundles; 062 063 public ApplicationHost(String name) { 064 this.name = name; 065 apps = new ArrayList<ApplicationFragment>(); 066 class2Bundles = new HashMap<Class<?>, Bundle>(); 067 listeners = new ArrayList<Reloadable>(); 068 extensions = new HashMap<String, ResourceExtension>(); 069 } 070 071 public BundleResource getExtension(BundleResource target, String segment) { 072 ResourceExtension xt = getExtension(target.getClass().getName() + "#" + segment); 073 if (xt != null) { 074 BundleResource res = target.getResource(xt.getResourceClass()); 075 if (res != null && res.accept(target)) { 076 res.getContext().pushBundle(xt.getBundle()); 077 return res; 078 } 079 } 080 return null; 081 } 082 083 public RenderingEngine getRendering() { 084 return rendering; 085 } 086 087 public void setRendering(RenderingEngine rendering) { 088 this.rendering = rendering; 089 } 090 091 public synchronized void addExtension(ResourceExtension xt) { 092 extensions.put(xt.getId(), xt); 093 class2Bundles.put(xt.getResourceClass(), xt.getBundle()); 094 if (rendering != null) { 095 rendering.flushCache(); 096 } 097 } 098 099 public synchronized void removeExtension(ResourceExtension xt) { 100 extensions.remove(xt.getId()); 101 class2Bundles.remove(xt.getResourceClass()); 102 if (rendering != null) { 103 rendering.flushCache(); 104 } 105 } 106 107 public synchronized ResourceExtension getExtension(String id) { 108 return extensions.get(id); 109 } 110 111 public synchronized ResourceExtension[] getExtensions(ResourceExtension xt) { 112 return extensions.values().toArray(new ResourceExtension[extensions.size()]); 113 } 114 115 public String getName() { 116 return name; 117 } 118 119 public Map<String, Boolean> getFeatures() { 120 return features; 121 } 122 123 public synchronized void add(ApplicationFragment app) { 124 apps.add(app); 125 } 126 127 public synchronized void remove(ApplicationFragment app) { 128 apps.remove(app); 129 } 130 131 public synchronized ApplicationFragment[] getApplications() { 132 return apps.toArray(new ApplicationFragment[apps.size()]); 133 } 134 135 public synchronized void addReloadListener(Reloadable listener) { 136 listeners.add(listener); 137 } 138 139 public synchronized void removeReloadListener(Reloadable listener) { 140 listeners.remove(listener); 141 } 142 143 public synchronized void reload() { 144 for (ApplicationFragment fragment : apps) { 145 fragment.reload(); 146 } 147 // TODO this will not work with extension subresources - find a fix 148 class2Bundles = new HashMap<Class<?>, Bundle>(); 149 for (Reloadable listener : listeners) { 150 listener.reload(); 151 } 152 if (rendering != null) { 153 rendering.flushCache(); 154 } 155 } 156 157 /** 158 * Get the bundle declaring the given root class. This method is not synchronized since it is assumed to be called 159 * after the application was created and before it was destroyed. <br> 160 * When a bundle is refreshing this method may throw exceptions but it is not usual to refresh bundles at runtime 161 * and making requests in same time. 162 * 163 * @param clazz 164 */ 165 public Bundle getBundle(Class<?> clazz) { 166 return class2Bundles.get(clazz); 167 } 168 169 @Override 170 public synchronized Set<Class<?>> getClasses() { 171 HashSet<Class<?>> result = new HashSet<Class<?>>(); 172 for (ApplicationFragment app : getApplications()) { 173 try { 174 for (Class<?> clazz : app.getClasses()) { 175 if (clazz.isAnnotationPresent(Path.class)) { 176 class2Bundles.put(clazz, app.getBundle()); 177 } 178 result.add(clazz); 179 } 180 } catch (java.lang.LinkageError e) { 181 log.error(e); 182 } 183 } 184 return result; 185 } 186 187 @Override 188 public synchronized Set<Object> getSingletons() { 189 HashSet<Object> result = new HashSet<Object>(); 190 result.add(new TemplateViewMessageBodyWriter()); 191 result.add(new ViewMessageBodyWriter()); 192 for (ApplicationFragment app : getApplications()) { 193 for (Object obj : app.getSingletons()) { 194 if (obj.getClass().isAnnotationPresent(Path.class)) { 195 class2Bundles.put(obj.getClass(), app.getBundle()); 196 } 197 result.add(obj); 198 } 199 } 200 return result; 201 } 202 203}