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