org.codice.proxy.http.HttpProxyCamelHttpTransportServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.proxy.http.HttpProxyCamelHttpTransportServlet.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p>
 * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or any later version.
 * <p>
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License
 * is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 * <p>
 * <p>
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.
 * <p>
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.
 */

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache 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.apache.org/licenses/LICENSE-2.0
 *
 * 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.codice.proxy.http;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.Route;
import org.apache.camel.component.servlet.DefaultHttpRegistry;
import org.apache.camel.component.servlet.HttpRegistry;
import org.apache.camel.component.servlet.ServletEndpoint;
import org.apache.camel.converter.ObjectConverter;
import org.apache.camel.http.common.CamelServlet;
import org.apache.camel.http.common.HttpConsumer;
import org.apache.camel.http.common.HttpHelper;
import org.apache.camel.http.common.HttpMessage;
import org.apache.camel.impl.DefaultExchange;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Camel HTTP servlet which can be used in Camel routes to route servlet invocations in routes.
 */
public class HttpProxyCamelHttpTransportServlet extends CamelServlet implements Externalizable {
    private static final long serialVersionUID = -1797014782158930490L;

    private static final Logger LOG = LoggerFactory.getLogger(HttpProxyCamelHttpTransportServlet.class);

    private transient CamelContext camelContext;

    private transient HttpRegistry httpRegistry;

    private boolean ignoreDuplicateServletName;

    private Map<String, HttpConsumer> consumers = new ConcurrentHashMap<String, HttpConsumer>();

    public HttpProxyCamelHttpTransportServlet(CamelContext camelContext) {
        this.camelContext = camelContext;
    }

    public HttpProxyCamelHttpTransportServlet() {

    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        String ignore = config.getInitParameter("ignoreDuplicateServletName");
        Boolean bool = ObjectConverter.toBoolean(ignore);
        if (bool != null) {
            ignoreDuplicateServletName = bool;
        } else {
            // always log so people can see it easier
            String msg = "Invalid parameter value for init-parameter ignoreDuplicateServletName with value: "
                    + ignore;
            LOG.error(msg);
            throw new ServletException(msg);
        }

        String name = config.getServletName();
        String contextPath = config.getServletContext().getContextPath();

        if (httpRegistry == null) {
            httpRegistry = DefaultHttpRegistry.getHttpRegistry(name);
            CamelServlet existing = httpRegistry.getCamelServlet(name);
            if (existing != null) {
                String msg = "Duplicate ServetName detected: " + name + ". Existing: " + existing + " This: "
                        + this.toString() + ". Its advised to use unique ServletName per Camel application.";
                // always log so people can see it easier
                if (isIgnoreDuplicateServletName()) {
                    LOG.warn(msg);
                } else {
                    LOG.error(msg);
                    throw new ServletException(msg);
                }
            }
            httpRegistry.register(this);
        }

        LOG.info("Initialized CamelHttpTransportServlet[name={}, contextPath={}]", getServletName(), contextPath);
    }

    @Override
    public void destroy() {
        DefaultHttpRegistry.removeHttpRegistry(getServletName());
        if (httpRegistry != null) {
            httpRegistry.unregister(this);
            httpRegistry = null;
        }
        LOG.info("Destroyed CamelHttpTransportServlet[{}]", getServletName());
    }

