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.api.bus.support;
017
018import java.util.List;
019import java.util.Map;
020
021import javax.ws.rs.Path;
022
023import org.apache.commons.collections.BidiMap;
024import org.apache.commons.collections.bidimap.DualHashBidiMap;
025import org.kuali.rice.core.api.config.ConfigurationException;
026import org.kuali.rice.core.api.exception.RiceRuntimeException;
027import org.kuali.rice.ksb.api.KsbApiConstants;
028import 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 */
038public 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}