View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.ksb.api.bus.support;
17  
18  import java.util.List;
19  import java.util.Map;
20  
21  import javax.ws.rs.Path;
22  
23  import org.apache.commons.collections.BidiMap;
24  import org.apache.commons.collections.bidimap.DualHashBidiMap;
25  import org.kuali.rice.core.api.config.ConfigurationException;
26  import org.kuali.rice.core.api.exception.RiceRuntimeException;
27  import org.kuali.rice.ksb.api.KsbApiConstants;
28  import org.kuali.rice.ksb.api.bus.ServiceConfiguration;
29  
30  /**
31   * Service definition for RESTful services.  A JAX-WS service has a resource
32   * class, which is the class or interface marked by the JAX-WS annotations
33   * (e.g. @Path, @GET, etc).  This may or may not be the implementation class.
34   * 
35   * @author Kuali Rice Team (rice.collab@kuali.org)
36   * 
37   */
38  public class RestServiceDefinition extends AbstractServiceDefinition {
39  
40      private static final long serialVersionUID = 5892163789061959602L;
41  
42  	private String resourceClass;
43  	transient private List<Object> resources;
44  	private BidiMap resourceToClassNameMap;
45  	transient private List<Object> providers;
46  	transient private Map<Object, Object> extensionMappings;
47  	transient private Map<Object, Object> languageMappings;
48  
49  	/**
50  	 * Default constructor.  Sets bus security to FALSE.
51  	 */
52  	public RestServiceDefinition() {
53  		setBusSecurity(false);
54  	}
55  	
56  	@Override
57  	public String getType() {
58  		return KsbApiConstants.ServiceTypes.REST;
59  	}
60  
61  	/**
62  	 * To ensure transparency that RESTful services are not digitally signed, throw an exception
63  	 * if someone tries to enable bus security.
64  	 *
65  	 * @see org.kuali.rice.ksb.AbstractServiceDefinition.ServiceDefinition#setBusSecurity(java.lang.Boolean)
66  	 */
67  	@Override
68  	public void setBusSecurity(Boolean busSecurity) {
69  	    if (busSecurity == true) {
70  	        throw new RiceRuntimeException("Rice does not support bus security (digital request/response signing) " +
71  	        		"for RESTful services");
72  	    }
73  	    super.setBusSecurity(busSecurity);
74  	}
75  
76  	/**
77  	 * Set the resourceClass, the class or interface marked by the JAX-WS annotations
78  	 * which specify the RESTful URL interface.
79  	 * @param resourceClass the resourceClass to set
80  	 */
81  	public void setResourceClass(String resourceClass) {
82  		this.resourceClass = resourceClass;
83  	}
84  
85  	/**
86  	 * @see #setResourceClass(String)
87  	 * @return the resourceClass
88  	 */
89  	public String getResourceClass() {
90  		return this.resourceClass;
91  	}
92  
93  	/**
94  	 * does some simple validation of this RESTServiceDefinition
95  	 *
96  	 * @see org.kuali.rice.ksb.AbstractServiceDefinition.ServiceDefinition#validate()
97  	 */
98  	@Override
99  	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 }