001/* 002 * (C) Copyright 2006-2011 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.servlet.config; 020 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.Hashtable; 024import java.util.List; 025import java.util.Map; 026 027import javax.servlet.ServletException; 028 029import org.nuxeo.ecm.webengine.jaxrs.Activator; 030import org.nuxeo.ecm.webengine.jaxrs.servlet.ServletHolder; 031import org.osgi.framework.Bundle; 032import org.osgi.service.http.HttpService; 033import org.osgi.service.http.NamespaceException; 034 035/** 036 * Handle servlet registration from Nuxeo extension points. This class is a singleton shared by the {@link Activator} 037 * and the {@link ServletRegistryComponent} component. Because we don't have yet a solution to synchronize the 038 * initialization time of the Activator and a Nuxeo component we are using a singleton instance to be able 039 * 040 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 041 */ 042public class ServletRegistry { 043 044 public static final String SERVLET_NAME = ServletRegistry.class.getName() + ".name"; 045 046 private static volatile ServletRegistry instance; 047 048 public static ServletRegistry getInstance() { 049 ServletRegistry reg = instance; 050 if (reg == null) { 051 synchronized (ServletRegistry.class) { 052 reg = new ServletRegistry(); 053 instance = reg; 054 } 055 } 056 return reg; 057 } 058 059 public static synchronized void dispose() { 060 instance = null; 061 } 062 063 /** 064 * Servlets contributed to the extension points. Servlets are contributed to the {@link HttpService} when it becomes 065 * available. 066 */ 067 protected List<ServletDescriptor> servlets; 068 069 protected List<FilterSetDescriptor> filters; 070 071 /** 072 * Store resources contributed from external bundles to servlets. Map the servlet path to the list of contributed 073 * resources 074 */ 075 protected Map<String, List<ResourcesDescriptor>> resources; 076 077 /** 078 * The registered HttpContext mapped to the servlet path. An HttpContext is created and inserted in this map when 079 * its servlet is registered against the HttpService. The context is removed when the servlet is unregsitered. 080 * <p> 081 * Resource contributions are injected into the context and reinjected each time the context is restarted. 082 */ 083 protected Map<String, BundleHttpContext> contexts; 084 085 /** 086 * The HttpService instance is injected when the service becomes available by the Activator. 087 */ 088 protected HttpService service; 089 090 /** 091 * The bundle owning this class 092 */ 093 protected Bundle bundle; 094 095 private ServletRegistry() { 096 this.servlets = new ArrayList<ServletDescriptor>(); 097 this.filters = new ArrayList<FilterSetDescriptor>(); 098 this.resources = new HashMap<String, List<ResourcesDescriptor>>(); 099 this.contexts = new HashMap<String, BundleHttpContext>(); 100 } 101 102 public synchronized ServletDescriptor[] getServletDescriptors() { 103 return servlets.toArray(new ServletDescriptor[servlets.size()]); 104 } 105 106 public synchronized FilterSetDescriptor[] getFilterSetDescriptors() { 107 return filters.toArray(new FilterSetDescriptor[filters.size()]); 108 } 109 110 public ServletDescriptor getServletDescriptor(String name) { 111 for (ServletDescriptor servlet : getServletDescriptors()) { 112 if (name.equals(servlet.name)) { 113 return servlet; 114 } 115 } 116 return null; 117 } 118 119 public List<FilterSetDescriptor> getFiltersFor(String name) { 120 ArrayList<FilterSetDescriptor> list = new ArrayList<FilterSetDescriptor>(); 121 for (FilterSetDescriptor filter : getFilterSetDescriptors()) { 122 if (name.equals(filter.targetServlet)) { 123 list.add(filter); 124 } 125 } 126 return list; 127 } 128 129 /** 130 * Called by the service tracker when HttpService is up to configure it with current contributed servlets 131 * 132 * @param service 133 */ 134 public synchronized void initHttpService(HttpService service) throws ServletException, NamespaceException { 135 if (this.service == null) { 136 this.service = service; 137 installServlets(); 138 } 139 } 140 141 public HttpService getHttpService() { 142 return service; 143 } 144 145 public synchronized void addServlet(ServletDescriptor descriptor) throws ServletException, NamespaceException { 146 servlets.add(descriptor); 147 installServlet(descriptor); 148 } 149 150 public synchronized void removeServlet(ServletDescriptor descriptor) { 151 servlets.remove(descriptor); 152 contexts.remove(descriptor.path); 153 if (service != null) { 154 // destroy first the listeners if any was initialized 155 ListenerSetDescriptor lsd = descriptor.getListenerSet(); 156 if (lsd != null) { 157 lsd.destroy(); 158 } 159 // unregister the servlet 160 service.unregister(descriptor.path); 161 } 162 } 163 164 public synchronized void reloadServlet(ServletDescriptor descriptor) throws ServletException, NamespaceException { 165 removeServlet(descriptor); 166 addServlet(descriptor); 167 } 168 169 public synchronized void addFilterSet(FilterSetDescriptor filter) { 170 filters.add(filter); 171 } 172 173 public synchronized void removeFilterSet(FilterSetDescriptor filter) { 174 filters.remove(filter); 175 } 176 177 public synchronized void addResources(ResourcesDescriptor rd) { 178 List<ResourcesDescriptor> list = resources.get(rd.getServlet()); 179 if (list == null) { 180 list = new ArrayList<ResourcesDescriptor>(); 181 } 182 list.add(rd); 183 // update context 184 BundleHttpContext ctx = contexts.get(rd.getServlet()); 185 if (ctx != null) { 186 ctx.setResources(list.toArray(new ResourcesDescriptor[list.size()])); 187 } 188 } 189 190 public synchronized void removeResources(ResourcesDescriptor rd) { 191 List<ResourcesDescriptor> list = resources.get(rd.getServlet()); 192 if (list != null) { 193 if (list.remove(rd)) { 194 if (list.isEmpty()) { 195 resources.remove(rd.getServlet()); 196 } 197 // update context 198 BundleHttpContext ctx = contexts.get(rd.getServlet()); 199 if (ctx != null) { 200 ctx.setResources(list.toArray(new ResourcesDescriptor[list.size()])); 201 } 202 } 203 } 204 } 205 206 private synchronized void installServlets() throws ServletException, NamespaceException { 207 if (service != null) { 208 for (ServletDescriptor sd : servlets) { 209 installServlet(sd); 210 } 211 } 212 } 213 214 private void installServlet(ServletDescriptor sd) throws ServletException, NamespaceException { 215 if (service != null) { 216 // ClassRef ref = sd.getClassRef(); 217 BundleHttpContext ctx = new BundleHttpContext(sd.bundle, sd.resources); 218 List<ResourcesDescriptor> rd = resources.get(sd.path); 219 // register resources contributed so far 220 if (rd != null) { 221 ctx.setResources(rd.toArray(new ResourcesDescriptor[rd.size()])); 222 } 223 Hashtable<String, String> params = new Hashtable<String, String>(); 224 if (sd.name != null) { 225 params.putAll(sd.getInitParams()); 226 params.put(SERVLET_NAME, sd.name); 227 } 228 service.registerServlet(sd.path, new ServletHolder(), params, ctx); 229 contexts.put(sd.path, ctx); 230 } 231 } 232 233 // static class BundleHttpContext implements HttpContext { 234 // protected Bundle bundle; 235 // public BundleHttpContext(Bundle bundle) { 236 // this.bundle = bundle; 237 // } 238 // @Override 239 // public String getMimeType(String name) { 240 // return null; 241 // } 242 // @Override 243 // public URL getResource(String name) { 244 // return null; 245 // } 246 // @Override 247 // public boolean handleSecurity(HttpServletRequest request, 248 // HttpServletResponse response) throws IOException { 249 // // default behaviour assumes the container has already performed authentication 250 // return true; 251 // } 252 // } 253}