001/**
002 * Copyright 2005-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.test.remote;
017
018import org.apache.logging.log4j.Logger;
019import org.apache.logging.log4j.LogManager;
020import org.apache.cxf.endpoint.Client;
021import org.apache.cxf.frontend.ClientProxy;
022import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
023import org.kuali.rice.ksb.impl.cxf.interceptors.ImmutableCollectionsInInterceptor;
024
025import javax.jws.WebService;
026import javax.xml.ws.Endpoint;
027
028/**
029 * Harness used to hold a reference to an endpoint that is published to support remote tests.  Tests using
030 * this harness should pass in a @WebService annotated interface class and an object of an implementing class
031 * of that interface to the publishEndpointAndReturnProxy method in @Before or setUp methods used in tests.
032 * <p/>
033 * The endpoint will always be published at a URL like http://localhost:1025/service where the port number changes
034 * each time publishEndpointAndReturnProxy is called and guarantees that an open port is used.
035 * <p/
036 * After each test is run, stopEndPoint should be called in @After or tearDown methods in order to unpublish the
037 * endpoint.
038 * <p/>
039 *
040 */
041public class RemoteTestHarness {
042
043    private static final Logger LOG = LogManager.getLogger(RemoteTestHarness.class);
044
045    private static String ENDPOINT_ROOT = "http://localhost"; //Default URL
046    private static String ENDPOINT_PATH = "/service";
047
048    private Endpoint endpoint;
049
050    @SuppressWarnings("unchecked")
051    /**
052     * Creates a published endpoint from the passed in serviceImplementation and also returns a proxy implementation
053     * of the passed in interface for clients to use to hit the created endpoint.
054     */
055    public <T> T publishEndpointAndReturnProxy(Class<T> jaxWsAnnotatedInterface, T serviceImplementation) {
056        if (jaxWsAnnotatedInterface.isInterface() &&
057                jaxWsAnnotatedInterface.getAnnotation(WebService.class) != null &&
058                jaxWsAnnotatedInterface.isInstance(serviceImplementation)) {
059
060            String endpointUrl = getAvailableEndpointUrl();
061            LOG.info("Publishing service to: " + endpointUrl);
062            endpoint = Endpoint.publish(endpointUrl, serviceImplementation);
063
064            JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
065            factory.setServiceClass(jaxWsAnnotatedInterface);
066            factory.setAddress(endpointUrl);
067
068            T serviceProxy = (T) factory.create();
069
070            /* Add the ImmutableCollectionsInInterceptor to mimic interceptors added in the KSB */
071            Client cxfClient = ClientProxy.getClient(serviceProxy);
072            cxfClient.getInInterceptors().add(new ImmutableCollectionsInInterceptor());
073
074            return serviceProxy;
075        } else {
076            throw new IllegalArgumentException("Passed in interface class type must be annotated with @WebService " +
077                    "and object reference must be an implementing class of that interface.");
078
079        }
080    }
081
082    /**
083     * Stops and makes an internal endpoint unpublished if it was previously published.
084     * Otherwise, this method is a no-op.
085     */
086    public void stopEndpoint() {
087        if (endpoint != null) {
088            endpoint.stop();
089        }
090    }
091
092    private String getAvailableEndpointUrl() {
093        String port = Integer.toString(AvailablePortFinder.getNextAvailable());
094        return ENDPOINT_ROOT + ":" + port + ENDPOINT_PATH;
095    }
096}