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}