/*******************************************************************************
 * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
 * This program and the accompanying materials are made available under the 
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
 * which accompanies this distribution. 
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *      Martin Matula - Oracle
 ******************************************************************************/

package org.eclipse.persistence.jpa.rs.jersey;

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.eclipse.persistence.jpa.rs.logging.LoggingLocalization;

/**
 * Jersey Servlet/Filter class that can be referenced in web.xml instead of Jersey 1.x specific
 * {@link com.sun.jersey.spi.container.servlet.ServletContainer} and Jersey 2.x specific
 * {@link org.glassfish.jersey.servlet.ServletContainer} to enable web application portability between
 * Jersey 1.x and Jersey 2.x servlet containers.
 * <p>
 *     Since for some of the {@link org.glassfish.jersey.servlet.ServletProperties servlet init parameters} that can be
 *     specified in web.xml you may want different values depending on which version of Jersey container is present,
 *     You can prefix the init parameter name either with {@code jersey1#} or {@code jersey2#} to
 *     make it specific to a given version. For example, to specify different values for
 *     {@code javax.ws.rs.Application} init parameter depending on the version of Jersey used, you can include
 *     the following in your web.xml:
 *     <pre>
 *     &lt;servlet&gt;
 *         &lt;servlet-name&gt;Jersey Web Application&lt;/servlet-name&gt;
 *         &lt;servlet-class&gt;org.glassfish.jersey.servlet.portability.PortableServletContainer&lt;/servlet-class&gt;
 *         &lt;init-param&gt;
 *             &lt;param-name&gt;jersey1#javax.ws.rs.Application&lt;/param-name&gt;
 *             &lt;param-value&gt;myapp.jersey1specific.Jersey1Application&lt;/param-value&gt;
 *         &lt;/init-param&gt;
 *         &lt;init-param&gt;
 *             &lt;param-name&gt;jersey2#javax.ws.rs.Application&lt;/param-name&gt;
 *             &lt;param-value&gt;myapp.jersey2specific.Jersey2Application&lt;/param-value&gt;
 *         &lt;/init-param&gt;
 *     &lt;/servlet&gt;
 *     </pre>
 * </p>
 *
 * @author Martin Matula (martin.matula at oracle.com)
 */
public class PortableServletContainer implements Filter, Servlet {
    private static final String JERSEY_1_PREFIX = "jersey1#";
    private static final String JERSEY_2_PREFIX = "jersey2#";

    private final Servlet wrappedServlet;
    private final Filter wrappedFilter;
    private final String includePrefix;
    private final String excludePrefix;

    /**
     * Create a new servlet container.
     */
    public PortableServletContainer() {
        Class<Servlet> servletClass;
        boolean isJersey1 = false;
        try {
            servletClass = (Class<Servlet>) Class.forName("com.sun.jersey.spi.container.servlet.ServletContainer");
            isJersey1 = true;
        } catch (ClassNotFoundException e) {
            // Jersey 1.x not present, try Jersey 2.x
            try {
                servletClass = (Class<Servlet>) Class.forName("org.glassfish.jersey.servlet.ServletContainer");
            } catch (ClassNotFoundException ex) {
                throw new IllegalStateException(LoggingLocalization.buildMessage("jersey.not.available", new Object[]{}));
            }
        }
        
        try {
            wrappedServlet = servletClass.newInstance();
            wrappedFilter = (Filter) wrappedServlet;
        } catch (Exception e) {
            throw new RuntimeException(LoggingLocalization.buildMessage("jersey.container.cant.load", new Object[]{}), e);
        }
        includePrefix = isJersey1 ? JERSEY_1_PREFIX : JERSEY_2_PREFIX;
        excludePrefix = isJersey1 ? JERSEY_2_PREFIX : JERSEY_1_PREFIX;
    }

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
        wrappedFilter.init(new FilterConfigWrapper(filterConfig));
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        wrappedFilter.doFilter(request, response, chain);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        wrappedServlet.init(new ServletConfigWrapper(config));
    }

    @Override
    public ServletConfig getServletConfig() {
        return wrappedServlet.getServletConfig();
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        wrappedServlet.service(req, res);
    }

    @Override
    public String getServletInfo() {
        return wrappedServlet.getServletInfo();
    }

    @Override
    public void destroy() {
        wrappedServlet.destroy();
    }

    private abstract class InitParamsWrapper {
        private final HashMap<String, String> filteredInitParams = new HashMap<String, String>();

        void init() {
            for (Enumeration e = getInitParamNames(); e.hasMoreElements();) {
                String name = (String) e.nextElement();
                String value = getInitParamValue(name);
                if (name.startsWith(includePrefix)) {
                    name = name.substring(includePrefix.length());
                } else if (name.startsWith(excludePrefix)) {
                    continue;
                }
                filteredInitParams.put(name, value);
            }
        }

        abstract String getInitParamValue(String name);
        abstract Enumeration getInitParamNames();

        public String getInitParameter(String name) {
            return filteredInitParams.get(name);
        }

        public Enumeration getInitParameterNames() {
            return Collections.enumeration(filteredInitParams.keySet());
        }
    }

    private class FilterConfigWrapper extends InitParamsWrapper implements FilterConfig {
        private final FilterConfig wrapped;

        FilterConfigWrapper(FilterConfig wrapped) {
            this.wrapped = wrapped;
            init();
        }

        @Override
        public String getFilterName() {
            return wrapped.getFilterName();
        }

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

        @Override
        String getInitParamValue(String name) {
            return wrapped.getInitParameter(name);
        }

        @Override
        Enumeration getInitParamNames() {
            return wrapped.getInitParameterNames();
        }
    }

    private class ServletConfigWrapper extends InitParamsWrapper implements ServletConfig {
        private final ServletConfig wrapped;

        ServletConfigWrapper(ServletConfig wrapped) {
            this.wrapped = wrapped;
            init();
        }

        @Override
        String getInitParamValue(String name) {
            return wrapped.getInitParameter(name);
        }

        @Override
        Enumeration getInitParamNames() {
            return wrapped.getInitParameterNames();
        }

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

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


@@ -1364,23 +1364,16 @@
 ific%0D%0A *
- %7B@link
  com.sun
@@ -1418,17 +1418,16 @@
 ontainer
-%7D
  and Jer
@@ -1446,23 +1446,16 @@
 ific%0D%0A *
- %7B@link
  org.gla
@@ -1492,17 +1492,16 @@
 ontainer
-%7D
  to enab

Remove link to javadoc that will not be available in EclipseLInk