001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.restfulEndpoints; 019 020import com.fasterxml.jackson.annotation.JsonView; 021import io.stallion.exceptions.ConfigException; 022import io.stallion.exceptions.UsageException; 023import io.stallion.services.Log; 024import io.stallion.settings.Settings; 025import io.stallion.users.Role; 026import org.apache.commons.lang3.StringUtils; 027 028import javax.ws.rs.*; 029import java.lang.annotation.Annotation; 030import java.lang.reflect.Method; 031import java.lang.reflect.Parameter; 032import java.util.ArrayList; 033import java.util.List; 034import java.util.regex.Pattern; 035 036import static io.stallion.utils.Literals.empty; 037 038 039public class ResourceToEndpoints { 040 041 private String basePath = ""; 042 043 public ResourceToEndpoints() { 044 045 } 046 047 public ResourceToEndpoints(String basePath) { 048 this.basePath = basePath; 049 } 050 051 public List<JavaRestEndpoint> convert(EndpointResource resource) { 052 Class cls = resource.getClass(); 053 List<JavaRestEndpoint> endpoints = new ArrayList<>(); 054 055 // Get defaults from the resource 056 057 Role defaultMinRole = Settings.instance().getUsers().getDefaultEndpointRoleObj(); 058 MinRole minRoleAnno = (MinRole)cls.getAnnotation(MinRole.class); 059 if (minRoleAnno != null) { 060 defaultMinRole = minRoleAnno.value(); 061 } 062 063 String defaultProduces = "text/html"; 064 Produces producesAnno = (Produces)cls.getAnnotation(Produces.class); 065 if (producesAnno != null && producesAnno.value().length > 0) { 066 defaultProduces = producesAnno.value()[0]; 067 } 068 069 Path pathAnno = (Path)cls.getAnnotation(Path.class); 070 if (pathAnno != null) { 071 basePath += pathAnno.value(); 072 } 073 074 Class defaultJsonViewClass = null; 075 DefaultJsonView jsonView = (DefaultJsonView)cls.getAnnotation(DefaultJsonView.class); 076 if (jsonView != null) { 077 defaultJsonViewClass = jsonView.value(); 078 } 079 080 081 for(Method method: cls.getDeclaredMethods()) { 082 JavaRestEndpoint endpoint = new JavaRestEndpoint(); 083 endpoint.setRole(defaultMinRole); 084 endpoint.setProduces(defaultProduces); 085 if (defaultJsonViewClass != null) { 086 endpoint.setJsonViewClass(defaultJsonViewClass); 087 } 088 089 Log.finer("Resource class method: {0}", method.getName()); 090 for(Annotation anno: method.getDeclaredAnnotations()) { 091 if (Path.class.isInstance(anno)) { 092 Path pth = (Path)anno; 093 endpoint.setRoute(getBasePath() + pth.value()); 094 } else if (GET.class.isInstance(anno)) { 095 endpoint.setMethod("GET"); 096 } else if (POST.class.isInstance(anno)) { 097 endpoint.setMethod("POST"); 098 } else if (DELETE.class.isInstance(anno)) { 099 endpoint.setMethod("DELETE"); 100 } else if (PUT.class.isInstance(anno)) { 101 endpoint.setMethod("PUT"); 102 } else if (Produces.class.isInstance(anno)) { 103 endpoint.setProduces(((Produces) anno).value()[0]); 104 } else if (MinRole.class.isInstance(anno)) { 105 endpoint.setRole(((MinRole) anno).value()); 106 } else if (XSRF.class.isInstance(anno)) { 107 endpoint.setCheckXSRF(((XSRF)anno).value()); 108 } else if (JsonView.class.isInstance(anno)) { 109 Class[] classes = ((JsonView)anno).value(); 110 if (classes == null || classes.length != 1) { 111 throw new UsageException("JsonView annotation for method " + method.getName() + " must have exactly one view class"); 112 } 113 endpoint.setJsonViewClass(classes[0]); 114 } 115 } 116 if (!empty(endpoint.getMethod()) && !empty(endpoint.getRoute())) { 117 endpoint.setJavaMethod(method); 118 endpoint.setResource(resource); 119 endpoints.add(endpoint); 120 Log.fine("Register endpoint {0} {1}", endpoint.getMethod(), endpoint.getRoute()); 121 } else { 122 continue; 123 } 124 int x = -1; 125 for(Parameter param: method.getParameters()) { 126 x++; 127 RequestArg arg = new RequestArg(); 128 for (Annotation anno: param.getAnnotations()) { 129 Log.finer("Param Annotation is: {0}, {1}", anno, anno.getClass().getName()); 130 if (BodyParam.class.isInstance(anno)) { 131 BodyParam bodyAnno = (BodyParam)(anno); 132 arg.setType("BodyParam"); 133 arg.setName(((BodyParam) anno).value()); 134 arg.setAnnotationClass(BodyParam.class); 135 arg.setRequired(bodyAnno.required()); 136 arg.setEmailParam(bodyAnno.isEmail()); 137 arg.setMinLength(bodyAnno.minLength()); 138 arg.setAllowEmpty(bodyAnno.allowEmpty()); 139 if (!empty(bodyAnno.validationPattern())) { 140 arg.setValidationPattern(Pattern.compile(bodyAnno.validationPattern())); 141 } 142 } else if (ObjectParam.class.isInstance(anno)) { 143 ObjectParam oParam = (ObjectParam)anno; 144 arg.setType("ObjectParam"); 145 arg.setName("noop"); 146 if (oParam.targetClass() == null || oParam.targetClass().equals(Object.class)) { 147 arg.setTargetClass(param.getType()); 148 } else { 149 arg.setTargetClass(oParam.targetClass()); 150 } 151 arg.setAnnotationClass(ObjectParam.class); 152 } else if (MapParam.class.isInstance(anno)) { 153 arg.setType("MapParam"); 154 arg.setName("noop"); 155 arg.setAnnotationClass(MapParam.class); 156 } else if (QueryParam.class.isInstance(anno)) { 157 arg.setType("QueryParam"); 158 arg.setName(((QueryParam)anno).value()); 159 arg.setAnnotationClass(QueryParam.class); 160 } else if (PathParam.class.isInstance(anno)) { 161 arg.setType("PathParam"); 162 arg.setName(((PathParam)anno).value()); 163 arg.setAnnotationClass(PathParam.class); 164 } else if (DefaultValue.class.isInstance(anno)) { 165 arg.setDefaultValue(((DefaultValue) anno).value()); 166 } 167 } 168 if (StringUtils.isEmpty(arg.getType()) || StringUtils.isEmpty(arg.getName())) { 169 throw new ConfigException("The endpoint " + endpoint.getMethod() + " " + endpoint.getRoute() + " (java method is " + endpoint.getJavaMethod().getName() + ")" + 170 " has an invalid argument. Argument " + x + " does not have a proper Param annotation and/or name."); 171 } 172 endpoint.getArgs().add(arg); 173 } 174 } 175 return endpoints; 176 } 177 178 public String getBasePath() { 179 return basePath; 180 } 181 182 public ResourceToEndpoints setBasePath(String basePath) { 183 this.basePath = basePath; 184 return this; 185 } 186}