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<>(); 097 this.filters = new ArrayList<>(); 098 this.resources = new HashMap<>(); 099 this.contexts = new HashMap<>(); 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<>(); 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 public synchronized void initHttpService(HttpService service) throws ServletException, NamespaceException { 133 if (this.service == null) { 134 this.service = service; 135 installServlets(); 136 } 137 } 138 139 public HttpService getHttpService() { 140 return service; 141 } 142 143 public synchronized void addServlet(ServletDescriptor descriptor) throws ServletException, NamespaceException { 144 servlets.add(descriptor); 145 installServlet(descriptor); 146 } 147 148 public synchronized void removeServlet(ServletDescriptor descriptor) { 149 servlets.remove(descriptor); 150 contexts.remove(descriptor.path); 151 if (service != null) { 152 // destroy first the listeners if any was initialized 153 ListenerSetDescriptor lsd = descriptor.getListenerSet(); 154 if (lsd != null) { 155 lsd.destroy(); 156 } 157 // unregister the servlet 158 service.unregister(descriptor.path); 159 } 160 } 161 162 public synchronized void reloadServlet(ServletDescriptor descriptor) throws ServletException, NamespaceException { 163 removeServlet(descriptor); 164 addServlet(descriptor); 165 } 166 167 public synchronized void addFilterSet(FilterSetDescriptor filter) { 168 filters.add(filter); 169 } 170 171 public synchronized void removeFilterSet(FilterSetDescriptor filter) { 172 filters.remove(filter); 173 } 174 175 public synchronized void addResources(ResourcesDescriptor rd) { 176 List<ResourcesDescriptor> list = resources.get(rd.getServlet()); 177 if (list == null) { 178 list = new ArrayList<>(); 179 } 180 list.add(rd); 181 // update context 182 BundleHttpContext ctx = contexts.get(rd.getServlet()); 183 if (ctx != null) { 184 ctx.setResources(list.toArray(new ResourcesDescriptor[list.size()])); 185 } 186 } 187 188 public synchronized void removeResources(ResourcesDescriptor rd) { 189 List<ResourcesDescriptor> list = resources.get(rd.getServlet()); 190 if (list != null) { 191 if (list.remove(rd)) { 192 if (list.isEmpty()) { 193 resources.remove(rd.getServlet()); 194 } 195 // update context 196 BundleHttpContext ctx = contexts.get(rd.getServlet()); 197 if (ctx != null) { 198 ctx.setResources(list.toArray(new ResourcesDescriptor[list.size()])); 199 } 200 } 201 } 202 } 203 204 private synchronized void installServlets() throws ServletException, NamespaceException { 205 if (service != null) { 206 for (ServletDescriptor sd : servlets) { 207 installServlet(sd); 208 } 209 } 210 } 211 212 private void installServlet(ServletDescriptor sd) throws ServletException, NamespaceException { 213 if (service != null) { 214 // ClassRef ref = sd.getClassRef(); 215 BundleHttpContext ctx = new BundleHttpContext(sd.bundle, sd.resources); 216 List<ResourcesDescriptor> rd = resources.get(sd.path); 217 // register resources contributed so far 218 if (rd != null) { 219 ctx.setResources(rd.toArray(new ResourcesDescriptor[rd.size()])); 220 } 221 Hashtable<String, String> params = new Hashtable<>(); 222 if (sd.name != null) { 223 params.putAll(sd.getInitParams()); 224 params.put(SERVLET_NAME, sd.name); 225 } 226 service.registerServlet(sd.path, new ServletHolder(), params, ctx); 227 contexts.put(sd.path, ctx); 228 } 229 } 230 231 // static class BundleHttpContext implements HttpContext { 232 // protected Bundle bundle; 233 // public BundleHttpContext(Bundle bundle) { 234 // this.bundle = bundle; 235 // } 236 // @Override 237 // public String getMimeType(String name) { 238 // return null; 239 // } 240 // @Override 241 // public URL getResource(String name) { 242 // return null; 243 // } 244 // @Override 245 // public boolean handleSecurity(HttpServletRequest request, 246 // HttpServletResponse response) throws IOException { 247 // // default behaviour assumes the container has already performed authentication 248 // return true; 249 // } 250 // } 251}