/** * */ package cn.bran.play.routing; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import javax.ws.rs.ApplicationPath; import javax.ws.rs.Path; import javax.ws.rs.Produces; import org.reflections.Reflections; import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; import cn.bran.japid.util.JapidFlags; import play.Application; import play.GlobalSettings; import play.api.mvc.Handler; import play.libs.F.Tuple; import play.mvc.Result; import play.mvc.SimpleResult; /** * @author bran * */ public class JaxrsRouter { static String routerPackage = "controllers"; static final String ASSET_SERVING = "jaxrc.assets.serving"; static String[] assetServing = new String[] { "/assets", "/public" }; // format: // "user space asset root, folder on file system" // // static Reflections ref = new Reflections(new ConfigurationBuilder() // .filterInputsBy(new FilterBuilder().include(routerPackage + ".*")) // .setScanners(new TypeAnnotationsScanner()).setUrls(ClasspathHelper.forPackage(routerPackage))); static String urlParamCapture = "\\{(.*?)\\}"; static Pattern urlParamCaptureP = Pattern.compile(urlParamCapture); private static ClassLoader parentClassloader; private static Set<Class<?>> classes; private static GlobalSettings global; private static String appPath = ""; static String prefixSlash(String s) { return s.startsWith("/") ? s : "/" + s; } private static List<RouterClass> routerClasses = new ArrayList<RouterClass>(); public static void init(Application app, play.GlobalSettings g) { parentClassloader = app.classloader(); classes = RouterUtils.classes(parentClassloader); global = g; ApplicationPath appPathAnno = global.getClass().getAnnotation(ApplicationPath.class); if (appPathAnno != null) appPath = appPathAnno.value(); routerClasses = parseRouterClasses(classes); String string = app.configuration().getString(ASSET_SERVING); if (string != null) assetServing = string.split(","); } /** * @author Bing Ran (bing.ran@gmail.com) * @param classes2 * @return */ private static List<RouterClass> parseRouterClasses(Set<Class<?>> classes2) { List<RouterClass> routers = new ArrayList<RouterClass>(); for (Class<?> cl : classes2) { JapidFlags.info("found class with JAX-RS Path annotation: " + cl.getName()); routers.add(new RouterClass(cl, appPath)); } return routers; } /** * provides a handler for given request * * @param global * the global of the current play application * @param r * request header * @return action handler * */ public static Handler handlerFor(final play.mvc.Http.RequestHeader r) { if (assetServing.length == 2 && r.path().startsWith(assetServing[0])) { // serve static asset // return controllers.Assets.at(assetServing[1], r.path().replaceFirst(assetServing[0], "")); return null; } else { final RouterClass targetRouterClass = RouterUtils.findLongestMatch(routerClasses, r); if (targetRouterClass == null) return null; final Tuple<Method, Object[]> methodWithArgs = targetRouterClass.findMethodAndGenerateArgs(r); if (methodWithArgs != null) { ResultBuilder resultBuilder = new ResultBuilder() { @Override public SimpleResult create() { try { Method m = methodWithArgs._1; Class<?> cl = targetRouterClass.clz; Object obj = global.getControllerInstance(cl); if (obj == null && (m.getModifiers() & Modifier.STATIC) != Modifier.STATIC) { throw new RuntimeException("the action method is not static while the target object is null: " + targetRouterClass.clz + "#" + m.getName()); } Object[] args = methodWithArgs._2; SimpleResult result = (SimpleResult)m.invoke(obj, args); Produces produces = methodWithArgs._1.getAnnotation(Produces.class); return produces != null ? new WrapProducer(produces.value()[0], result) : result; } catch (InvocationTargetException cause) { System.err.println("Exception occured while trying to invoke: " + targetRouterClass.clz.getName() + "#" + methodWithArgs._1.getName() + " with " + methodWithArgs._2 + " for uri:" + r.path()); throw new RuntimeException(cause.getCause()); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e.getCause()); } } }; JavaActionBridge handler = new JavaActionBridge(targetRouterClass.clz, methodWithArgs._1, resultBuilder); return handler; } } JapidFlags.debug("Japid router could not route this request"); return null; } /** * @author Bing Ran (bing.ran@gmail.com) * @param classloader */ public static void setClassLoader(ClassLoader classloader) { parentClassloader = classloader; } public static String getRouteTableString() { String s = ""; for (RouterClass c : routerClasses) { s += c.toString(); } return s; } public static List<RouteEntry> getRouteTable() { List<RouteEntry> ret = new ArrayList<RouteEntry>(); for (RouterClass c : routerClasses) { ret.addAll(c.getRouteTable()); } return ret; } static public Set<Class<?>> getControllersWithPath() { Reflections reflections = new Reflections(routerPackage); Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(Path.class); return annotated; } }