001/**
002 * Copyright 2005-2016 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.collections.MapUtils;
019import org.apache.cxf.binding.BindingFactoryManager;
020import org.apache.cxf.interceptor.LoggingInInterceptor;
021import org.apache.cxf.interceptor.LoggingOutInterceptor;
022import org.apache.cxf.jaxrs.JAXRSBindingFactory;
023import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
024import org.apache.log4j.Logger;
025import org.kuali.rice.core.api.exception.RiceRuntimeException;
026import org.kuali.rice.core.api.security.credentials.CredentialsSource;
027import org.kuali.rice.ksb.api.bus.support.RestServiceConfiguration;
028import org.kuali.rice.ksb.api.messaging.ResourceFacade;
029import org.kuali.rice.ksb.messaging.BusClientFailureProxy;
030import org.kuali.rice.ksb.messaging.bam.BAMClientProxy;
031import org.kuali.rice.ksb.security.soap.CredentialsOutHandler;
032import org.kuali.rice.ksb.service.KSBServiceLocator;
033
034import java.net.URL;
035import java.util.Map;
036
037/**
038 * implementation of {@link ResourceFacade}
039 *
040 * @author Kuali Rice Team (rice.collab@kuali.org)
041 *
042 */
043public class ResourceFacadeImpl implements ResourceFacade {
044
045        private static final Logger LOG = Logger.getLogger(ResourceFacadeImpl.class);
046
047        private final RestServiceConfiguration serviceConfiguration;
048        private CredentialsSource credentialsSource;
049        private URL actualEndpointUrl;
050
051        public ResourceFacadeImpl(final RestServiceConfiguration serviceConfiguration, URL actualEndpointUrl) {
052                if (serviceConfiguration == null) {
053                        throw new IllegalArgumentException("serviceConfiguration cannot be null");
054                }
055                if (actualEndpointUrl == null) {
056                        throw new IllegalArgumentException("actual endpoint url cannot be null");
057                }
058                this.serviceConfiguration = serviceConfiguration;
059                this.actualEndpointUrl = actualEndpointUrl;
060        }
061        
062        /**
063         * This overridden method ...
064         *
065         * @see org.kuali.rice.ksb.api.messaging.ResourceFacade#getResource(java.lang.Class)
066         */
067        public <R> R getResource(Class<R> resourceClass) {
068                if (resourceClass == null) throw new IllegalArgumentException("resourceClass argument must not be null");
069
070                if (!serviceConfiguration.hasClass(resourceClass.getName())) {
071                        throw new IllegalArgumentException("Service " + serviceConfiguration.getServiceName() +
072                                        " does not contain an implementation of type " + resourceClass.getName());
073                }
074
075                return getServiceProxy(resourceClass);
076        }
077
078        /**
079         * This overridden method ...
080         *
081         * @see org.kuali.rice.ksb.api.messaging.ResourceFacade#getResource(java.lang.String)
082         */
083        public <R> R getResource(String resourceName) {
084
085                String resourceClassName = null;
086
087                Map<String, String> resourceToClassNameMap = serviceConfiguration.getResourceToClassNameMap();
088
089                if (resourceName != null && resourceToClassNameMap != null)
090                        resourceClassName = resourceToClassNameMap.get(resourceName);
091                else
092                        resourceClassName = serviceConfiguration.getResourceClass();
093
094                if (resourceClassName == null)
095                        throw new RiceRuntimeException("No resource class name was found for the specified resourceName: " + resourceName);
096
097                Class<?> resourceClass = null;
098
099                try {
100                        resourceClass = Class.forName(resourceClassName);
101                } catch (ClassNotFoundException e) {
102                        throw new RiceRuntimeException("Configured resource class " + resourceClassName +
103                                        " in service " + serviceConfiguration.getServiceName() + " is not loadable", e);
104                }
105
106                
107                // allow this to class cast if the types don't match, up to the caller to ensure this is correct
108                @SuppressWarnings("unchecked")
109        R serviceProxy = (R)getServiceProxy(resourceClass);
110                return serviceProxy;
111        }
112
113        /**
114         * This method ...
115         *
116         * @param resourceClass
117         * @return
118         */
119        private <R> R getServiceProxy(Class<R> resourceClass) {
120                JAXRSClientFactoryBean clientFactory;
121
122        clientFactory = new JAXRSClientFactoryBean();
123        clientFactory.setBus(KSBServiceLocator.getCXFBus());
124
125        clientFactory.setResourceClass(resourceClass);
126        clientFactory.setAddress(actualEndpointUrl.toString());
127        BindingFactoryManager bindingFactoryManager = KSBServiceLocator.getCXFBus().getExtension(BindingFactoryManager.class);
128        JAXRSBindingFactory bindingFactory = new JAXRSBindingFactory();
129        bindingFactory.setBus(KSBServiceLocator.getCXFBus());
130
131        bindingFactoryManager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, bindingFactory);
132
133        //Set logging interceptors
134        if (LOG.isDebugEnabled()) {
135                clientFactory.getOutInterceptors().add(new LoggingOutInterceptor());
136        }
137
138        if (getCredentialsSource() != null) {
139            clientFactory.getOutInterceptors().add(new CredentialsOutHandler(getCredentialsSource(), serviceConfiguration));
140        }
141
142        if (LOG.isDebugEnabled()) {
143                clientFactory.getInInterceptors().add(new LoggingInInterceptor());
144        }
145
146        Object service = clientFactory.create();
147        return getServiceProxyWithFailureMode(resourceClass, service, serviceConfiguration);
148        }
149        
150        public boolean isSingleResourceService() {
151                return MapUtils.isEmpty(serviceConfiguration.getResourceToClassNameMap());
152        }
153
154        public void setCredentialsSource(final CredentialsSource credentialsSource) {
155                this.credentialsSource = credentialsSource;
156        }
157
158        protected CredentialsSource getCredentialsSource() {
159                return this.credentialsSource;
160        }
161
162        protected <R> R getServiceProxyWithFailureMode(final Class<R> resourceClass, final Object service, final RestServiceConfiguration serviceConfiguration) {
163                Object bamWrappedClientProxy = BAMClientProxy.wrap(service, serviceConfiguration);
164                Object proxy = BusClientFailureProxy.wrap(bamWrappedClientProxy, serviceConfiguration);
165                if (!resourceClass.isInstance(proxy)) {
166                        throw new IllegalArgumentException("Wrapped proxy is of the wrong type " + proxy.getClass() + ", expected " + resourceClass);
167                }
168                return resourceClass.cast(proxy);
169        }
170
171}