    @Override
    protected void service(HttpServletRequest oldRequest, HttpServletResponse response)
            throws ServletException, IOException {

        //Wrap request and clean the query String
        HttpProxyWrappedCleanRequest request = new HttpProxyWrappedCleanRequest(oldRequest);

        log.trace("Service: {}", request);

        // Is there a consumer registered for the request.
        HttpConsumer consumer = resolve(request);

        if (consumer == null) {
            String path = request.getPathInfo();
            log.trace("Service Request Path = {}", path);
            String endpointName = getEndpointNameFromPath(path);
            log.trace("Endpoint Name = {}", endpointName);

            Route route = camelContext.getRoute(endpointName);
            try {
                if (route != null) {
                    connect((HttpConsumer) route.getConsumer());
                }
            } catch (Exception e) {
                log.debug("Exception while creating consumer", e);
            }
            consumer = resolve(request);
        }

        if (consumer == null) {
            log.debug("No consumer to service request {}", request);
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // are we suspended?
        if (consumer.getEndpoint().isSuspended()) {
            log.debug("Consumer suspended, cannot service request {}", request);
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
            return;
        }

        if (consumer.getEndpoint().getHttpMethodRestrict() != null
                && !consumer.getEndpoint().getHttpMethodRestrict().equals(request.getMethod())) {
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            return;
        }

        if ("TRACE".equals(request.getMethod()) && !consumer.getEndpoint().isTraceEnabled()) {
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            return;
        }

        // create exchange and set data on it
        Exchange exchange = new DefaultExchange(consumer.getEndpoint(), ExchangePattern.InOut);

        if (consumer.getEndpoint().isBridgeEndpoint()) {
            exchange.setProperty(Exchange.SKIP_GZIP_ENCODING, Boolean.TRUE);
        }
        if (consumer.getEndpoint().isDisableStreamCache()) {
            exchange.setProperty(Exchange.DISABLE_HTTP_STREAM_CACHE, Boolean.TRUE);
        }

        // we override the classloader before building the HttpMessage just in case the binding
        // does some class resolution
        ClassLoader oldTccl = overrideTccl(exchange);
        HttpHelper.setCharsetFromContentType(request.getContentType(), exchange);
        exchange.setIn(new HttpMessage(exchange, request, response));
        // set context path as header
        String contextPath = consumer.getEndpoint().getPath();
        exchange.getIn().setHeader("CamelServletContextPath", contextPath);

        String httpPath = (String) exchange.getIn().getHeader(Exchange.HTTP_PATH);
        // here we just remove the CamelServletContextPath part from the HTTP_PATH
        if (contextPath != null && httpPath.startsWith(contextPath)) {
            exchange.getIn().setHeader(Exchange.HTTP_PATH, httpPath.substring(contextPath.length()));
        }

        // we want to handle the UoW
        try {
            consumer.createUoW(exchange);
        } catch (Exception e) {
            log.error("Error processing request", e);
            throw new ServletException(e);
        }

        try {
            if (log.isTraceEnabled()) {
                log.trace("Processing request for exchangeId: {}", exchange.getExchangeId());
            }
            // process the exchange
            consumer.getProcessor().process(exchange);
        } catch (Exception e) {
            exchange.setException(e);
        }

        try {
            // now lets output to the response
            if (log.isTraceEnabled()) {
                log.trace("Writing response for exchangeId: {}", exchange.getExchangeId());
            }
            Integer bs = consumer.getEndpoint().getResponseBufferSize();
            if (bs != null) {
                log.trace("Using response buffer size: {}", bs);
                response.setBufferSize(bs);
            }
            consumer.getBinding().writeResponse(exchange, response);
        } catch (IOException e) {
            log.error("Error processing request", e);
            throw e;
        } catch (Exception e) {
            log.error("Error processing request", e);
            throw new ServletException(e);
        } finally {
            consumer.doneUoW(exchange);
            restoreTccl(exchange, oldTccl);
        }
    }

    private ServletEndpoint getServletEndpoint(HttpConsumer consumer) {
        if (!(consumer.getEndpoint() instanceof ServletEndpoint)) {
            throw new RuntimeException(
                    "Invalid consumer type. Must be ServletEndpoint but is " + consumer.getClass().getName());
        }
        return (ServletEndpoint) consumer.getEndpoint();
    }

    protected HttpConsumer resolve(HttpServletRequest request) {
        String path = request.getPathInfo();
        log.trace("Request path is: {}", path);
        String endpointName = getEndpointNameFromPath(path);
        log.trace("Looking up consumer for endpoint: {}", endpointName);
        HttpConsumer answer = consumers.get(endpointName);

        if (answer == null) {
            log.debug("Consumer Keys: {}", Arrays.toString(consumers.keySet().toArray()));
            for (Map.Entry<String, HttpConsumer> entry : consumers.entrySet()) {
                if ((entry.getValue()).getEndpoint().isMatchOnUriPrefix() && path.startsWith(entry.getKey())) {
                    answer = consumers.get(entry.getKey());
                    break;
                }
            }
        }
        return answer;
    }

    @Override
    public void connect(HttpConsumer consumer) {
        log.debug("Getting ServletEndpoint for consumer: {}", consumer);
        ServletEndpoint endpoint = getServletEndpoint(consumer);
        if (endpoint.getServletName() != null) {
            String endpointName = getEndpointNameFromPath(consumer.getPath());
            log.debug("Adding consumer for endpointName: {}", endpointName);
            consumers.put(endpointName, consumer);
        }
    }

    @Override
    public void disconnect(HttpConsumer consumer) {
        String path = consumer.getPath();
        String endpointName = getEndpointNameFromPath(path);
        log.debug("Disconnecting consumer: {}", endpointName);
        consumers.remove(endpointName);
    }

    public boolean isIgnoreDuplicateServletName() {
        return ignoreDuplicateServletName;
    }

    @Override
    public String toString() {
        String name = getServletName();
        if (name != null) {
            return "CamelHttpTransportServlet[name=" + getServletName() + "]";
        } else {
            return "CamelHttpTransportServlet";
        }
    }

    private String getEndpointNameFromPath(String path) {
        // path is like: "/example1/something/0/thing.html"
        // or is like: "/example1"
        // endpointName is: "example1"
        String endpointName = (StringUtils.indexOf(path, "/", 1) != StringUtils.INDEX_NOT_FOUND)
                ? StringUtils.substring(path, 0, StringUtils.indexOf(path, "/", 1))
                : path;
        endpointName = StringUtils.remove(endpointName, "/");
        return endpointName;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {

    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

    }
}