jadex.extension.rs.publish.DefaultRestMethodGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jadex-platform-webservice-grizzly Show documentation
Show all versions of jadex-platform-webservice-grizzly Show documentation
Provides webservice support via Grizzly.
package jadex.extension.rs.publish;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jadex.bridge.service.IServiceIdentifier;
import jadex.commons.MethodInfo;
import jadex.commons.SReflect;
import jadex.extension.rs.invoke.RSJAXAnnotationHelper;
import jadex.extension.rs.publish.annotation.MethodMapper;
import jadex.extension.rs.publish.annotation.ParametersMapper;
import jadex.extension.rs.publish.annotation.ResultMapper;
/**
* The default rest method generator. Analyses
* the Jadex service interface and possible the
* baseclass (which can be a concrete or abstract class or
* an interface).
*/
public class DefaultRestMethodGenerator implements IRestMethodGenerator
{
/**
* Generate the rest method infos.
* @param service The Jadex service.
* @param classloader The classloader.
* @param baseclass The (abstract or concrete) baseclass or interface.
* @param mapprops Additional mapping properties.
* @return The method infos.
* @throws Exception
*/
public List generateRestMethodInfos(IServiceIdentifier serviceid, ClassLoader classloader,
Class> baseclass, Map mapprops) throws Exception
{
List ret = new ArrayList();
boolean gen = mapprops.get(AbstractRestServicePublishService.GENERATE)!=null?
((Boolean)mapprops.get(AbstractRestServicePublishService.GENERATE)).booleanValue(): true;
boolean geninfo = mapprops.get(AbstractRestServicePublishService.GENERATE_INFO)!=null?
((Boolean)mapprops.get(AbstractRestServicePublishService.GENERATE_INFO)).booleanValue(): true;
Class> iface = serviceid.getServiceType().getType(classloader);
MediaType[] formats = AbstractRestServicePublishService.DEFAULT_FORMATS;
Object tmp = mapprops.get(AbstractRestServicePublishService.FORMATS);
if(tmp instanceof String[])
{
String[] fms = (String[])tmp;
formats = new MediaType[fms.length];
for(int i=0; i paths = new HashSet();
Set methods = new LinkedHashSet();
if(baseclass!=null)
{
// Add all methods if is specific interface
if(baseclass.isInterface())
{
Method[] ims = baseclass.getMethods();
for(int i=0; i clazz = baseclass;
while(!clazz.equals(Object.class))
{
Method[] bms = baseclass.getMethods();
for(int i=0; i it = methods.iterator(); it.hasNext(); )
{
MethodWrapper mw = it.next();
Method method = mw.getMethod();
List consumed = new ArrayList();
List produced = new ArrayList();
// Determine rest method type.
Class> resttype = RSJAXAnnotationHelper.getDeclaredRestType(method);
// User defined method, use as is
if(resttype!=null)
{
if(method.isAnnotationPresent(Consumes.class))
{
String[] cons = method.getAnnotation(Consumes.class).value();
if(cons!=null)
{
for(int i=0; i clazz = pm.value().clazz();
if(clazz!=null && !Object.class.equals(clazz))
{
parametermapper = new Value(clazz);
}
else
{
parametermapper = new Value(pm.value().value());
}
}
automapping = pm.automapping();
}
Value resultmapper = null;
if(method.isAnnotationPresent(ResultMapper.class))
{
ResultMapper pm = (ResultMapper)method.getAnnotation(ResultMapper.class);
Class> clazz = pm.value().clazz();
if(clazz!=null && !Object.class.equals(clazz))
resultmapper = new Value(clazz);
else
resultmapper = new Value(pm.value().value());
}
String path = "";
if(method.isAnnotationPresent(Path.class))
{
path = ((Path)method.getAnnotation(Path.class)).value();
}
else
{
path = mw.getName();
}
ret.add(new RestMethodInfo(method, mw.getName(), getPathName(path, paths), resttype, consumed, produced,
methodmapper, parametermapper, automapping, resultmapper,
AbstractRestServicePublishService.class, "invoke"));
}
// Guess how method should be restified
else
{
resttype = guessRestType(method);
// Determine how many and which rest methods have to be created for the set of consumed media types.
if(!GET.class.equals(resttype))
{
for(int j=0; j consumed = new ArrayList();
List produced = new ArrayList();
produced.add(MediaType.TEXT_HTML_TYPE);
ret.add(new RestMethodInfo(new Class[0], String.class, new Class[0], "getServiceInfo", getPathName("", paths), GET.class,
consumed, produced, null, null, false, null,
AbstractRestServicePublishService.class, "getServiceInfo"));
}
// System.out.println("paths: "+paths);
return ret;
}
/**
* Guess the http type (GET, POST, PUT, DELETE, ...) of a method.
* @param method The method.
* @return The rs annotation of the method type to use
*/
public Class> guessRestType(Method method)
{
// Retrieve = GET (hasparams && hasret)
// Update = POST (hasparams && hasret)
// Create = PUT return is pointer to new resource (hasparams? && hasret)
// Delete = DELETE (hasparams? && hasret?)
Class> ret = GET.class;
Class> rettype = SReflect.unwrapGenericType(method.getGenericReturnType());
Class>[] paramtypes = method.getParameterTypes();
boolean hasparams = paramtypes.length>0;
boolean hasret = rettype!=null && !rettype.equals(Void.class) && !rettype.equals(void.class);
// GET or POST if has both
if(hasret)
{
if(hasparams)
{
if(hasStringConvertableParameters(method, rettype, paramtypes))
{
ret = GET.class;
}
else
{
ret = POST.class;
}
}
}
// todo: other types?
// System.out.println("rest-type: "+ret.getName()+" "+method.getName()+" "+hasparams+" "+hasret);
return ret;
// return GET.class;
}
/**
* A a method wrapper.
* @param mw The method mapper.
* @param methods The set of method wrapper.
*/
protected static void addMethodWrapper(MethodWrapper mw, Set methods)
{
if(methods.contains(mw))
{
String basename = mw.getName();
for(int j=0; methods.contains(mw); j++)
{
mw.setName(basename+j);
}
}
methods.add(mw);
}
/**
* Get a path name based on a start name.
* @param path The path name.
* @param paths The already taken paths.
* @return The available path.
*/
protected static String getPathName(String path, Set paths)
{
if(paths.contains(path))
{
String basename = path;
for(int i=0; paths.contains(path); i++)
{
path = basename+i;
}
}
addPath(path, paths);
return path;
}
/**
* Add a path to the set of paths.
* Add the path plus the path with trailing slash.
* @param path The path.
* @param paths The paths.
*/
protected static void addPath(String path, Set paths)
{
String p = path;
if(path.endsWith("/"))
p = path.substring(0, path.length()-1);
else
p = path + "/";
paths.add(path);
paths.add(p);
}
/**
* Test if a method has parameters that are all convertible from string.
* @param method The method.
* @param rettype The return types (possibly unwrapped from future type).
* @param paramtypes The parameter types.
* @return True, if is convertible.
*/
public boolean hasStringConvertableParameters(Method method, Class> rettype, Class>[] paramtypes)
{
boolean ret = true;
for(int i=0; i type)
{
boolean ret = true;
if(!SReflect.isStringConvertableType(type))
{
try
{
Method m = type.getMethod("fromString", new Class[]{String.class});
}
catch(Exception e)
{
try
{
Method m = type.getMethod("valueOf", new Class[]{String.class});
}
catch(Exception e2)
{
ret = false;
}
}
}
return ret;
}
}