/****************************************************************************
* Copyright (C) 2012 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.ws.marshal;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.annotation.Nonnull;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;
/**
* Supply classloader responsible for all ws classes.
* This functionality is needed to dynamically load and instantiate the ws clients.
*
* @author Tobias Wich <tobias.wich@ecsec.de>
*/
public abstract class WSClassLoader {
private static final ClassLoader inst = WSClassLoader.class.getClassLoader();
protected static final Map<String,String> serviceClasses;
protected static final Map<String,String> servicePorts;
static {
// load service maps
serviceClasses = new HashMap<String, String>();
servicePorts = new HashMap<String, String>();
serviceClasses.put("SAL", "org.openecard.ws.SAL_Service");
servicePorts .put("SAL", "SALPort");
serviceClasses.put("Publish", "org.openecard.ws.Publish_Service");
servicePorts .put("Publish", "Publish");
serviceClasses.put("Subscribe", "org.openecard.ws.Subscribe_Service");
servicePorts .put("Subscribe", "Subscribe");
serviceClasses.put("ISO24727-Protocols", "org.openecard.ws.ISO24727Protocols_Service");
servicePorts .put("ISO24727-Protocols", "ISO24727ProtocolsPort");
serviceClasses.put("IFD", "org.openecard.ws.IFD_Service");
servicePorts .put("IFD", "IFDPort");
serviceClasses.put("IFDCallback", "org.openecard.ws.IFDCallback_Service");
servicePorts .put("IFDCallback", "IFDCallbackPort");
serviceClasses.put("GetCardInfoOrACD", "org.openecard.ws.GetCardInfoOrACD_Service");
servicePorts .put("GetCardInfoOrACD", "GetCardInfoOrACD");
serviceClasses.put("GetRecognitionTree", "org.openecard.ws.GetRecognitionTree_Service");
servicePorts .put("GetRecognitionTree", "GetRecognitionTree");
serviceClasses.put("Management", "org.openecard.ws.Management_Service");
servicePorts .put("Management", "ManagementPort");
serviceClasses.put("UpdateService", "org.openecard.ws.UpdateService_Service");
servicePorts .put("UpdateService", "UpdateServicePort");
}
// the loadClass function can not be made typesafe, so ignore this warning and live with the error if it happens
@SuppressWarnings("unchecked")
private static Class<Service> loadClass(String serviceName) throws ClassNotFoundException {
String className = serviceClasses.get(serviceName);
ClassLoader cl = WSClassLoader.class.getClassLoader();
return (Class<Service>) cl.loadClass(className);
}
private static WebServiceClient getClientAnnotation(Class<Service> clazz) {
return clazz.getAnnotation(WebServiceClient.class);
}
private static URL getWSDL() {
URL url = WSClassLoader.class.getResource("/ALL.wsdl");
if (url == null) {
url = WSClassLoader.class.getResource("ALL.wsdl");
}
return url;
}
/**
* Gets a set of all available services implementation identifiers.
* The identifiers are freely chosen by the developer and thus are not related to the services QName. The
* identifiers returned by this function may be used in the {@link #getClientService()} functions.
*
* @return Set with all supported services.
*/
@Nonnull
public static Set<String> getSupportedServices() {
TreeSet<String> s = new TreeSet<String>(serviceClasses.keySet());
Iterator<String> it = s.iterator();
while (it.hasNext()) {
String nextService = it.next();
try {
Class c = loadClass(nextService);
} catch (ClassNotFoundException ex) {
// no such class, remove this service
it.remove();
}
}
return s;
}
/**
* Gets a client service instance of the given name.
* The name must be one of the names returned by {@link #getSupportedServices()}. The use of this method is
* strongly encouraged, as it uses the WSDLs and XML schemas bundled with this class. This leads to lower load
* times.<br/>
* The service client is initialized with the values from the WSDL. In order to set other endpoints, use the other
* {@code getClientService} functions.
*
* @param serviceName Name of the service. Must be one of the names returned by {@link #getSupportedServices()}.
* @return Webservice client initialized with the settings from the WSDL.
* @throws NoSuchMethodException In case there is no suitable constructor or {@code getPort()} method in the service
* class.
* @throws InstantiationException In case the constructor call failed.
* @throws IllegalAccessException In case the constructor call or the {@code getPort()} method failed.
* @throws InvocationTargetException In case the constructor call or the {@code getPort()} method failed.
* @throws ClassNotFoundException In case no such service is registered in this loader.
* @see #getClientService(java.lang.String, java.lang.String)
* @see #getClientService(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public static Object getClientService(@Nonnull String serviceName)
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
ClassNotFoundException {
// the relevant information to create a service is extracted from the service instance
// get serviceloader instance
Class<Service> loaderClass = loadClass(serviceName);
WebServiceClient clientAnnotation = getClientAnnotation(loaderClass);
Constructor<Service> constructor = loaderClass.getConstructor(URL.class, QName.class);
QName serviceQname = new QName(clientAnnotation.targetNamespace(), clientAnnotation.name());
Service serviceLoaderInst = constructor.newInstance(getWSDL(), serviceQname);
//Constructor constructor = loaderClass.getConstructor();
//Service serviceLoaderInst = (Service) constructor.newInstance();
// get portmethod and call it to get actual service
String portName = servicePorts.get(serviceName);
Method portMethod = serviceLoaderInst.getClass().getMethod("get" + portName, WebServiceFeature[].class);
//WebServiceFeature validator = (WebServiceFeature) inst.loadClass("com.sun.xml.internal.ws.developer.SchemaValidationFeature").getConstructor().newInstance();
//Object serviceInst = portMethod.invoke(serviceLoaderInst, new Object[]{ new WebServiceFeature[] {validator} });
Object serviceInst = portMethod.invoke(serviceLoaderInst, new Object[]{ new WebServiceFeature[] {} });
return serviceInst;
}
/**
* Gets a client service instance of the given name and set the given endpoint address.
* A detailed explanation can be found in {@link #getClientService(java.lang.String).
*
* @param serviceName Name of the service. Must be one of the names returned by {@link #getSupportedServices()}.
* @param schema Schema part of the URL (e.g. {@code https}).
* @param host Host part of the URL.
* @param port Port part of the URL.
* @param resource Resource part of the URL (e.g. {@code /} or {@code /service/endpoint}).
* @return Webservice client initialized with the settings from the WSDL and the endpoint set to the given value.
* @throws NoSuchMethodException In case there is no suitable constructor or {@code getPort()} method in the service
* class.
* @throws InstantiationException In case the constructor call failed.
* @throws IllegalAccessException In case the constructor call or the {@code getPort()} method failed.
* @throws InvocationTargetException In case the constructor call or the {@code getPort()} method failed.
* @throws ClassNotFoundException In case no such service is registered in this loader.
*/
public static Object getClientService(@Nonnull String serviceName, @Nonnull String schema, @Nonnull String host,
@Nonnull String port, @Nonnull String resource)
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException,
ClassNotFoundException {
String address = schema + "://" + host + ":" + port + resource;
Object serviceInst = getClientService(serviceName, address);
return serviceInst;
}
/**
* Gets a client service instance of the given name and set the given endpoint address.
* A detailed explanation can be found in {@link #getClientService(java.lang.String).
*
* @param serviceName Name of the service. Must be one of the names returned by {@link #getSupportedServices()}.
* @param address URL pointing to the service endpoint.
* @return Webservice client initialized with the settings from the WSDL and the endpoint set to the given value.
* @throws NoSuchMethodException In case there is no suitable constructor or {@code getPort()} method in the service
* class.
* @throws InstantiationException In case the constructor call failed.
* @throws IllegalAccessException In case the constructor call or the {@code getPort()} method failed.
* @throws InvocationTargetException In case the constructor call or the {@code getPort()} method failed.
* @throws ClassNotFoundException In case no such service is registered in this loader.
*/
public static Object getClientService(String serviceName, String address)
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
// get client
Object serviceInst = getClientService(serviceName);
// set endpoint
BindingProvider bp = (BindingProvider) serviceInst;
Map<String,Object> context = bp.getRequestContext();
context.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address);
return serviceInst;
}
}