001    /**
002     * Copyright 2005-2011 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     */
016    package org.kuali.rice.test.remote;
017    
018    import org.apache.commons.logging.Log;
019    import org.apache.commons.logging.LogFactory;
020    import org.apache.cxf.endpoint.Client;
021    import org.apache.cxf.frontend.ClientProxy;
022    import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
023    import org.kuali.rice.ksb.impl.cxf.interceptors.ImmutableCollectionsInInterceptor;
024    
025    import javax.jws.WebService;
026    import 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     */
041    public class RemoteTestHarness {
042    
043        private static final Log LOG = LogFactory.getLog(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    }