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