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