001    /**
002     * Copyright 2005-2014 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.ksb.messaging.serviceconnectors;
017    
018    import org.apache.commons.collections.MapUtils;
019    import org.apache.cxf.binding.BindingFactoryManager;
020    import org.apache.cxf.interceptor.LoggingInInterceptor;
021    import org.apache.cxf.interceptor.LoggingOutInterceptor;
022    import org.apache.cxf.jaxrs.JAXRSBindingFactory;
023    import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
024    import org.apache.log4j.Logger;
025    import org.kuali.rice.core.api.exception.RiceRuntimeException;
026    import org.kuali.rice.core.api.security.credentials.CredentialsSource;
027    import org.kuali.rice.ksb.api.bus.support.RestServiceConfiguration;
028    import org.kuali.rice.ksb.api.messaging.ResourceFacade;
029    import org.kuali.rice.ksb.messaging.BusClientFailureProxy;
030    import org.kuali.rice.ksb.messaging.bam.BAMClientProxy;
031    import org.kuali.rice.ksb.security.soap.CredentialsOutHandler;
032    import org.kuali.rice.ksb.service.KSBServiceLocator;
033    
034    import java.net.URL;
035    import java.util.Map;
036    
037    /**
038     * implementation of {@link ResourceFacade}
039     *
040     * @author Kuali Rice Team (rice.collab@kuali.org)
041     *
042     */
043    public 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    }