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 }