001package org.tynamo.resteasy;
002
003import org.apache.tapestry5.SymbolConstants;
004import org.apache.tapestry5.internal.services.CheckForUpdatesFilter;
005import org.apache.tapestry5.ioc.annotations.Inject;
006import org.apache.tapestry5.ioc.annotations.IntermediateType;
007import org.apache.tapestry5.ioc.annotations.Symbol;
008import org.apache.tapestry5.ioc.services.SymbolSource;
009import org.apache.tapestry5.ioc.util.TimeInterval;
010import org.apache.tapestry5.services.*;
011import org.jboss.resteasy.core.Dispatcher;
012import org.jboss.resteasy.core.SynchronousDispatcher;
013import org.jboss.resteasy.plugins.server.servlet.*;
014import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
015import org.jboss.resteasy.spi.HttpRequest;
016import org.jboss.resteasy.spi.HttpResponse;
017import org.jboss.resteasy.spi.ResteasyProviderFactory;
018import org.jboss.resteasy.spi.ResteasyUriInfo;
019import org.jboss.resteasy.util.GetRestful;
020import org.slf4j.Logger;
021
022import javax.servlet.ServletException;
023import javax.servlet.http.HttpServletRequest;
024import javax.servlet.http.HttpServletResponse;
025import javax.ws.rs.core.Application;
026import javax.ws.rs.ext.Provider;
027import java.io.IOException;
028import java.util.ArrayList;
029import java.util.regex.Pattern;
030
031public class ResteasyRequestFilter implements HttpServletRequestFilter, HttpRequestFactory, HttpResponseFactory {
032
033        private final ServletContainerDispatcher servletContainerDispatcher;
034        private final Dispatcher dispatcher;
035        private final ResteasyProviderFactory providerFactory;
036        private final ApplicationGlobals globals;
037
038        private final Pattern filterPattern;
039        private final Logger logger;
040
041        private boolean productionMode;
042        private boolean corsEnabled;
043
044        private CheckForUpdatesFilter checkForUpdatesFilter;
045
046        private RequestHandler dummyHandler = new RequestHandler() {
047                @Override
048                public boolean service(Request request, Response response) throws IOException {
049                        return false;
050                }
051        };
052
053        public ResteasyRequestFilter(
054                        @Inject @Symbol(ResteasySymbols.MAPPING_PREFIX) String filterPath,
055                        Logger logger,
056                        ApplicationGlobals globals,
057                        Application application,
058                        SymbolSource source,
059                        @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode,
060                        UpdateListenerHub updateListenerHub,
061                        @Symbol(SymbolConstants.FILE_CHECK_INTERVAL) @IntermediateType(TimeInterval.class) long checkInterval,
062                        @Symbol(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT) @IntermediateType(TimeInterval.class) long updateTimeout,
063                        @Symbol(ResteasySymbols.CORS_ENABLED) boolean corsEnabled
064        ) throws ServletException {
065
066                this.logger = logger;
067                this.filterPattern = Pattern.compile(filterPath + ".*", Pattern.CASE_INSENSITIVE);
068                this.globals = globals;
069
070                ListenerBootstrap bootstrap = new TapestryResteasyBootstrap(globals.getServletContext(), source);
071
072                servletContainerDispatcher = new ServletContainerDispatcher();
073                servletContainerDispatcher.init(globals.getServletContext(), bootstrap, this, this);
074                dispatcher = servletContainerDispatcher.getDispatcher();
075                providerFactory = servletContainerDispatcher.getDispatcher().getProviderFactory();
076                processApplication(application);
077
078                this.productionMode = productionMode;
079                this.corsEnabled = corsEnabled;
080                checkForUpdatesFilter = new CheckForUpdatesFilter(updateListenerHub, checkInterval, updateTimeout);
081        }
082
083        @Override
084        public boolean service(HttpServletRequest request, HttpServletResponse response,
085                               HttpServletRequestHandler handler) throws IOException {
086
087                String path = request.getServletPath();
088                String pathInfo = request.getPathInfo();
089
090                if (pathInfo != null) path += pathInfo;
091
092                if (filterPattern.matcher(path).matches()) {
093
094                        if (!productionMode) {
095                                checkForUpdatesFilter.service(null, null, dummyHandler);
096                        }
097
098                        servletContainerDispatcher.service(request.getMethod(), request, response, true);
099                        if (corsEnabled)
100                        {
101                                response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
102                        }
103                        return true;
104                }
105
106                return handler.service(request, response);
107        }
108
109        @Override
110        public HttpRequest createResteasyHttpRequest(String httpMethod, HttpServletRequest request,
111                                                                                                 ResteasyHttpHeaders headers, ResteasyUriInfo uriInfo,
112                                                                                                 HttpResponse theResponse, HttpServletResponse response) {
113                return new HttpServletInputMessage(request, response, globals.getServletContext(), theResponse, headers, uriInfo, httpMethod.toUpperCase(), (SynchronousDispatcher) dispatcher);
114        }
115
116        @Override
117        public HttpResponse createResteasyHttpResponse(HttpServletResponse response) {
118                return createServletResponse(response);
119        }
120
121        protected HttpResponse createServletResponse(HttpServletResponse response) {
122                return new HttpServletResponseWrapper(response, providerFactory);
123        }
124
125        private void processApplication(Application config) {
126                logger.info("Deploying " + Application.class.getName() + ": " + config.getClass());
127                ArrayList<Class> actualResourceClasses = new ArrayList<Class>();
128                ArrayList<Class> actualProviderClasses = new ArrayList<Class>();
129                ArrayList resources = new ArrayList();
130                ArrayList providers = new ArrayList();
131                if (config.getClasses() != null) {
132                        for (Class clazz : config.getClasses()) {
133                                if (GetRestful.isRootResource(clazz)) {
134                                        actualResourceClasses.add(clazz);
135                                } else if (clazz.isAnnotationPresent(Provider.class)) {
136                                        actualProviderClasses.add(clazz);
137                                } else {
138                                        throw new RuntimeException("Application.getClasses() returned unknown class type: " + clazz.getName());
139                                }
140                        }
141                }
142                if (config.getSingletons() != null) {
143                        for (Object obj : config.getSingletons()) {
144                                if (GetRestful.isRootResource(obj.getClass())) {
145                                        logger.info("Adding singleton resource " + obj.getClass().getName() + " from Application " + Application.class.getName());
146                                        resources.add(obj);
147                                } else if (obj.getClass().isAnnotationPresent(Provider.class)) {
148                                        providers.add(obj);
149                                } else {
150                                        throw new RuntimeException("Application.getSingletons() returned unknown class type: " + obj.getClass().getName());
151                                }
152                        }
153                }
154                for (Class clazz : actualProviderClasses) providerFactory.registerProvider(clazz);
155                for (Object obj : providers) providerFactory.registerProviderInstance(obj);
156                for (Class clazz : actualResourceClasses) dispatcher.getRegistry().addPerRequestResource(clazz);
157                for (Object obj : resources) dispatcher.getRegistry().addSingletonResource(obj);
158        }
159}