package com.intrbiz.bergamot.ui.router; import java.lang.reflect.Parameter; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.TreeSet; import com.intrbiz.Util; import com.intrbiz.balsa.engine.impl.route.Route; import com.intrbiz.balsa.engine.impl.route.exec.ExecBuilder; import com.intrbiz.balsa.engine.impl.route.exec.argument.ArgumentBuilder; import com.intrbiz.balsa.engine.impl.route.exec.argument.ListParameterArgument; import com.intrbiz.balsa.engine.impl.route.exec.argument.ParameterArgument; import com.intrbiz.balsa.engine.route.Router; import com.intrbiz.balsa.util.BalsaWriter; import com.intrbiz.bergamot.metadata.IgnoreBinding; import com.intrbiz.bergamot.ui.BergamotApp; import com.intrbiz.metadata.Any; import com.intrbiz.metadata.ListOf; import com.intrbiz.metadata.Prefix; import com.intrbiz.metadata.RequireValidPrincipal; import com.intrbiz.metadata.SetOf; import com.intrbiz.metadata.Template; @Prefix("/about") @Template("layout/main") @RequireValidPrincipal() public class AboutRouter extends Router<BergamotApp> { @Any("/") public void about() { var("bergamot_version", BergamotApp.VERSION.NUMBER); var("bergamot_codename", BergamotApp.VERSION.CODE_NAME); encode("about/index"); } @Any("/generate/api/bindings/java") public void generateJavaAPIBindings() throws Exception { StringBuilder bindings = new StringBuilder(); Set<String> imports = new TreeSet<String>(); // generate the bindings for (Router<?> router : app().getRouters()) { String prefix = router.getPrefix(); if (prefix.startsWith("/api")) { for (Route route : Route.fromRouter(prefix, router)) { if ((! route.isFilter()) && (! route.isExceptionHandler())) { RouteInfo info = new RouteInfo(route); if (! info.isIgnoreBinding()) { info.buildRouteAPICall(bindings, imports); } } } } } // write the response BalsaWriter writer = response().ok().contentType("text/java").header("Content-Disposition", "attachment; filename=BergamotClient.java").getViewWriter(); // write the client writer.write("package com.intrbiz.bergamot;\n"); writer.write("\n"); writer.write("import com.intrbiz.bergamot.credentials.*;\n"); // imports for (String imported : imports) { writer.append("import ").append(imported).append(";\n"); } writer.write("\n"); writer.write("public class BergamotClient extends BaseBergamotClient\n"); writer.write("{\n"); writer.write("\n"); writer.write(" public BergamotClient(String baseURL, ClientCredentials credentials)\n"); writer.write(" {\n"); writer.write(" super(baseURL, credentials);\n"); writer.write(" }\n"); writer.write("\n"); writer.write(" public BergamotClient(String baseURL, String username, String password)\n"); writer.write(" {\n"); writer.write(" super(baseURL, username, password);\n"); writer.write(" }\n"); writer.write("\n"); writer.write(" public BergamotClient(String baseURL, String token)\n"); writer.write(" {\n"); writer.write(" super(baseURL, token);\n"); writer.write(" }\n"); writer.write("\n"); writer.write(" public BergamotClient(String baseURL)\n"); writer.write(" {\n"); writer.write(" super(baseURL);\n"); writer.write(" }\n"); writer.write("\n"); // calls writer.write(bindings.toString()); // done writer.write("}\n"); writer.write("\n"); } @Any("/routes") public void routes() throws Exception { // introspect our routes List<RouteInfo> routes = var("routes", new LinkedList<RouteInfo>()); for (Router<?> router : app().getRouters()) { String prefix = router.getPrefix(); for (Route route : Route.fromRouter(prefix, router)) { if ((! route.isFilter()) && (! route.isExceptionHandler())) routes.add(new RouteInfo(route)); } } encode("about/routes"); } public static class RouteInfo { private String method; private String prefix; private String pattern; private LinkedHashSet<String> as; private List<RouteParameter> parameters; private Class<?> returnType; private Class<?> returnOfType; private String callName; private boolean ignoreBinding; public RouteInfo(Route route) throws Exception { this.method = route.getMethod(); this.prefix = route.getPrefix(); this.pattern = route.getPattern(); this.as = new LinkedHashSet<String>(Arrays.asList(route.getCompiledPattern().getAs())); this.parameters = new LinkedList<RouteParameter>(); this.callName = route.getHandler().getName(); this.returnType = route.getHandler().getReturnType(); // of type ListOf loa = route.getHandler().getAnnotation(ListOf.class); if (loa != null) this.returnOfType = loa.value(); SetOf soa = route.getHandler().getAnnotation(SetOf.class); if (soa != null) this.returnOfType = soa.value(); // manual binding this.ignoreBinding = route.getHandler().getAnnotation(IgnoreBinding.class) != null; // use the exec build to introspect the route ExecBuilder exec = ExecBuilder.build(route); // look at the annotations Parameter[] parameters = route.getHandler().getParameters(); for (int i = 0; i < exec.getArguments().length; i++) { ArgumentBuilder<?> builder = exec.getArguments()[i]; Parameter parameter = parameters[i]; if (builder instanceof ParameterArgument) { ParameterArgument paramArg = (ParameterArgument) builder; this.parameters.add(new RouteParameter(paramArg.getParameterName(), parameter.getName(), parameter.getType(), this.as.contains(paramArg.getParameterName()))); } else if (builder instanceof ListParameterArgument) { ListParameterArgument paramArg = (ListParameterArgument) builder; this.parameters.add(new RouteParameter(paramArg.getParameterName(), parameter.getName(), parameter.getType(), false)); } } } public String getMethod() { return this.method; } public String getPrefix() { return prefix; } public String getPattern() { return pattern; } public String getPath() { return (this.prefix.endsWith("/") ? this.prefix.substring(0, this.prefix.length() -1) : this.prefix) + this.pattern; } public LinkedHashSet<String> getAs() { return as; } public List<RouteParameter> getParameters() { return parameters; } public Class<?> getReturnType() { return returnType; } public Class<?> getReturnOfType() { return this.returnOfType; } public String getBindingReturnType() { if (void.class == this.returnType) { return "String"; } else if (List.class == this.returnType) { return "List<" + (this.returnOfType == null ? "?" : this.returnOfType.getSimpleName()) + ">"; } return this.returnType.getSimpleName(); } public String getCallName() { return callName; } public boolean isIgnoreBinding() { return this.ignoreBinding; } public void buildRouteAPICall(StringBuilder sb, Set<String> imports) { imports.add("java.io.IOException"); imports.add("org.apache.http.client.fluent.*"); imports.add("com.intrbiz.bergamot.*"); imports.add("com.intrbiz.bergamot.model.message.*"); for (RouteParameter param : this.parameters) { imports.add(param.getType().getCanonicalName()); } if (void.class != this.returnType) imports.add(this.returnType.getCanonicalName()); if (this.returnOfType != null) imports.add(this.returnOfType.getCanonicalName()); sb.append("\n\n"); sb.append(" public static class ").append(Util.ucFirst(this.callName)).append("Call extends BergamotAPICall<").append(this.getBindingReturnType()).append(">\n"); sb.append(" {\n"); // parameters for (RouteParameter parameter : this.parameters) { sb.append("\n"); sb.append(" private ").append(parameter.getType().getSimpleName()).append(" ").append(parameter.getFieldName()).append(";\n"); } sb.append("\n"); // sb.append(" public ").append(Util.ucFirst(this.callName)).append("Call(BaseBergamotClient client)\n"); sb.append(" {\n"); sb.append(" super(client);\n"); sb.append(" }\n"); // sb.append("\n"); for (RouteParameter parameter : this.parameters) { sb.append("\n"); sb.append(" public ").append(Util.ucFirst(this.callName)).append("Call ").append(parameter.getFieldName()) .append("(").append(parameter.getType().getSimpleName()).append(" ").append(parameter.getFieldName()).append(")\n"); sb.append(" {\n"); sb.append(" this.").append(parameter.getFieldName()).append(" = ").append(parameter.getFieldName()).append(";\n"); sb.append(" return this;\n"); sb.append(" }\n"); } // String url = "\"" + this.getPath() + "\""; for (RouteParameter param : this.parameters) { if (param.isAs()) url = url.replace(":" + param.getName(), "\" + this." + param.getFieldName() + " + \""); } // sb.append("\n"); sb.append(" public ").append(this.getBindingReturnType()).append(" execute()\n"); sb.append(" {\n"); sb.append(" try\n"); sb.append(" {\n"); sb.append(" Response response = execute(\n"); if ("post".equalsIgnoreCase(this.method) || "any".equalsIgnoreCase(this.method)) { sb.append(" post(url(").append(url).append("))\n"); sb.append(" .addHeader(authHeader())\n"); sb.append(" .bodyForm(\n"); boolean ns = false; for (RouteParameter param : this.parameters) { if (! param.isAs()) { if (ns) sb.append(",\n"); sb.append(" param(\"").append(param.getName()).append("\", this.").append(param.getFieldName()).append(")"); ns = true; } } sb.append("\n"); sb.append(" )\n"); } else { if (this.parameters.stream().allMatch((p) -> p.isAs())) { sb.append(" get(url(").append(url).append("))\n"); sb.append(" .addHeader(authHeader())\n"); } else { sb.append(" get(\n"); sb.append(" appendQuery(\n"); sb.append(" url(").append(url).append(")"); for (RouteParameter param : this.parameters) { if (! param.isAs()) { sb.append(",\n"); sb.append(" param(\"").append(param.getName()).append("\", this.").append(param.getFieldName()).append(")"); } } sb.append("\n"); sb.append(" )\n"); sb.append(" ).addHeader(authHeader())\n"); } } sb.append(" );\n"); if (void.class == this.returnType) { sb.append(" return response.returnContent().asString();\n"); } else if (List.class == this.returnType) { sb.append(" return transcoder().decodeListFromString(response.returnContent().asString(), ").append(this.returnOfType == null ? "?" : this.returnOfType.getSimpleName()).append(".class);\n"); } else { sb.append(" return transcoder().decodeFromString(response.returnContent().asString(), ").append(this.getReturnType().getSimpleName()).append(".class);\n"); } sb.append(" }\n"); sb.append(" catch (IOException e)\n"); sb.append(" {\n"); sb.append(" throw new BergamotAPIException(\"Error calling Bergamot Monitoring API\", e);\n"); sb.append(" }\n"); sb.append(" }\n"); sb.append("\n"); sb.append(" }\n"); sb.append("\n\n"); // the binding method sb.append(" public ").append(Util.ucFirst(this.callName)).append("Call call").append(Util.ucFirst(this.callName)).append("()\n"); sb.append(" {\n"); sb.append(" return new ").append(Util.ucFirst(this.callName)).append("Call(this);\n"); sb.append(" }\n"); sb.append("\n"); } } public static class RouteParameter { private final String name; private final String fieldName; private final Class<?> type; private final boolean as; public RouteParameter(String name, String fieldName, Class<?> type, boolean as) { this.name = name; this.fieldName = fieldName; this.type = type; this.as = as; } public String getName() { return name; } public String getFieldName() { return fieldName; } public Class<?> getType() { return type; } public boolean isAs() { return this.as; } } }