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.api.bus.support;
017    
018    import java.util.List;
019    import java.util.Map;
020    
021    import javax.ws.rs.Path;
022    
023    import org.apache.commons.collections.BidiMap;
024    import org.apache.commons.collections.bidimap.DualHashBidiMap;
025    import org.kuali.rice.core.api.config.ConfigurationException;
026    import org.kuali.rice.core.api.exception.RiceRuntimeException;
027    import org.kuali.rice.ksb.api.KsbApiConstants;
028    import org.kuali.rice.ksb.api.bus.ServiceConfiguration;
029    
030    /**
031     * Service definition for RESTful services.  A JAX-WS service has a resource
032     * class, which is the class or interface marked by the JAX-WS annotations
033     * (e.g. @Path, @GET, etc).  This may or may not be the implementation class.
034     * 
035     * @author Kuali Rice Team (rice.collab@kuali.org)
036     * 
037     */
038    public class RestServiceDefinition extends AbstractServiceDefinition {
039    
040        private static final long serialVersionUID = 5892163789061959602L;
041    
042            private String resourceClass;
043            transient private List<Object> resources;
044            private BidiMap resourceToClassNameMap;
045            transient private List<Object> providers;
046            transient private Map<Object, Object> extensionMappings;
047            transient private Map<Object, Object> languageMappings;
048    
049            /**
050             * Default constructor.  Sets bus security to FALSE.
051             */
052            public RestServiceDefinition() {
053                    setBusSecurity(false);
054            }
055            
056            @Override
057            public String getType() {
058                    return KsbApiConstants.ServiceTypes.REST;
059            }
060    
061            /**
062             * To ensure transparency that RESTful services are not digitally signed, throw an exception
063             * if someone tries to enable bus security.
064             *
065             * @see org.kuali.rice.ksb.AbstractServiceDefinition.ServiceDefinition#setBusSecurity(java.lang.Boolean)
066             */
067            @Override
068            public void setBusSecurity(Boolean busSecurity) {
069                if (busSecurity == true) {
070                    throw new RiceRuntimeException("Rice does not support bus security (digital request/response signing) " +
071                                    "for RESTful services");
072                }
073                super.setBusSecurity(busSecurity);
074            }
075    
076            /**
077             * Set the resourceClass, the class or interface marked by the JAX-WS annotations
078             * which specify the RESTful URL interface.
079             * @param resourceClass the resourceClass to set
080             */
081            public void setResourceClass(String resourceClass) {
082                    this.resourceClass = resourceClass;
083            }
084    
085            /**
086             * @see #setResourceClass(String)
087             * @return the resourceClass
088             */
089            public String getResourceClass() {
090                    return this.resourceClass;
091            }
092    
093            /**
094             * does some simple validation of this RESTServiceDefinition
095             *
096             * @see org.kuali.rice.ksb.AbstractServiceDefinition.ServiceDefinition#validate()
097             */
098            @Override
099            public void validate() {
100    
101                    List<Object> resources = getResources();
102    
103                    if (resources != null && !resources.isEmpty()) {
104                            resourceToClassNameMap = new DualHashBidiMap();
105                            for (Object resource : resources) {
106                                    // If there is no service set then we have to assume that it's the first resource
107                                    if (getService() == null) {
108                                            setService(resource);
109                                    }
110    
111                                    Class<?> resourceClass = resource.getClass();
112                                    if (resourceClass != null) {
113                                            Class<?>[] interfaces = null;
114    
115                                            if (resourceClass.isInterface()) {
116                                                    interfaces = new Class[1];
117                                                    interfaces[0] = resourceClass;
118                                            } else {
119                                                    interfaces = resourceClass.getInterfaces();
120                                            }
121    
122                                            if (interfaces != null) {
123                                                    for (Class<?> iface : interfaces) {
124                                                            Path pathAnnotation = (Path)iface.getAnnotation(Path.class);
125                                                            if (pathAnnotation != null) {
126                                                                    String pathAnnotationValue = pathAnnotation.value();
127                                                                    String resourceId = pathAnnotationValue == null || pathAnnotationValue.equals("/") ? iface.getSimpleName() : pathAnnotationValue;
128                                                                    resourceToClassNameMap.put(resourceId, iface.getName());
129                                                            } else {
130                                                                    // If no path annotation exists, use the simple class name
131                                                                    resourceToClassNameMap.put(iface.getSimpleName(), iface.getName());
132                                                            }
133                                                    }
134                                            }
135                                    }
136                            }
137    
138                    }
139                    
140                    super.validate();
141    
142                    // if interface is null, set it to the service class
143                    if (getResourceClass() == null) {
144                            Class<?>[] interfaces = getService().getClass().getInterfaces();
145                            if (interfaces != null && interfaces.length > 0) {
146                                    setResourceClass(interfaces[0].getName());
147                            } else {
148                                throw new ConfigurationException("resource class must be set to export a REST service");
149                            }
150                    }
151    
152                    // Validate that the JAX-WS annotated class / interface is available to the classloader.
153                    try {
154                        Class.forName(getResourceClass());
155                    } catch (ClassNotFoundException e) {
156                        throw new ConfigurationException(
157                                "resource class '" + getResourceClass() + "' could not be found in the classpath");
158                    }
159    
160            }
161            
162            @Override
163            protected ServiceConfiguration configure() {
164                    return RestServiceConfiguration.fromServiceDefinition(this);
165            }
166    
167            /**
168             * @return the resources
169             */
170            public List<Object> getResources() {
171                    return this.resources;
172            }
173    
174            /**
175             * @param resources the resources to set
176             */
177            public void setResources(List<Object> resources) {
178                    this.resources = resources;
179            }
180    
181            /**
182             * @return the resourceToClassNameMap
183             */
184            @SuppressWarnings("unchecked")
185            public Map<String, String> getResourceToClassNameMap() {
186                    return this.resourceToClassNameMap;
187            }
188    
189            /**
190             * @param className
191             * @return true if this service contains a resource for the given class name
192             */
193            public boolean hasClass(String className) {
194                    if (resourceToClassNameMap == null) return false;
195                    return resourceToClassNameMap.containsValue(className);
196            }
197    
198            /**
199             * @return the providers
200             */
201            public List<Object> getProviders() {
202                    return this.providers;
203            }
204    
205            /**
206             * @param providers the providers to set
207             */
208            public void setProviders(List<Object> providers) {
209                    this.providers = providers;
210            }
211    
212            /**
213             * @return the extensionMappings
214             */
215            public Map<Object, Object> getExtensionMappings() {
216                    return this.extensionMappings;
217            }
218    
219            /**
220             * @param extensionMappings the extensionMappings to set
221             */
222            public void setExtensionMappings(Map<Object, Object> extensionMappings) {
223                    this.extensionMappings = extensionMappings;
224            }
225    
226            /**
227             * @return the languageMappings
228             */
229            public Map<Object, Object> getLanguageMappings() {
230                    return this.languageMappings;
231            }
232    
233            /**
234             * @param languageMappings the languageMappings to set
235             */
236            public void setLanguageMappings(Map<Object, Object> languageMappings) {
237                    this.languageMappings = languageMappings;
238            }
239    
240    
241    
242    }