org.kuali.rice.ksb.messaging.servlet.KSBDispatcherServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.rice.ksb.messaging.servlet.KSBDispatcherServlet.java

Source

/**
 * Copyright 2005-2014 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.rice.ksb.messaging.servlet;

import org.apache.commons.collections.EnumerationUtils;
import org.apache.cxf.Bus;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.message.Message;
import org.apache.cxf.transport.http.DestinationRegistry;
import org.apache.cxf.transport.http.HTTPTransportFactory;
import org.apache.cxf.transport.servlet.ServletController;
import org.apache.cxf.transport.servlet.servicelist.ServiceListGeneratorServlet;
import org.apache.log4j.Logger;
import org.kuali.rice.core.api.config.property.ConfigContext;
import org.kuali.rice.core.api.exception.RiceRuntimeException;
import org.kuali.rice.core.api.util.ClassLoaderUtils;
import org.kuali.rice.ksb.api.KsbApiServiceLocator;
import org.kuali.rice.ksb.api.bus.Endpoint;
import org.kuali.rice.ksb.api.bus.ServiceConfiguration;
import org.kuali.rice.ksb.api.bus.support.SoapServiceConfiguration;
import org.kuali.rice.ksb.security.SignatureSigningResponseWrapper;
import org.kuali.rice.ksb.security.SignatureVerifyingRequestWrapper;
import org.kuali.rice.ksb.service.KSBServiceLocator;
import org.springframework.beans.BeansException;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;

/**
 * A {@link DispatcherServlet} which dispatches incoming requests to the appropriate
 * service endpoint.
 *
 * @author Kuali Rice Team (rice.collab@kuali.org)
 */
public class KSBDispatcherServlet extends DispatcherServlet {

    private static final Logger LOG = Logger.getLogger(KSBDispatcherServlet.class);
    private static final long serialVersionUID = 6790121225857950019L;
    private static final String REMOTING_SERVLET_CONFIG_LOCATION = "classpath:org/kuali/rice/ksb/config/remoting-servlet.xml";

    private KSBHttpInvokerHandler httpInvokerHandler;
    private ServletController cxfServletController;

    @Override
    protected void initFrameworkServlet() throws ServletException, BeansException {
        this.httpInvokerHandler = new KSBHttpInvokerHandler();

        Bus bus = KSBServiceLocator.getCXFBus();

        List<Interceptor<? extends Message>> inInterceptors = KSBServiceLocator.getInInterceptors();
        if (inInterceptors != null) {
            List<Interceptor<? extends Message>> busInInterceptors = bus.getInInterceptors();
            busInInterceptors.addAll(inInterceptors);
        }

        List<Interceptor<? extends Message>> outInterceptors = KSBServiceLocator.getOutInterceptors();
        if (outInterceptors != null) {
            List<Interceptor<? extends Message>> busOutInterceptors = bus.getOutInterceptors();
            busOutInterceptors.addAll(outInterceptors);
        }

        HTTPTransportFactory transportFactory = bus.getExtension(HTTPTransportFactory.class);
        if (transportFactory == null) {
            throw new IllegalStateException("Failed to locate HTTPTransportFactory extension on Apache CXF Bus");
        }

        DestinationRegistry destinationRegistry = transportFactory.getRegistry();

        this.cxfServletController = new ServletController(destinationRegistry,
                getCXFServletConfig(this.getServletConfig()),
                new ServiceListGeneratorServlet(destinationRegistry, bus));

        this.setPublishEvents(false);
    }

    /**
     * This is a workaround after upgrading to CXF 2.7.0 whereby we could no longer just call "setHideServiceList" on
     * the ServletController. Instead, it is now reading this information from the ServletConfig, so wrapping the base
     * ServletContext to return true or false for hide service list depending on whether or not we are in dev mode.
     */
    protected ServletConfig getCXFServletConfig(final ServletConfig baseServletConfig) {
        // disable handling of URLs ending in /services which display CXF generated service lists if we are not in dev mode
        final String shouldHide = Boolean
                .toString(!ConfigContext.getCurrentContextConfig().getDevMode().booleanValue());
        return new ServletConfig() {
            private static final String HIDE_SERVICE_LIST_PAGE_PARAM = "hide-service-list-page";

            @Override
            public String getServletName() {
                return baseServletConfig.getServletName();
            }

            @Override
            public ServletContext getServletContext() {
                return baseServletConfig.getServletContext();
            }

            @Override
            public String getInitParameter(String parameter) {
                if (HIDE_SERVICE_LIST_PAGE_PARAM.equals(parameter)) {
                    return shouldHide;
                }
                return baseServletConfig.getInitParameter(parameter);
            }

            @Override
            public Enumeration<String> getInitParameterNames() {
                List<String> initParameterNames = EnumerationUtils
                        .toList(baseServletConfig.getInitParameterNames());
                initParameterNames.add(HIDE_SERVICE_LIST_PAGE_PARAM);
                return new Vector<String>(initParameterNames).elements();
            }
        };
    }

    @Override
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (handler instanceof HttpRequestHandler) {
            return new HttpRequestHandlerAdapter();
        } else if (handler instanceof Controller) {
            Object unwrappedHandler = ClassLoaderUtils.unwrapFromProxy(handler);
            if (unwrappedHandler instanceof CXFServletControllerAdapter) {
                // TODO this just seems weird as this controller is initially null when it's created, does there need to be some synchronization here?
                ((CXFServletControllerAdapter) unwrappedHandler).setController(cxfServletController);
            }
            return new SimpleControllerHandlerAdapter();
        }
        throw new RiceRuntimeException("handler of type " + handler.getClass().getName()
                + " is not known and can't be used by " + KSBDispatcherServlet.class.getName());
    }

    @Override
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        return this.httpInvokerHandler.getHandler(request);
    }

    @Override
    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex) throws Exception {
        try {
            QName serviceName = this.httpInvokerHandler.getServiceNameFromRequest(request);
            LOG.info("Caught Exception from service " + serviceName, ex);
        } catch (Throwable throwable) {
            LOG.warn("Caught exception attempting to log exception thrown from remotely accessed service",
                    throwable);
        }
        return null;
    }

    /**
     * Overrides the service method to replace the request and responses with one which will provide input and output streams for
     * verifying and signing the data.
     */
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        if (isSecure(request)) {
            super.service(new SignatureVerifyingRequestWrapper(request),
                    new SignatureSigningResponseWrapper(response));
        } else {
            super.service(request, response);
        }
    }

    @Override
    protected WebApplicationContext initWebApplicationContext() {
        setContextConfigLocation(REMOTING_SERVLET_CONFIG_LOCATION);
        return super.initWebApplicationContext();
    }

    protected boolean isSecure(HttpServletRequest request) {
        QName serviceName = this.httpInvokerHandler.getServiceNameFromRequest(request);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Checking service " + serviceName + " for security enabled");
        }
        Endpoint endpoint = KsbApiServiceLocator.getServiceBus().getEndpoint(serviceName);
        if (endpoint == null) {
            LOG.error("Attempting to acquire non-existent service " + request.getRequestURI());
            throw new RiceRuntimeException("Attempting to acquire non-existent service.");
        }
        ServiceConfiguration serviceConfiguration = endpoint.getServiceConfiguration();
        if (serviceConfiguration instanceof SoapServiceConfiguration) {
            return false;
        }
        return serviceConfiguration.getBusSecurity();
    }

}