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}