001/**
002 * Copyright 2005-2017 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.ksb.messaging.serviceconnectors;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.log4j.Logger;
020import org.kuali.rice.core.api.config.property.Config;
021import org.kuali.rice.core.api.config.property.ConfigContext;
022import org.kuali.rice.core.api.exception.RiceRuntimeException;
023import org.kuali.rice.core.api.security.credentials.CredentialsSource;
024import org.kuali.rice.core.api.security.credentials.CredentialsSourceFactory;
025import org.kuali.rice.ksb.api.bus.ServiceConfiguration;
026import org.kuali.rice.ksb.api.bus.ServiceDefinition;
027import org.kuali.rice.ksb.api.bus.support.JavaServiceConfiguration;
028import org.kuali.rice.ksb.api.bus.support.RestServiceConfiguration;
029import org.kuali.rice.ksb.api.bus.support.SoapServiceConfiguration;
030import org.kuali.rice.ksb.messaging.AlternateEndpoint;
031import org.kuali.rice.ksb.messaging.AlternateEndpointLocation;
032import org.kuali.rice.ksb.util.KSBConstants;
033
034import java.net.MalformedURLException;
035import java.net.URL;
036import java.util.List;
037import java.util.regex.Matcher;
038import java.util.regex.Pattern;
039
040/**
041 * Constructs a ServiceConnector based on the provided
042 * ServiceInfo/ServiceDefinition. Connects that ServiceConnector to the
043 * appropriate CredentialsSource.
044 * <p>
045 * ServiceConnector will fail if a CredentialsSource for the Service Definition
046 * cannot be located.
047 * 
048 * @author Kuali Rice Team (rice.collab@kuali.org)
049 * 
050 * @since 0.9
051 * @see ServiceConnector
052 * @see ServiceDefinition
053 * @see CredentialsSource
054 */
055public class ServiceConnectorFactory {
056
057        private static final Logger LOG = Logger.getLogger(ServiceConnectorFactory.class);
058        
059        public static ServiceConnector getServiceConnector(
060                        final ServiceConfiguration serviceConfiguration) {
061                final CredentialsSourceFactory credentialsSourceFactory = (CredentialsSourceFactory) ConfigContext
062                                .getCurrentContextConfig().getObjects()
063                                .get(Config.CREDENTIALS_SOURCE_FACTORY);
064                final CredentialsSource credentialsSource = credentialsSourceFactory != null ? credentialsSourceFactory
065                                .getCredentialsForType(serviceConfiguration.getCredentialsType()) : null;
066                ServiceConnector serviceConnector = null;
067
068                if (serviceConfiguration.getCredentialsType() != null && credentialsSource == null) {
069                        throw new RiceRuntimeException("Service requires credentials but no factory or CredentialsSource could be located.");
070                }
071
072                String alternateEndpoint = determineAlternateEndpoint(serviceConfiguration);
073                URL alternateEndpointUrl = null;
074                if (!StringUtils.isBlank(alternateEndpoint)) {
075                        try {
076                                alternateEndpointUrl = new URL(alternateEndpoint);
077                        } catch (MalformedURLException e) {
078                                throw new IllegalStateException("Encountered invalid alternate endpoint url: " + alternateEndpoint, e);
079                        }
080                }
081                
082                // TODO switch this to use serviceConfiguration.getType() at some point in the future and allow for
083                // this to be easily "pluggable" with new connector types
084                //
085                // if set in local mode then preempt any protocol connectors
086                if (ConfigContext.getCurrentContextConfig().getDevMode()) {
087                        serviceConnector = new BusLocalConnector(serviceConfiguration);
088                } else if (serviceConfiguration instanceof JavaServiceConfiguration) {
089                        serviceConnector = new HttpInvokerConnector((JavaServiceConfiguration) serviceConfiguration, alternateEndpointUrl);
090                } else if (serviceConfiguration instanceof SoapServiceConfiguration) {
091                        serviceConnector = new SOAPConnector((SoapServiceConfiguration) serviceConfiguration, alternateEndpointUrl);
092                } else if (serviceConfiguration instanceof RestServiceConfiguration) {
093                        serviceConnector = new RESTConnector((RestServiceConfiguration) serviceConfiguration, alternateEndpointUrl);
094                }
095
096                if (serviceConnector == null) {
097                        throw new RiceRuntimeException("Don't support service type of " + serviceConfiguration);
098                }
099                serviceConnector.setCredentialsSource(credentialsSource);
100
101                return serviceConnector;
102        }
103        
104        public static String determineAlternateEndpoint(ServiceConfiguration serviceConfiguration) {
105                String alternateEndpointUrl = null;
106                List<AlternateEndpointLocation> alternateEndpointLocations = (List<AlternateEndpointLocation>) ConfigContext.getCurrentContextConfig().getObject(KSBConstants.Config.KSB_ALTERNATE_ENDPOINT_LOCATIONS);
107                if (alternateEndpointLocations != null) {
108                    for (AlternateEndpointLocation alternateEndpointLocation : alternateEndpointLocations) {
109                        if (Pattern.matches(".*" + alternateEndpointLocation.getEndpointHostReplacementPattern() + ".*", serviceConfiguration.getEndpointUrl().toExternalForm())) {
110                                Pattern myPattern = Pattern.compile(alternateEndpointLocation.getEndpointHostReplacementPattern());
111                                Matcher myMatcher = myPattern.matcher(serviceConfiguration.getEndpointUrl().toExternalForm());
112                                String alternateEndpoint = myMatcher.replaceFirst(alternateEndpointLocation.getEndpointHostReplacementValue());
113                                if ( LOG.isInfoEnabled() ) {
114                                        LOG.info("Found an alternate url host value ("
115                                                        + alternateEndpointLocation.getEndpointHostReplacementValue() + ") for endpoint: "
116                                                        + serviceConfiguration.getEndpointUrl() + " -> instead using: " + alternateEndpoint);
117                                }
118                                alternateEndpointUrl = alternateEndpoint;
119                                break;
120                        }
121                    }
122                }
123                List<AlternateEndpoint> alternateEndpoints = (List<AlternateEndpoint>) ConfigContext.getCurrentContextConfig().getObject(KSBConstants.Config.KSB_ALTERNATE_ENDPOINTS);
124                if (alternateEndpoints != null) {
125                    for (AlternateEndpoint alternateEndpoint : alternateEndpoints) {
126                        if (Pattern.matches(alternateEndpoint.getEndpointUrlPattern(), serviceConfiguration.getEndpointUrl().toExternalForm())) {
127                                if ( LOG.isInfoEnabled() ) {
128                                        LOG.info("Found an alternate url for endpoint: " + serviceConfiguration.getEndpointUrl() + " -> instead using: " + alternateEndpoint.getActualEndpoint());
129                                }
130                                alternateEndpointUrl = alternateEndpoint.getActualEndpoint();
131                                break;
132                        }
133                    }
134                }
135                return alternateEndpointUrl;
136        }
137
138}