All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.linkedin.restli.examples.greetings.server.GreetingsResourceCodeGenerator Maven / Gradle / Ivy

Go to download

Pegasus is a framework for building robust, scalable service architectures using dynamic discovery and simple asychronous type-checked REST + JSON APIs.

There is a newer version: 27.7.18
Show newest version
/*
   Copyright (c) 2012 LinkedIn Corp.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package com.linkedin.restli.examples.greetings.server;

import com.linkedin.common.callback.Callback;
import com.linkedin.parseq.BaseTask;
import com.linkedin.parseq.Context;
import com.linkedin.parseq.Task;
import com.linkedin.parseq.Tasks;
import com.linkedin.parseq.promise.Promise;
import com.linkedin.parseq.promise.Promises;
import com.linkedin.parseq.promise.SettablePromise;
import com.linkedin.restli.examples.greetings.api.Greeting;
import com.linkedin.restli.internal.server.model.ResourceMethodDescriptor.InterfaceType;
import com.linkedin.restli.server.annotations.CallbackParam;
import com.linkedin.restli.server.annotations.ParSeqContext;
import com.linkedin.restli.server.annotations.RestLiCollection;
import com.linkedin.restli.server.annotations.RestMethod;
import com.linkedin.restli.server.resources.BaseResource;
import com.linkedin.restli.server.resources.KeyValueResource;
import com.linkedin.restli.server.resources.ResourceContextHolder;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.annotation.Generated;

/**
 * Generate concrete implementations of the greetings resource, {@link GreetingsResourceImpl}, with
 * all methods and annotations copied. This allows us to exhaustively test all interface types.
 * 

* See the generateGreetings task in build.gradle and the * TestGreetingsClientFlavors test case. * * @author jnwang */ public class GreetingsResourceCodeGenerator { private static final Class IMPL_CLASS = GreetingsResourceImpl.class; private static final String NAMESPACE = "com.linkedin.restli.examples.greetings.client"; private static final Class RES_ANNOTATION = RestLiCollection.class; private static final Class KEY = Long.class; private static final Class VALUE = Greeting.class; private static final String PACKAGE = "com.linkedin.restli.examples.greetings.server"; private static final String BASIC_CLASS_NAME = "GreetingsResource"; private static final String BASIC_RES_NAME = "greetings"; private final Class _templateClass; private final InterfaceType _type; private final String _suffix; private final boolean _useParSeqCtx; private final String _resourceName; private final String _className; private GreetingsResourceCodeGenerator(final Class templateClass, final InterfaceType type, final String suffix, final boolean useParSeqCtx) { _templateClass = templateClass; _type = type; _suffix = suffix; _useParSeqCtx = useParSeqCtx; _resourceName = BASIC_RES_NAME + _suffix; _className = BASIC_CLASS_NAME + _suffix; } /** * @param args * template, interface type, suffix, use ParSeq context * @throws ClassNotFoundException */ public static void main(final String[] args) throws ClassNotFoundException { // want a hard failure if something goes wrong, so gradle stops if (args.length != 4) throw new IllegalArgumentException("Bad arguments to code generator"); final Class template = Class.forName(args[0]); final InterfaceType type = InterfaceType.valueOf(args[1]); final String suffix = args[2]; final boolean useParSeqCtx = Boolean.valueOf(args[3]); final GreetingsResourceCodeGenerator gen = new GreetingsResourceCodeGenerator(template, type, suffix, useParSeqCtx); gen.printAll(System.out); } private void printAll(final PrintStream out) { // print out the main body later final ByteArrayOutputStream bodyOutStream = new ByteArrayOutputStream(); final PrintStream bodyOut = new PrintStream(bodyOutStream); printClassHeader(bodyOut); bodyOut.println(); printAllMethods(bodyOut); bodyOut.println("}"); // end class // package and imports first out.printf("package %s;%n", PACKAGE); out.println(); for (final String c : _importedFull) { // don't make import statements for arrays, the base class will be imported separately if (!(c.endsWith("[]"))) { out.printf("import %s;%n", c); } } out.println(); // main code body out.print(new String(bodyOutStream.toByteArray())); } /** * class annotation, class definition, private variables */ private void printClassHeader(final PrintStream out) { out.printf("/* WARNING: GENERATED CODE - DO NOT MODIFY BY HAND */%n%n"); // generated annotation out.printf("@%s(\"%s\")%n", className(Generated.class), getClass().getName()); // collection resource annotation out.printf("@%s(name = \"%s\", namespace = \"%s\")%n", className(RES_ANNOTATION), _resourceName, NAMESPACE); // class declaration out.printf("public class %s extends ", _className); if (_templateClass.isInterface()) out.printf("%s implements ", className(ResourceContextHolder.class)); out.printf("%s<%s, %s>", className(_templateClass), className(KEY), className(VALUE)); out.println(); out.println("{"); // private constants out.printf("private static final GreetingsResourceImpl _impl = new GreetingsResourceImpl(\"%s\");%n", _resourceName); if (_type == InterfaceType.CALLBACK || _type == InterfaceType.PROMISE && !_useParSeqCtx) { out.printf("private static final %s _scheduler = %s.newScheduledThreadPool(1);%n", className(ScheduledExecutorService.class), className(Executors.class)); out.println("private static final int DELAY = 100;"); } } private void printAllMethods(final PrintStream out) { // avoid stuff declared in Object and superclasses final Method[] methods = IMPL_CLASS.getMethods(); for (final Method method : methods) if (method.getDeclaringClass() == IMPL_CLASS) printMethod(out, method); } private void printMethod(final PrintStream out, final Method method) { final Type returnType = method.getGenericReturnType(); // method annotations final Annotation[] annos = method.getAnnotations(); for (final Annotation anno : annos) { if (RestMethod.class.equals(anno.annotationType().getEnclosingClass())) if (!_templateClass.equals(KeyValueResource.class)) continue; out.println(toStrAnnotation(anno)); } // method header out.print("public "); // return type switch (_type) { case CALLBACK: out.print("void"); break; case PROMISE: out.print(genericWrapper(Promise.class, returnType)); break; case SYNC: out.print(toStrGenericType(returnType)); break; case TASK: out.print(genericWrapper(Task.class, returnType)); break; } out.printf(" %s(", method.getName()); printArgumentList(out, method); out.print(")"); printThrowsClause(out, method); out.println(); out.println("{"); // begin method body printMethodBody(out, method); out.println("}"); // end method out.println(); } private void printArgumentList(final PrintStream out, final Method method) { final Type[] argTypes = method.getGenericParameterTypes(); final Annotation[][] argAnnos = method.getParameterAnnotations(); boolean first = true; for (int i = 0; i < argTypes.length; i++) { final Type argType = argTypes[i]; if (argType == BaseResource.class) continue; if (!first) out.print(", "); first = false; for (final Annotation anno : argAnnos[i]) out.print(toStrAnnotation(anno) + " "); out.printf("final %s %s", toStrGenericType(argType), (char) ('a' + i)); } if (_type == InterfaceType.CALLBACK) { final Type returnType = method.getGenericReturnType(); if (!first) out.print(", "); out.printf("@%s final %s callback", className(CallbackParam.class), genericWrapper(Callback.class, returnType)); } else if (_useParSeqCtx) { if (!first) out.print(", "); out.printf("@%s final %s psContext", className(ParSeqContext.class), className(Context.class)); } } private void printThrowsClause(final PrintStream out, final Method method) { final Class[] exceptions = method.getExceptionTypes(); if (exceptions.length > 0) { out.print(" throws "); for (int i = 0; i < exceptions.length; i++) { if (i != 0) out.print(", "); out.print(className(exceptions[i])); } } } private void printMethodBody(final PrintStream out, final Method method) { final Type returnType = method.getGenericReturnType(); switch (_type) { case CALLBACK: out.println("final Runnable requestHandler = new Runnable() {"); out.println("public void run () {"); out.println("try {"); if (returnType == void.class) { printImplCall(out, method); out.println(";"); out.println("callback.onSuccess(null);"); } else { out.print("callback.onSuccess("); printImplCall(out, method); out.println(");"); } out.println("} catch (final Throwable throwable) {"); out.println("callback.onError(throwable);"); out.println("}"); // try catch out.println("}"); // run out.println("};"); // runnable out.printf("_scheduler.schedule(requestHandler, DELAY, %s.MILLISECONDS);", className(TimeUnit.class)); break; case PROMISE: if (_useParSeqCtx) { out.printf("final %s result = %s.settable();%n", genericWrapper(SettablePromise.class, returnType), className(Promises.class)); out.println("final Runnable requestHandler = new Runnable() {"); out.println("public void run () {"); out.println("try {"); if (returnType == void.class) { printImplCall(out, method); out.println(";"); out.println("result.done(null);"); } else { out.print("result.done("); printImplCall(out, method); out.println(");"); } out.println("} catch (final Throwable throwable) {"); out.println("result.fail(throwable);"); out.println("}"); // try catch out.println("}"); // run out.println("};"); // runnable out.printf("psContext.run(%s.action(\"restli-%s\", requestHandler));%n", className(Tasks.class), method.getName()); out.println("return result;"); } else { out.printf("final %s result = %s.settable();%n", genericWrapper(SettablePromise.class, returnType), className(Promises.class)); out.println("final Runnable requestHandler = new Runnable() {"); out.println("public void run () {"); out.println("try {"); if (returnType == void.class) { printImplCall(out, method); out.println(";"); out.println("result.done(null);"); } else { out.print("result.done("); printImplCall(out, method); out.println(");"); } out.println("} catch (final Throwable throwable) {"); out.println("result.fail(throwable);"); out.println("}"); // try catch out.println("}"); // run out.println("};"); // runnable out.printf("_scheduler.schedule(requestHandler, DELAY, %s.MILLISECONDS);", className(TimeUnit.class)); out.println("return result;"); } break; case SYNC: if (!returnType.equals(void.class)) out.print("return "); printImplCall(out, method); out.println(";"); break; case TASK: out.printf("return new %s()%n", genericWrapper(BaseTask.class, returnType)); out.println("{"); out.printf("protected %s run(final %s context) throws Exception%n", genericWrapper(Promise.class, returnType), className(Context.class)); out.println("{"); if (returnType.equals(void.class)) { printImplCall(out, method); out.println(";"); out.printf("return %s.value(null);%n", className(Promises.class)); } else { out.printf("return %s.value(", className(Promises.class)); printImplCall(out, method); out.println(");"); } out.println("}"); out.println("};"); break; } } /** * _impl.method(a, b, ...) */ private void printImplCall(final PrintStream out, final Method method) { final Type[] argTypes = method.getGenericParameterTypes(); out.printf("_impl.%s(", method.getName()); for (int i = 0; i < argTypes.length; i++) { if (i > 0) out.print(", "); if (argTypes[i] == BaseResource.class) out.printf("%s.this", _className); else out.print((char) ('a' + i)); } out.print(")"); } /** * @return the source code corresponding to the given type */ private String toStrGenericType(final Type t) { return toStrGenericType(t, false); } private static final Map, String> PRIMITIVES_TO_CLASSES; static { final Map, String> map = new HashMap, String>(); map.put(byte.class, "Byte"); map.put(short.class, "Short"); map.put(int.class, "Integer"); map.put(long.class, "Long"); map.put(float.class, "Float"); map.put(double.class, "Double"); map.put(boolean.class, "Boolean"); map.put(char.class, "Character"); map.put(void.class, "Void"); PRIMITIVES_TO_CLASSES = Collections.unmodifiableMap(map); } /** * @param coercePrimitives * should primitives be converted to their corresponding objects, e.g. int => Integer * @return source code for the given type */ private String toStrGenericType(final Type t, final boolean coercePrimitives) { if (t instanceof Class) { final Class c = (Class) t; if (c.isPrimitive() && coercePrimitives) { if (PRIMITIVES_TO_CLASSES.containsKey(c)) return PRIMITIVES_TO_CLASSES.get(c); else // map should be complete throw new AssertionError("unknown primitive: " + c.toString()); } else { return className((Class) t); } } if (t instanceof ParameterizedType) { final ParameterizedType param = (ParameterizedType) t; final Type[] args = param.getActualTypeArguments(); final StringBuilder result = new StringBuilder(); result.append(toStrGenericType(param.getRawType())); result.append('<'); // begin type params for (int i = 0; i < args.length; i++) { if (i > 0) result.append(", "); result.append(toStrGenericType(args[i], true)); } result.append('>'); // end type params return result.toString(); } if (t instanceof GenericArrayType) { final GenericArrayType type = (GenericArrayType) t; return toStrGenericType(type.getGenericComponentType()) + "[]"; } throw new UnsupportedOperationException("Don't know how to handle " + t); } /** * @return the source code corresponding to the annotation */ // NOTE does not handle all cases, just enough to work private String toStrAnnotation(final Annotation anno) { final StringBuilder sb = new StringBuilder(); sb.append('@'); final Class annoType = anno.annotationType(); sb.append(className(annoType)); final Method[] methods = annoType.getDeclaredMethods(); final Map values = new TreeMap(); for (final Method method : methods) { try { final Object value = method.invoke(anno); if (value.equals(method.getDefaultValue())) continue; final String name = method.getName(); if (value instanceof String) values.put(name, "\"" + value + "\""); else if (value instanceof Enum) values.put(name, className(value.getClass()) + "." + value); else if (value instanceof Class) values.put(name, className((Class) value) + ".class"); else values.put(name, value.toString()); } catch (final IllegalAccessException e) { throw new AssertionError(e); } catch (final InvocationTargetException e) { throw new AssertionError(e); } } // omit parens when no values if (values.size() > 0) sb.append('('); // begin annotation values boolean first = true; for (final Map.Entry entry : values.entrySet()) { if (!first) sb.append(", "); first = false; final String key = entry.getKey(); // omit "value = " when only one if (values.size() == 1 && key.equals("value")) { sb.append(entry.getValue()); } else { sb.append(entry.getKey()); sb.append(" = "); sb.append(entry.getValue()); } } if (values.size() > 0) sb.append(')'); // end annotation values return sb.toString(); } /** * @return genericClass<typeArg>, using the boxed version of typeArg if it is a primitive */ private String genericWrapper(final Class genericClass, final Type typeArg) { return String.format("%s<%s>", className(genericClass), toStrGenericType(typeArg, true)); } private final Set _importedShort = new TreeSet(); private final Set _importedFull = new TreeSet(); /** * Import the class and give the short name if possible */ private String className(final Class c) { if (c.isPrimitive()) return c.getCanonicalName(); if (_importedFull.contains(c.getCanonicalName())) return c.getSimpleName(); final String simple = c.getSimpleName(); if (_importedShort.contains(simple)) return c.getCanonicalName(); // can't import, name conflict _importedFull.add(c.getCanonicalName()); _importedShort.add(c.getSimpleName()); return c.getSimpleName(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy