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 }