View Javadoc

1   /**
2    * Copyright 2005-2013 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 org.apache.commons.collections.EnumerationUtils;
19  import org.apache.cxf.Bus;
20  import org.apache.cxf.interceptor.Interceptor;
21  import org.apache.cxf.message.Message;
22  import org.apache.cxf.transport.http.DestinationRegistry;
23  import org.apache.cxf.transport.http.HTTPTransportFactory;
24  import org.apache.cxf.transport.servlet.ServletController;
25  import org.apache.cxf.transport.servlet.servicelist.ServiceListGeneratorServlet;
26  import org.apache.log4j.Logger;
27  import org.kuali.rice.core.api.config.property.ConfigContext;
28  import org.kuali.rice.core.api.exception.RiceRuntimeException;
29  import org.kuali.rice.core.api.util.ClassLoaderUtils;
30  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
31  import org.kuali.rice.ksb.api.bus.Endpoint;
32  import org.kuali.rice.ksb.api.bus.ServiceConfiguration;
33  import org.kuali.rice.ksb.api.bus.support.SoapServiceConfiguration;
34  import org.kuali.rice.ksb.security.SignatureSigningResponseWrapper;
35  import org.kuali.rice.ksb.security.SignatureVerifyingRequestWrapper;
36  import org.kuali.rice.ksb.service.KSBServiceLocator;
37  import org.springframework.beans.BeansException;
38  import org.springframework.web.HttpRequestHandler;
39  import org.springframework.web.context.WebApplicationContext;
40  import org.springframework.web.servlet.DispatcherServlet;
41  import org.springframework.web.servlet.HandlerAdapter;
42  import org.springframework.web.servlet.HandlerExecutionChain;
43  import org.springframework.web.servlet.ModelAndView;
44  import org.springframework.web.servlet.mvc.Controller;
45  import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
46  import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
47  
48  import javax.servlet.ServletConfig;
49  import javax.servlet.ServletContext;
50  import javax.servlet.ServletException;
51  import javax.servlet.http.HttpServletRequest;
52  import javax.servlet.http.HttpServletResponse;
53  import javax.xml.namespace.QName;
54  import java.io.IOException;
55  import java.util.Enumeration;
56  import java.util.List;
57  import java.util.Vector;
58  
59  /**
60   * A {@link DispatcherServlet} which dispatches incoming requests to the appropriate
61   * service endpoint.
62   *
63   * @author Kuali Rice Team (rice.collab@kuali.org)
64   */
65  public class KSBDispatcherServlet extends DispatcherServlet {
66  
67  	private static final Logger LOG = Logger.getLogger(KSBDispatcherServlet.class);
68  	private static final long serialVersionUID = 6790121225857950019L;
69      private static final String REMOTING_SERVLET_CONFIG_LOCATION = "classpath:org/kuali/rice/ksb/config/remoting-servlet.xml";
70  
71  	private KSBHttpInvokerHandler httpInvokerHandler;
72  	private ServletController cxfServletController;
73  
74      @Override
75  	protected void initFrameworkServlet() throws ServletException, BeansException {
76  		this.httpInvokerHandler = new KSBHttpInvokerHandler();
77  		
78          Bus bus = KSBServiceLocator.getCXFBus();
79  
80          List<Interceptor<? extends Message>> inInterceptors = KSBServiceLocator.getInInterceptors();
81          if(inInterceptors != null) {
82          	List<Interceptor<? extends Message>> busInInterceptors = bus.getInInterceptors();
83          	busInInterceptors.addAll(inInterceptors);
84          }
85         
86          List<Interceptor<? extends Message>> outInterceptors = KSBServiceLocator.getOutInterceptors();
87          if(outInterceptors != null) {
88          	List<Interceptor<? extends Message>> busOutInterceptors = bus.getOutInterceptors();
89          	busOutInterceptors.addAll(outInterceptors);
90          }
91  
92  
93  		HTTPTransportFactory transportFactory = bus.getExtension(HTTPTransportFactory.class);
94          if (transportFactory == null) {
95              throw new IllegalStateException("Failed to locate HTTPTransportFactory extension on Apache CXF Bus");
96          }
97  
98          DestinationRegistry destinationRegistry = transportFactory.getRegistry();
99  
100 
101 		this.cxfServletController = new ServletController(destinationRegistry, getCXFServletConfig(
102                 this.getServletConfig()), new ServiceListGeneratorServlet(destinationRegistry, bus));
103 
104 		this.setPublishEvents(false);
105 	}
106 
107     /**
108      * This is a workaround after upgrading to CXF 2.7.0 whereby we could no longer just call "setHideServiceList" on
109      * the ServletController. Instead, it is now reading this information from the ServletConfig, so wrapping the base
110      * ServletContext to return true or false for hide service list depending on whether or not we are in dev mode.
111      */
112     protected ServletConfig getCXFServletConfig(final ServletConfig baseServletConfig) {
113         // disable handling of URLs ending in /services which display CXF generated service lists if we are not in dev mode
114         final String shouldHide = Boolean.toString(!ConfigContext.getCurrentContextConfig().getDevMode().booleanValue());
115         return new ServletConfig() {
116             private static final String HIDE_SERVICE_LIST_PAGE_PARAM = "hide-service-list-page";
117             @Override
118             public String getServletName() {
119                 return baseServletConfig.getServletName();
120             }
121             @Override
122             public ServletContext getServletContext() {
123                 return baseServletConfig.getServletContext();
124             }
125             @Override
126             public String getInitParameter(String parameter) {
127                 if (HIDE_SERVICE_LIST_PAGE_PARAM.equals(parameter)) {
128                     return shouldHide;
129                 }
130                 return baseServletConfig.getInitParameter(parameter);
131             }
132             @Override
133             public Enumeration<String> getInitParameterNames() {
134                 List<String> initParameterNames = EnumerationUtils.toList(baseServletConfig.getInitParameterNames());
135                 initParameterNames.add(HIDE_SERVICE_LIST_PAGE_PARAM);
136                 return new Vector<String>(initParameterNames).elements();
137             }
138         };
139     }
140 
141     @Override
142 	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
143 		if (handler instanceof HttpRequestHandler) {
144 			return new HttpRequestHandlerAdapter();
145 		} else if (handler instanceof Controller) {
146 			Object unwrappedHandler = ClassLoaderUtils.unwrapFromProxy(handler);
147 			if (unwrappedHandler instanceof CXFServletControllerAdapter) {
148 				// TODO this just seems weird as this controller is initially null when it's created, does there need to be some synchronization here?
149 				((CXFServletControllerAdapter)unwrappedHandler).setController(cxfServletController);
150 			}			
151 			return new SimpleControllerHandlerAdapter();
152 		}
153 		throw new RiceRuntimeException("handler of type " + handler.getClass().getName() + " is not known and can't be used by " + KSBDispatcherServlet.class.getName());
154 	}
155 
156     @Override
157 	protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
158 		return this.httpInvokerHandler.getHandler(request);
159 	}
160 
161 	@Override
162 	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
163 		try {
164 			QName serviceName = this.httpInvokerHandler.getServiceNameFromRequest(request);
165 			LOG.info("Caught Exception from service " + serviceName, ex);
166 		} catch (Throwable throwable) {
167 			LOG.warn("Caught exception attempting to log exception thrown from remotely accessed service", throwable);
168 		}
169 		return null;
170 	}
171 
172 	/**
173 	 * Overrides the service method to replace the request and responses with one which will provide input and output streams for
174 	 * verifying and signing the data.
175 	 */
176 	@Override
177 	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
178 		if (isSecure(request)) {
179 			super.service(new SignatureVerifyingRequestWrapper(request), new SignatureSigningResponseWrapper(response));
180 		} else {
181 			super.service(request, response);
182 		}
183 	}
184 
185     @Override
186     protected WebApplicationContext initWebApplicationContext() {
187         setContextConfigLocation(REMOTING_SERVLET_CONFIG_LOCATION);
188         return super.initWebApplicationContext();
189     }
190 
191 	protected boolean isSecure(HttpServletRequest request) {
192 		QName serviceName = this.httpInvokerHandler.getServiceNameFromRequest(request);
193 		if (LOG.isDebugEnabled()) {
194 		    LOG.debug("Checking service " + serviceName + " for security enabled");
195 		}
196 		Endpoint endpoint = KsbApiServiceLocator.getServiceBus().getEndpoint(serviceName);
197 		if (endpoint == null) {
198 			LOG.error("Attempting to acquire non-existent service " + request.getRequestURI());
199 		    throw new RiceRuntimeException("Attempting to acquire non-existent service.");
200 		}
201 		ServiceConfiguration serviceConfiguration = endpoint.getServiceConfiguration();
202 		if (serviceConfiguration instanceof SoapServiceConfiguration) {
203 		    return false;
204 		}
205 		return serviceConfiguration.getBusSecurity();
206 	}
207 
208 }