/*! * Copyright 2010 - 2015 Pentaho Corporation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.pentaho.di.repository.pur; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import javax.xml.namespace.QName; import javax.xml.ws.BindingProvider; import javax.xml.ws.Service; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.soap.SOAPBinding; import org.apache.commons.lang.StringUtils; import org.pentaho.di.core.util.ExecutorUtil; import org.pentaho.di.repository.pur.WebServiceSpecification.ServiceType; import org.pentaho.platform.repository2.unified.webservices.jaxws.IUnifiedRepositoryJaxwsWebService; import org.pentaho.platform.security.policy.rolebased.ws.IAuthorizationPolicyWebService; import org.pentaho.platform.security.policy.rolebased.ws.IRoleAuthorizationPolicyRoleBindingDaoWebService; import org.pentaho.platform.security.userrole.ws.IUserRoleListWebService; import org.pentaho.platform.security.userroledao.ws.IUserRoleWebService; import com.pentaho.di.services.PentahoDiPlugin; import com.pentaho.pdi.ws.IRepositorySyncWebService; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.config.ClientConfig; import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; import com.sun.xml.ws.developer.JAXWSProperties; /** * Web service factory. Not a true factory in that the things that this factory can create are not configurable. But it * does cache the services. * * @author mlowery */ public class WebServiceManager implements ServiceManager { /** * Header name must match that specified in ProxyTrustingFilter. Note that an header has the following form: initial * capital letter followed by all lowercase letters. */ private static final String TRUST_USER = "_trust_user_"; //$NON-NLS-1$ private static final String NAMESPACE_URI = "http://www.pentaho.org/ws/1.0"; //$NON-NLS-1$ private static final ExecutorService executor = ExecutorUtil.getExecutor(); private final Map<String, Future<Object>> serviceCache = new HashMap<String, Future<Object>>(); private final Map<Class<?>, WebServiceSpecification> serviceNameMap; private final String baseUrl; private final String lastUsername; private Map<Class<?>, WebServiceSpecification> tempServiceNameMap; // hold the map while building public WebServiceManager( String baseUrl, String username ) { this.baseUrl = baseUrl; this.lastUsername = username; tempServiceNameMap = new HashMap<Class<?>, WebServiceSpecification>(); registerWsSpecification( IUnifiedRepositoryJaxwsWebService.class, "unifiedRepository" ); //$NON-NLS-1$ registerWsSpecification( IRepositorySyncWebService.class, "repositorySync" ); //$NON-NLS-1$ registerWsSpecification( IUserRoleListWebService.class, "userRoleListService" ); //$NON-NLS-1$ registerWsSpecification( IUserRoleWebService.class, "userRoleService" ); //$NON-NLS-1$ registerWsSpecification( IRoleAuthorizationPolicyRoleBindingDaoWebService.class, "roleBindingDao" ); //$NON-NLS-1$ registerWsSpecification( IAuthorizationPolicyWebService.class, "authorizationPolicy" ); //$NON-NLS-1$ registerRestSpecification( PentahoDiPlugin.PurRepositoryPluginApiRevision.class, "purRepositoryPluginApiRevision" ); //$NON-NLS-1$ this.serviceNameMap = Collections.unmodifiableMap( tempServiceNameMap ); tempServiceNameMap = null; } @Override @SuppressWarnings( "unchecked" ) public <T> T createService( final String username, final String password, final Class<T> clazz ) throws MalformedURLException { final Future<Object> resultFuture; synchronized ( serviceCache ) { // if this is true, a coder did not make sure that clearServices was called on disconnect if ( lastUsername != null && !lastUsername.equals( username ) ) { throw new IllegalStateException(); } final WebServiceSpecification webServiceSpecification = serviceNameMap.get( clazz ); final String serviceName = webServiceSpecification.getServiceName(); if ( serviceName == null ) { throw new IllegalStateException(); } if ( webServiceSpecification.getServiceType().equals( ServiceType.JAX_WS ) ) { // build the url handling whether or not baseUrl ends with a slash // String baseUrl = repositoryMeta.getRepositoryLocation().getUrl(); final URL url = new URL( baseUrl + ( baseUrl.endsWith( "/" ) ? "" : "/" ) + "webservices/" + serviceName + "?wsdl" ); //$NON-NLS-1$ //$NON-NLS-2$ String key = url.toString() + '_' + serviceName + '_' + clazz.getName(); if ( !serviceCache.containsKey( key ) ) { resultFuture = executor.submit( new Callable<Object>() { @Override public Object call() throws Exception { Service service = Service.create( url, new QName( NAMESPACE_URI, serviceName ) ); T port = service.getPort( clazz ); // add TRUST_USER if necessary if ( StringUtils.isNotBlank( System.getProperty( "pentaho.repository.client.attemptTrust" ) ) ) { ( (BindingProvider) port ).getRequestContext().put( MessageContext.HTTP_REQUEST_HEADERS, Collections.singletonMap( TRUST_USER, Collections.singletonList( username ) ) ); } else { // http basic authentication ( (BindingProvider) port ).getRequestContext().put( BindingProvider.USERNAME_PROPERTY, username ); ( (BindingProvider) port ).getRequestContext().put( BindingProvider.PASSWORD_PROPERTY, password ); } // accept cookies to maintain session on server ( (BindingProvider) port ).getRequestContext().put( BindingProvider.SESSION_MAINTAIN_PROPERTY, true ); // support streaming binary data // TODO mlowery this is not portable between JAX-WS implementations (uses com.sun) ( (BindingProvider) port ).getRequestContext().put( JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192 ); SOAPBinding binding = (SOAPBinding) ( (BindingProvider) port ).getBinding(); binding.setMTOMEnabled( true ); return port; } } ); serviceCache.put( key, resultFuture ); } else { resultFuture = serviceCache.get( key ); } } else { if ( webServiceSpecification.getServiceType().equals( ServiceType.JAX_RS ) ) { String key = baseUrl.toString() + '_' + serviceName + '_' + clazz.getName(); if ( !serviceCache.containsKey( key ) ) { resultFuture = executor.submit( new Callable<Object>() { @Override public Object call() throws Exception { ClientConfig clientConfig = new DefaultClientConfig(); Client client = Client.create( clientConfig ); client.addFilter( new HTTPBasicAuthFilter( username, password ) ); Class<?>[] parameterTypes = new Class<?>[] { Client.class, URI.class }; String factoryClassName = webServiceSpecification.getServiceClass().getName(); factoryClassName = factoryClassName.substring( 0, factoryClassName.lastIndexOf( "$" ) ); Class<?> factoryClass = Class.forName( factoryClassName ); Method method = factoryClass.getDeclaredMethod( webServiceSpecification.getServiceName(), parameterTypes ); T port = (T) method.invoke( null, new Object[] { client, new URI( baseUrl + "/plugin" ) } ); return port; } } ); serviceCache.put( key, resultFuture ); } else { resultFuture = serviceCache.get( key ); } } else { resultFuture = null; } } try { if ( clazz.isInterface() ) { return UnifiedRepositoryInvocationHandler.forObject( (T) resultFuture.get(), clazz ); } else { return (T) resultFuture.get(); } } catch ( InterruptedException e ) { throw new RuntimeException( e ); } catch ( ExecutionException e ) { Throwable cause = e.getCause(); if ( cause != null ) { if ( cause instanceof RuntimeException ) { throw (RuntimeException) cause; } else if ( cause instanceof MalformedURLException ) { throw (MalformedURLException) cause; } } throw new RuntimeException( e ); } } } @Override public synchronized void close() { serviceCache.clear(); } private void registerWsSpecification( Class<?> serviceClass, String serviceName ) { registerSpecification( WebServiceSpecification.getWsServiceSpecification( serviceClass, serviceName ) ); } private void registerRestSpecification( Class<?> serviceClass, String serviceName ) { try { registerSpecification( WebServiceSpecification.getRestServiceSpecification( serviceClass, serviceName ) ); } catch ( NoSuchMethodException e ) { // TODO Auto-generated catch block e.printStackTrace(); } catch ( SecurityException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } private void registerSpecification( WebServiceSpecification webServiceSpecification ) { tempServiceNameMap.put( webServiceSpecification.getServiceClass(), webServiceSpecification ); } }