View Javadoc

1   /**
2    * Copyright 2005-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.ksb.messaging.servlet;
17  
18  import java.io.IOException;
19  import java.util.List;
20  import java.util.Locale;
21  
22  import javax.servlet.ServletException;
23  import javax.servlet.http.HttpServletRequest;
24  import javax.servlet.http.HttpServletResponse;
25  import javax.xml.namespace.QName;
26  
27  import org.apache.cxf.Bus;
28  import org.apache.cxf.interceptor.Interceptor;
29  import org.apache.cxf.message.Message;
30  import org.apache.cxf.transport.servlet.ServletController;
31  import org.apache.cxf.transport.servlet.ServletTransportFactory;
32  import org.apache.log4j.Logger;
33  import org.kuali.rice.core.api.config.property.ConfigContext;
34  import org.kuali.rice.core.api.exception.RiceRuntimeException;
35  import org.kuali.rice.core.api.util.ClassLoaderUtils;
36  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
37  import org.kuali.rice.ksb.api.bus.Endpoint;
38  import org.kuali.rice.ksb.api.bus.ServiceConfiguration;
39  import org.kuali.rice.ksb.api.bus.support.SoapServiceConfiguration;
40  import org.kuali.rice.ksb.security.SignatureSigningResponseWrapper;
41  import org.kuali.rice.ksb.security.SignatureVerifyingRequestWrapper;
42  import org.kuali.rice.ksb.service.KSBServiceLocator;
43  import org.springframework.beans.BeansException;
44  import org.springframework.context.i18n.LocaleContext;
45  import org.springframework.web.HttpRequestHandler;
46  import org.springframework.web.context.WebApplicationContext;
47  import org.springframework.web.servlet.DispatcherServlet;
48  import org.springframework.web.servlet.HandlerAdapter;
49  import org.springframework.web.servlet.HandlerExecutionChain;
50  import org.springframework.web.servlet.ModelAndView;
51  import org.springframework.web.servlet.mvc.Controller;
52  import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
53  import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
54  
55  
56  /**
57   * A {@link DispatcherServlet} which dispatches incoming requests to the appropriate
58   * service endpoint.
59   *
60   * @author Kuali Rice Team (rice.collab@kuali.org)
61   */
62  public class KSBDispatcherServlet extends DispatcherServlet {
63  
64  	private static final Logger LOG = Logger.getLogger(KSBDispatcherServlet.class);
65  
66  	private static final long serialVersionUID = 6790121225857950019L;
67  	private KSBHttpInvokerHandler httpInvokerHandler;
68  	private ServletController cxfServletController;
69  	
70  	protected void initFrameworkServlet() throws ServletException, BeansException {
71  		this.httpInvokerHandler = new KSBHttpInvokerHandler();
72  		
73          Bus bus = KSBServiceLocator.getCXFBus();
74  
75          List<Interceptor<? extends Message>> inInterceptors = KSBServiceLocator.getInInterceptors();
76          if(inInterceptors != null) {
77          	List<Interceptor<? extends Message>> busInInterceptors = bus.getInInterceptors();
78          	busInInterceptors.addAll(inInterceptors);
79          }
80         
81          List<Interceptor<? extends Message>> outInterceptors = KSBServiceLocator.getOutInterceptors();
82          if(outInterceptors != null) {
83          	List<Interceptor<? extends Message>> busOutInterceptors = bus.getOutInterceptors();
84          	busOutInterceptors.addAll(outInterceptors);
85          }
86          
87  		ServletTransportFactory servletTransportFactory = KSBServiceLocator.getCXFServletTransportFactory();
88  				
89  		this.cxfServletController = new ServletController(servletTransportFactory, this.getServletConfig(), this.getServletContext(), bus);
90  		
91  		if (!ConfigContext.getCurrentContextConfig().getDevMode()) {
92  		    // disable handling of URLs ending in /services which display CXF generated service lists
93  		    this.cxfServletController.setHideServiceList(true);
94  		}
95  		
96  		this.setPublishEvents(false);
97  	}
98  
99  	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
100 		if (handler instanceof HttpRequestHandler) {
101 			return new HttpRequestHandlerAdapter();
102 		} else if (handler instanceof Controller) {
103 			Object unwrappedHandler = ClassLoaderUtils.unwrapFromProxy(handler);
104 			if (unwrappedHandler instanceof CXFServletControllerAdapter) {
105 				// TODO this just seems weird as this controller is initially null when it's created, does there need to be some synchronization here?
106 				((CXFServletControllerAdapter)unwrappedHandler).setController(cxfServletController);
107 			}			
108 			return new SimpleControllerHandlerAdapter();
109 		}
110 		throw new RiceRuntimeException("handler of type " + handler.getClass().getName() + " is not known and can't be used by " + KSBDispatcherServlet.class.getName());
111 	}
112 
113 	/**
114 	 * Return the HandlerExecutionChain for this request.
115 	 * Try all handler mappings in order.
116 	 * @param request current HTTP request
117 	 * @param cache whether to cache the HandlerExecutionChain in a request attribute
118 	 * @return the HandlerExceutionChain, or <code>null</code> if no handler could be found
119 	 */
120 	protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
121 		return this.httpInvokerHandler.getHandler(request);
122 	}
123 
124 	@Override
125 	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
126 		try {
127 			QName serviceName = this.httpInvokerHandler.getServiceNameFromRequest(request);
128 			LOG.info("Caught Exception from service " + serviceName, ex);
129 		} catch (Throwable throwable) {
130 			LOG.warn("Caught exception attempting to log exception thrown from remotely accessed service", throwable);
131 		}
132 		return null;
133 	}
134 
135 	/**
136 	 * Overrides the service method to replace the request and responses with one which will provide input and output streams for
137 	 * verifying and signing the data.
138 	 */
139 	@Override
140 	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
141 		if (isSecure(request)) {
142 			super.service(new SignatureVerifyingRequestWrapper(request), new SignatureSigningResponseWrapper(response));
143 		} else {
144 			super.service(request, response);
145 		}
146 	}
147 
148 	protected boolean isSecure(HttpServletRequest request) {
149 		QName serviceName = this.httpInvokerHandler.getServiceNameFromRequest(request);
150 		if (LOG.isDebugEnabled()) {
151 		    LOG.debug("Checking service " + serviceName + " for security enabled");
152 		}
153 		Endpoint endpoint = KsbApiServiceLocator.getServiceBus().getEndpoint(serviceName);
154 		if (endpoint == null) {
155 			LOG.error("Attempting to acquire non-existent service " + request.getRequestURI());
156 		    throw new RiceRuntimeException("Attempting to acquire non-existent service.");
157 		}
158 		ServiceConfiguration serviceConfiguration = endpoint.getServiceConfiguration();
159 		if (serviceConfiguration instanceof SoapServiceConfiguration) {
160 		    return false;
161 		}
162 		return serviceConfiguration.getBusSecurity();
163 	}
164 	
165 	/**
166 	 * Overriding this method to correct a NullPointerException when the
167 	 * getLocale() method is called on the LocaleContext returned here.  This
168 	 * tries to use the LocaleContext from the parent class, but if a NPE is
169 	 * thrown when getLocale() is invoked on the context it will return a new
170 	 * LocaleContext which defaults to English.
171 	 */
172 	@Override
173     protected LocaleContext buildLocaleContext(HttpServletRequest request) {
174         try {
175             // Get the context from the parent class
176             LocaleContext localeContext = super.buildLocaleContext(request);
177             // Check to see if the localeResolver is null
178             localeContext.getLocale();
179             return localeContext;
180         } catch (NullPointerException npe) {
181             // If a NPE is thrown catch it and return a LocaleContext which
182             // always returns English
183             return new LocaleContext() {
184                 public Locale getLocale() {
185                     return Locale.ENGLISH;
186                 }
187             };
188         }
189     }
190 }