From e4cd1f6034bddfcfc54b1f179519ab69142e7489 Mon Sep 17 00:00:00 2001 From: Harald Pehl Date: Tue, 10 Mar 2015 17:47:09 +0100 Subject: [PATCH] Refactored to use the action type as parameter for the process methods --- README.md | 12 +- .../jboss/gwt/circuit/meta/ActionType.java | 37 -- .../org/jboss/gwt/circuit/meta/Process.java | 14 +- pom.xml | 49 ++- processor/pom.xml | 20 +- .../AbstractErrorAbsorbingProcessor.java | 143 -------- .../circuit/processor/AbstractGenerator.java | 22 +- .../processor/GenerationException.java | 15 +- .../gwt/circuit/processor/GenerationUtil.java | 78 +--- .../gwt/circuit/processor/ProcessInfo.java | 50 +-- .../gwt/circuit/processor/StoreProcessor.java | 335 +++++++++--------- .../javax.annotation.processing.Processor | 1 - .../gwt/circuit/processor/templates/Store.ftl | 10 +- .../circuit/sample/bookstore/BookStore.java | 6 +- .../sample/bookstore/BookStoreTest.java | 1 + samples/todo/pom.xml | 13 + .../sample/todo/client/stores/TodoStore.java | 27 +- .../sample/todo/client/stores/UserStore.java | 17 +- 18 files changed, 331 insertions(+), 519 deletions(-) delete mode 100644 meta/src/main/java/org/jboss/gwt/circuit/meta/ActionType.java delete mode 100644 processor/src/main/java/org/jboss/gwt/circuit/processor/AbstractErrorAbsorbingProcessor.java delete mode 100644 processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor diff --git a/README.md b/README.md index c733281..feb51d2 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ public class TodoStore { private final List todos; @Process(actionType = SaveTodo.class) - public void onSave(final Todo todo, final Dispatcher.Channel channel) { + public void onSave(final SaveTodo todo, final Dispatcher.Channel channel) { // persist the todo (backend call) } } @@ -139,7 +139,7 @@ Circuit allows you to express dependencies between Stores on the level of an Act ```java public class TodoStore { @Process(actionType = RemoveUser.class, dependencies = {UserStore.class}) - public void onRemoveUser(String user, final Dispatcher.Channel channel) { + public void onRemoveUser(RemoveUser user, final Dispatcher.Channel channel) { // when a user is removed we removes his todo's [...] } @@ -172,10 +172,10 @@ When Stores complete the processing of an Action, they acknowledge the Action th public class TodoStore { @Process(actionType = SaveTodo.class) - public void onSave(final Todo todo, final Dispatcher.Channel channel) { + public void onSave(final SaveTodo todo, final Dispatcher.Channel channel) { // async invocation - todoService.save(todo, new TodoCallback(channel) { + todoService.save(todo.getTodo(), new TodoCallback(channel) { @Override public void onSuccess(final Void result) { // acknowledgement @@ -248,8 +248,8 @@ public class ShoesStore { The signature for methods annotated with `@Process` must adhere the following rules: - The return type must be `void` -- Parameters 0..n-1 map to the action's payload. For each parameter there must be a getter in the action. -- The last parameter (or the only one, if no payload is referenced) must be of type `org.jboss.gwt.circuit.Dispatcher.Channel`. +- The last parameter (or the only one, if no action is referenced) must be of type `org.jboss.gwt.circuit.Dispatcher.Channel`. +- The first parameter can be the action which is processed. To see the annotations in action take a look at the [wardrobe](samples/wardrobe) and [todo](samples/todo) samples. diff --git a/meta/src/main/java/org/jboss/gwt/circuit/meta/ActionType.java b/meta/src/main/java/org/jboss/gwt/circuit/meta/ActionType.java deleted file mode 100644 index d1d0223..0000000 --- a/meta/src/main/java/org/jboss/gwt/circuit/meta/ActionType.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package org.jboss.gwt.circuit.meta; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Marks the annotated class as the payload of an action. - */ -@Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE}) -public @interface ActionType { -} diff --git a/meta/src/main/java/org/jboss/gwt/circuit/meta/Process.java b/meta/src/main/java/org/jboss/gwt/circuit/meta/Process.java index 4e2d259..1ce7739 100644 --- a/meta/src/main/java/org/jboss/gwt/circuit/meta/Process.java +++ b/meta/src/main/java/org/jboss/gwt/circuit/meta/Process.java @@ -21,6 +21,8 @@ */ package org.jboss.gwt.circuit.meta; +import org.jboss.gwt.circuit.Action; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,18 +30,18 @@ /** * Marks a method within a class annotated with {@link Store} as the method which should receive actions. - * The method must return void and have at least one parameter: - *
    + * The method must return void and must match one of these signatures: + *
      *
    1. Action w/o payload: A single parameter of type {@link org.jboss.gwt.circuit.Dispatcher.Channel} is required
    2. - *
    3. Action with payload: Parameters 0..n-1 are the action's payload, the last parameter must be - * the {@link org.jboss.gwt.circuit.Dispatcher.Channel}
    4. - *
+ *
  • Action with payload: Two parameters. The first is the action, the second the + * {@link org.jboss.gwt.circuit.Dispatcher.Channel}
  • + * */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Process { - Class actionType(); + Class actionType(); Class[] dependencies() default {}; } diff --git a/pom.xml b/pom.xml index 4baa419..77bdeb2 100644 --- a/pom.xml +++ b/pom.xml @@ -75,15 +75,15 @@ 1.7 1.7 - 1.2 - 3.0.0.Final - 2.3.20 - 2.6.1 - - 17.0 + 1.0-rc2 + 1.0-SNAPSHOT + 1.2 + 2.3.22 + 2.7.0 + 18.0 0.9.2-hal - 4.11 + 4.12 @@ -118,6 +118,16 @@ cdi-api ${enterprise.cdi.version} + + com.google.auto.service + auto-service + ${auto-service.version} + + + com.google.auto + auto-common + ${auto-common.version} + @@ -126,23 +136,12 @@ ${jgrapht.version} - - - org.jboss.errai.bom - errai-version-master - ${errai.version} - pom - import - + - org.jboss.errai - errai-parent - ${errai.version} - pom - import + com.google.gwt + gwt-user + ${gwt.version} - - com.google.guava guava-gwt @@ -153,11 +152,6 @@ guava ${guava.version} - - com.google.gwt - gwt-user - ${gwt.version} - @@ -214,5 +208,4 @@ - diff --git a/processor/pom.xml b/processor/pom.xml index 97988e3..42d0094 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -46,21 +46,17 @@ com.google.guava guava + + com.google.auto.service + auto-service + + + com.google.auto + auto-common + org.freemarker freemarker - - - - - org.apache.maven.plugins - maven-compiler-plugin - - -proc:none - - - - \ No newline at end of file diff --git a/processor/src/main/java/org/jboss/gwt/circuit/processor/AbstractErrorAbsorbingProcessor.java b/processor/src/main/java/org/jboss/gwt/circuit/processor/AbstractErrorAbsorbingProcessor.java deleted file mode 100644 index 4909b9e..0000000 --- a/processor/src/main/java/org/jboss/gwt/circuit/processor/AbstractErrorAbsorbingProcessor.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package org.jboss.gwt.circuit.processor; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.util.Set; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.RoundEnvironment; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import javax.lang.model.element.Name; -import javax.lang.model.element.TypeElement; -import javax.tools.Diagnostic; -import javax.tools.JavaFileObject; - -/** - * Contains a series of adaptations and workarounds to make annotation processors work well under Eclipse JDT APT. Does - * not limit compatibility with other annotation processing environments (such as javac). - */ -public abstract class AbstractErrorAbsorbingProcessor extends AbstractProcessor { - - private Throwable rememberedInitError; - - protected AbstractErrorAbsorbingProcessor() { - try { - freemarker.log.Logger.selectLoggerLibrary(freemarker.log.Logger.LIBRARY_NONE); - } catch (ClassNotFoundException e) { - rememberedInitError = e; - } - } - - @Override - public final boolean process(Set annotations, RoundEnvironment roundEnv) { - try { - if (rememberedInitError != null) { - throw rememberedInitError; - } - return processWithExceptions(annotations, roundEnv); - } catch (Throwable e) { - // eclipse JDT goes into an infinite loop when the annotation processor throws any exception - // so we have to catch EVERYTHING, even Errors. - - StringWriter stringWriter = new StringWriter(); - e.printStackTrace(new PrintWriter(stringWriter)); - final String errorMessage = "Internal error in " + getClass().getName() + stringWriter.toString(); - - boolean emittedSpecificError = false; - for (TypeElement annotation : annotations) { - for (Element annotationTarget : roundEnv.getElementsAnnotatedWith(annotation)) { - processingEnv.getMessager().printMessage( - Diagnostic.Kind.ERROR, - errorMessage, - annotationTarget, - findAnnotationMirror(annotationTarget, annotation)); - emittedSpecificError = true; - } - } - - // if the above loop caught nothing, the type we were called for didn't contain an annotation - // we handle (maybe it was inherited). In this case, we'll just emit a non-location-specific error - // so there is at least some sort of diagnostic message for the user to go on! - if (!emittedSpecificError) { - processingEnv.getMessager().printMessage( - Diagnostic.Kind.ERROR, - errorMessage); - } - - return false; - } - } - - private static AnnotationMirror findAnnotationMirror(Element target, TypeElement annotationType) { - final Name annotationTypeName = annotationType.getQualifiedName(); - for (AnnotationMirror am : target.getAnnotationMirrors()) { - if (GenerationUtil.getQualifiedName(am).contentEquals(annotationTypeName)) { - return am; - } - } - return null; - } - - /** - * Subclasses must call this from their constructors if something throws an - * exception during initialization of the instance. Once this method has - * been called with a non-null throwable, the - * {@link #processWithExceptions(Set, RoundEnvironment)} method will not be - * called on this instance. - * - * @param t the exception that occurred (and was caught) during instance - * creation of this annotation processor instance. - */ - protected void rememberInitializationError(Throwable t) { - rememberedInitError = t; - } - - /** - * Same contract as {@link #process(Set, RoundEnvironment)}, except that any - * exceptions thrown are caught and printed as messages of type - * {@link javax.tools.Diagnostic.Kind#ERROR}. This is done to keep Eclipse JDT from going into an - * infinite processing loop. - */ - protected abstract boolean processWithExceptions( - Set annotations, RoundEnvironment roundEnv) throws Exception; - - /** - * Writes the given code to javac's Filer. - */ - protected final void writeCode(final String packageName, final String className, - final StringBuffer code) - throws IOException { - - JavaFileObject jfo = processingEnv.getFiler().createSourceFile(packageName + "." + className); - Writer w = jfo.openWriter(); - BufferedWriter bw = new BufferedWriter(w); - bw.append(code); - bw.close(); - w.close(); - } -} diff --git a/processor/src/main/java/org/jboss/gwt/circuit/processor/AbstractGenerator.java b/processor/src/main/java/org/jboss/gwt/circuit/processor/AbstractGenerator.java index 37b9ffe..d05bd6f 100644 --- a/processor/src/main/java/org/jboss/gwt/circuit/processor/AbstractGenerator.java +++ b/processor/src/main/java/org/jboss/gwt/circuit/processor/AbstractGenerator.java @@ -21,29 +21,29 @@ */ package org.jboss.gwt.circuit.processor; +import freemarker.template.Configuration; +import freemarker.template.DefaultObjectWrapperBuilder; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.Version; + import java.io.BufferedWriter; import java.io.IOException; import java.io.StringWriter; import java.util.Map; -import freemarker.template.Configuration; -import freemarker.template.DefaultObjectWrapper; -import freemarker.template.Template; -import freemarker.template.TemplateException; - abstract class AbstractGenerator { protected final Configuration config; protected AbstractGenerator() { - config = new Configuration(); + Version version = new Version(2, 3, 22); + config = new Configuration(version); config.setDefaultEncoding("UTF-8"); config.setClassForTemplateLoading(getClass(), "templates"); - config.setObjectWrapper(new DefaultObjectWrapper()); + config.setObjectWrapper(new DefaultObjectWrapperBuilder(version).build()); } - // TODO If we can use Java 8 change this to - // public StringBuffer generate(java.util.function.Supplier> contextSupplier, String templateName) throws GenerationException protected StringBuffer generate(Map context, String templateName) throws GenerationException { final StringWriter sw = new StringWriter(); final BufferedWriter bw = new BufferedWriter(sw); @@ -52,14 +52,14 @@ protected StringBuffer generate(Map context, String templateName // template.complete(contextSupplier.get(), bw); template.process(context, bw); } catch (IOException | TemplateException ioe) { - throw new GenerationException(ioe); + throw new GenerationException("Error generating template " + templateName + ": " + ioe.getMessage()); } finally { try { bw.close(); sw.close(); } catch (IOException ioe) { //noinspection ThrowFromFinallyBlock - throw new GenerationException(ioe); + throw new GenerationException("Error generating template " + templateName + ": " + ioe.getMessage()); } } return sw.getBuffer(); diff --git a/processor/src/main/java/org/jboss/gwt/circuit/processor/GenerationException.java b/processor/src/main/java/org/jboss/gwt/circuit/processor/GenerationException.java index 6d53236..a9516fc 100644 --- a/processor/src/main/java/org/jboss/gwt/circuit/processor/GenerationException.java +++ b/processor/src/main/java/org/jboss/gwt/circuit/processor/GenerationException.java @@ -21,13 +21,22 @@ */ package org.jboss.gwt.circuit.processor; -public class GenerationException extends Exception { +import javax.lang.model.element.Element; + +public class GenerationException extends RuntimeException { + + private final Element element; public GenerationException(final String msg) { + this(null, msg); + } + + public GenerationException(final Element element, final String msg) { super(msg); + this.element = element; } - public GenerationException(Throwable t) { - super(t); + public Element getElement() { + return element; } } diff --git a/processor/src/main/java/org/jboss/gwt/circuit/processor/GenerationUtil.java b/processor/src/main/java/org/jboss/gwt/circuit/processor/GenerationUtil.java index 2660aee..9c8a57a 100644 --- a/processor/src/main/java/org/jboss/gwt/circuit/processor/GenerationUtil.java +++ b/processor/src/main/java/org/jboss/gwt/circuit/processor/GenerationUtil.java @@ -22,7 +22,13 @@ package org.jboss.gwt.circuit.processor; import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.*; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; @@ -44,52 +50,6 @@ private GenerationUtil() {} */ static final String[] ANY_PARAMS = new String[0]; - static String storeImplementation(String delegate) { - return delegate + "Adapter"; - } - - static List findGetter(final TypeElement originalClassElement, final ProcessingEnvironment processingEnvironment, - final TypeMirror requiredReturnType, final String name) { - - final Types typeUtils = processingEnvironment.getTypeUtils(); - final String getter = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); - - TypeElement classElement = originalClassElement; - while (true) { - final List methods = ElementFilter.methodsIn(classElement.getEnclosedElements()); - - List matches = new ArrayList<>(); - for (ExecutableElement e : methods) { - if (!e.getSimpleName().toString().equals(getter)) { - continue; - } - final TypeMirror actualReturnType = e.getReturnType(); - if (!typeUtils.isAssignable(actualReturnType, requiredReturnType)) { - continue; - } - if (e.getModifiers().contains(Modifier.STATIC)) { - continue; - } - if (e.getModifiers().contains(Modifier.PRIVATE)) { - continue; - } - matches.add(e); - } - - if (!matches.isEmpty()) { - return matches; - } - - TypeMirror superclass = classElement.getSuperclass(); - if (superclass instanceof DeclaredType) { - classElement = (TypeElement) ((DeclaredType) superclass).asElement(); - } else { - break; - } - } - return Collections.emptyList(); - } - static List getAnnotatedMethods(final TypeElement originalClassElement, final ProcessingEnvironment processingEnvironment, final String annotationName, final TypeMirror requiredReturnType, final String[] requiredParameterTypes) { @@ -150,18 +110,6 @@ static Name getQualifiedName(AnnotationMirror annotation) { return ((TypeElement) annotation.getAnnotationType().asElement()).getQualifiedName(); } - static Collection extractValue(final AnnotationValue value) { - if (value.getValue() instanceof Collection) { - final Collection varray = (List) value.getValue(); - final ArrayList result = new ArrayList<>(varray.size()); - for (final Object active : varray) { - result.addAll(extractValue((AnnotationValue) active)); - } - return result; - } - return Collections.singleton(value.getValue().toString()); - } - static boolean doParametersMatch(final Types typeUtils, final Elements elementUtils, final ExecutableElement e, @@ -186,4 +134,16 @@ static boolean doParametersMatch(final Types typeUtils, } return true; } + + static Collection extractValue(final AnnotationValue value) { + if (value.getValue() instanceof Collection) { + final Collection varray = (List) value.getValue(); + final ArrayList result = new ArrayList<>(varray.size()); + for (final Object active : varray) { + result.addAll(extractValue((AnnotationValue) active)); + } + return result; + } + return Collections.singleton(value.getValue().toString()); + } } diff --git a/processor/src/main/java/org/jboss/gwt/circuit/processor/ProcessInfo.java b/processor/src/main/java/org/jboss/gwt/circuit/processor/ProcessInfo.java index 7c0e4df..cfd6d39 100644 --- a/processor/src/main/java/org/jboss/gwt/circuit/processor/ProcessInfo.java +++ b/processor/src/main/java/org/jboss/gwt/circuit/processor/ProcessInfo.java @@ -21,26 +21,27 @@ */ package org.jboss.gwt.circuit.processor; -import java.util.*; +import javax.lang.model.element.ExecutableElement; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; public class ProcessInfo { - private final String method; + private final ExecutableElement methodElement; // used only for error reporting private final String actionType; - private final List payload; + private final String method; + private final int params; private Set dependencies; - public ProcessInfo(final String method, String actionType) { - this.method = method; + public ProcessInfo(String actionType, ExecutableElement method) { + this.methodElement = method; this.actionType = actionType; - this.payload = new ArrayList<>(); + this.method = method.getSimpleName().toString(); + this.params = method.getParameters().size(); this.dependencies = new HashSet<>(); } - public boolean isSingleArg() { - return payload.isEmpty(); - } - public String getMethod() { return method; } @@ -49,12 +50,8 @@ public String getActionType() { return actionType; } - public void addPayload(String name) { - payload.add(name); - } - - public List getPayload() { - return payload; + public boolean isSingleArg() { + return params == 1; } public void addDependency(String storeClassName) { @@ -77,23 +74,30 @@ public String getDependencies() { return csv.toString(); } + ExecutableElement getMethodElement() { + return methodElement; + } + @Override - public boolean equals(final Object o) { - if (this == o) { return true; } - if (!(o instanceof ProcessInfo)) { return false; } + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ProcessInfo)) return false; ProcessInfo that = (ProcessInfo) o; - if (!method.equals(that.method)) { return false; } - if (!payload.equals(that.payload)) { return false; } + if (params != that.params) return false; + if (!method.equals(that.method)) return false; + if (!actionType.equals(that.actionType)) return false; + return dependencies.equals(that.dependencies); - return true; } @Override public int hashCode() { int result = method.hashCode(); - result = 31 * result + payload.hashCode(); + result = 31 * result + actionType.hashCode(); + result = 31 * result + params; + result = 31 * result + dependencies.hashCode(); return result; } } diff --git a/processor/src/main/java/org/jboss/gwt/circuit/processor/StoreProcessor.java b/processor/src/main/java/org/jboss/gwt/circuit/processor/StoreProcessor.java index 7f315a8..bd2f2db 100644 --- a/processor/src/main/java/org/jboss/gwt/circuit/processor/StoreProcessor.java +++ b/processor/src/main/java/org/jboss/gwt/circuit/processor/StoreProcessor.java @@ -21,6 +21,11 @@ */ package org.jboss.gwt.circuit.processor; +import com.google.auto.common.AnnotationMirrors; +import com.google.auto.common.MoreElements; +import com.google.auto.common.MoreTypes; +import com.google.auto.service.AutoService; +import com.google.common.base.Optional; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import org.jboss.gwt.circuit.ChangeSupport; @@ -32,18 +37,21 @@ import org.jgrapht.graph.DefaultDirectedGraph; import org.jgrapht.graph.DefaultEdge; -import javax.annotation.processing.Messager; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.annotation.processing.SupportedSourceVersion; +import javax.annotation.processing.*; import javax.lang.model.SourceVersion; -import javax.lang.model.element.*; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.NoType; import javax.lang.model.type.TypeKind; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; -import javax.tools.Diagnostic; import javax.tools.FileObject; +import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import java.io.BufferedWriter; import java.io.IOException; @@ -54,9 +62,10 @@ import static javax.tools.Diagnostic.Kind.NOTE; import static org.jboss.gwt.circuit.processor.GenerationUtil.ANY_PARAMS; +@AutoService(Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_7) @SupportedAnnotationTypes("org.jboss.gwt.circuit.meta.Store") -public class StoreProcessor extends AbstractErrorAbsorbingProcessor { +public class StoreProcessor extends AbstractProcessor { static final String GRAPH_VIZ_OUTPUT = "dependencies.gv"; @@ -64,6 +73,11 @@ public class StoreProcessor extends AbstractErrorAbsorbingProcessor { private final Map> dagValidation; private final List metadata; + private Types typeUtils; + private Elements elementUtils; + private Filer filer; + private Messager messager; + public StoreProcessor() { graphVizInfos = new HashMap<>(); dagValidation = new HashMap<>(); @@ -71,139 +85,122 @@ public StoreProcessor() { } @Override - protected boolean processWithExceptions(final Set annotations, - final RoundEnvironment roundEnv) throws Exception { - - if (roundEnv.errorRaised()) { - return false; - } - if (!roundEnv.processingOver()) { - collectData(roundEnv); - } else { - generateFiles(); - } - return true; + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + typeUtils = processingEnv.getTypeUtils(); + elementUtils = processingEnv.getElementUtils(); + filer = processingEnv.getFiler(); + messager = processingEnv.getMessager(); } - - // ------------------------------------------------------ collect methods - - private void collectData(final RoundEnvironment roundEnv) throws Exception { - - final Messager messager = processingEnv.getMessager(); - final Types typeUtils = processingEnv.getTypeUtils(); - final Elements elementUtils = processingEnv.getElementUtils(); - + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { for (Element e : roundEnv.getElementsAnnotatedWith(Store.class)) { + + // collect data for *one* store TypeElement storeElement = (TypeElement) e; PackageElement packageElement = (PackageElement) storeElement.getEnclosingElement(); - - final String packageName = packageElement.getQualifiedName().toString(); - final String storeDelegate = storeElement.getSimpleName().toString(); - final boolean changeSupport = typeUtils.isAssignable(storeElement.asType(), + String packageName = packageElement.getQualifiedName().toString(); + String storeDelegate = storeElement.getSimpleName().toString(); + boolean changeSupport = typeUtils.isAssignable(storeElement.asType(), elementUtils.getTypeElement(ChangeSupport.class.getName()).asType()); - final String storeClassName = GenerationUtil.storeImplementation(storeDelegate); - messager.printMessage(NOTE, - String.format("Discovered annotated store [%s]", storeElement.getQualifiedName())); - - List processMethods = new ArrayList<>(); - if (findValidProcessMethods(messager, typeUtils, storeElement, processMethods)) { - Collection processInfos = createProcessInfos(messager, typeUtils, elementUtils, - storeElement, processMethods); + String storeClassName = storeDelegate + "Adapter"; + info("Discovered annotated store [%s]", storeElement.getQualifiedName()); + try { + List processMethods = findValidProcessMethods(storeElement); + Collection processInfos = createProcessInfos(storeElement, processMethods); + validateProcessMethods(storeElement, processInfos); metadata.add(new StoreDelegateMetadata(packageName, storeClassName, storeDelegate, changeSupport, processInfos)); - } else { - // no valid process methods! - messager.printMessage(ERROR, - String.format("%s does not contain suitable methods annotated with %s.", - storeElement.getQualifiedName(), Process.class.getName())); - break; + } catch (GenerationException ge) { + error(ge); + return true; + } + } + + // generate code for *all* stores + try { + for (StoreDelegateMetadata md : metadata) { + info("Generating code for [%s]", md.storeClassName); + StoreGenerator generator = new StoreGenerator(); + final StringBuffer code = generator.generate(md); + writeCode(md.packageName, md.storeClassName, code); + info("Successfully generated store implementation [%s]", md.storeClassName); + } + metadata.clear(); + if (roundEnv.processingOver()) { + String graphVizFile = writeGraphViz(); + validateDAG(graphVizFile); } + } catch (IOException e) { + error("Error generating code: %s", e.getMessage()); } + return true; } - private boolean findValidProcessMethods(final Messager messager, final Types typeUtils, - final TypeElement storeElement, List processMethods) { - boolean valid = true; + // ------------------------------------------------------ collect methods + + private List findValidProcessMethods(final TypeElement storeElement) + throws NoSuchElementException { NoType voidType = typeUtils.getNoType(TypeKind.VOID); List allProcessMethods = GenerationUtil.getAnnotatedMethods(storeElement, processingEnv, Process.class.getName(), voidType, ANY_PARAMS); if (allProcessMethods.isEmpty()) { - messager.printMessage(ERROR, String.format( - "No process methods found in [%s]. Please use @%s to mark one or several methods as process methods.", - storeElement.getQualifiedName(), Process.class.getName())); - valid = false; + // no valid process methods! + throw new GenerationException(storeElement, + String.format("%s does not contain suitable methods annotated with %s.", + storeElement.getQualifiedName(), Process.class.getName())); } - for (ExecutableElement processMethod : allProcessMethods) { - processMethods.add(processMethod); - } - return valid; + return allProcessMethods; } - private Collection createProcessInfos(final Messager messager, final Types typeUtils, - Elements elementUtils, final TypeElement storeElement, - final List processMethods) - throws GenerationException { - - final List processInfos = new LinkedList<>(); + private Collection createProcessInfos(final TypeElement storeElement, + final List processMethods) { + final List processInfos = new ArrayList<>(); final String storeDelegate = storeElement.getSimpleName().toString(); for (ExecutableElement methodElement : processMethods) { - String actionType = Void.class.getCanonicalName(); - Collection dependencies = Collections.emptySet(); - AnnotationMirror processAnnotation = GenerationUtil.getAnnotation(elementUtils, methodElement, Process.class.getName()); - for (Map.Entry entry : processAnnotation.getElementValues().entrySet()) { - if ("dependencies".equals(entry.getKey().getSimpleName().toString())) { - dependencies = GenerationUtil.extractValue(entry.getValue()); - } else if ("actionType".equals(entry.getKey().getSimpleName().toString())) { - actionType = (String) ((Set) GenerationUtil.extractValue(entry.getValue())).iterator().next(); + String actionType = null; + Collection dependencies = Collections.emptyList(); + + // parse @Process parameter + Optional processAnnotation = MoreElements.getAnnotationMirror(methodElement, Process.class); + if (processAnnotation.isPresent()) { + Map values = AnnotationMirrors.getAnnotationValuesWithDefaults(processAnnotation.get()); + for (Map.Entry entry : values.entrySet()) { + if ("actionType".equals(entry.getKey().getSimpleName().toString())) { + actionType = (String) ((Set) GenerationUtil.extractValue(entry.getValue())).iterator().next(); + } else if ("dependencies".equals(entry.getKey().getSimpleName().toString())) { + dependencies = GenerationUtil.extractValue(entry.getValue()); + } } } - - TypeElement actionTypeElement = elementUtils.getTypeElement(actionType); - ProcessInfo processInfo = new ProcessInfo(methodElement.getSimpleName().toString(), actionType); + assert actionType != null; + ProcessInfo processInfo = new ProcessInfo(actionType, methodElement); processInfos.add(processInfo); + + // collect dependencies for (String store : dependencies) { // IMPORTANT: The actual dependency is the store adaptee! processInfo.addDependency(store + ".class"); } + // analyze the process signature List parameters = methodElement.getParameters(); - if (parameters.size() == 1) { - // if a single param is used it needs to be the dispatcher channel - verifyDispatcherChannel(messager, typeUtils, storeElement, methodElement, parameters.get(0)); - continue; - - } else if (parameters.size() > 1) { - // parameters 1..n-1 are payload, the last one is the dispatcher channel - for (int i = 0; i < parameters.size(); i++) { - if (i == parameters.size() - 1) { - verifyDispatcherChannel(messager, typeUtils, storeElement, methodElement, parameters.get(i)); - } else { - VariableElement parameter = parameters.get(i); - String payloadName = parameter.getSimpleName().toString(); - - // Check getter in action type - List getter = GenerationUtil.findGetter(actionTypeElement, processingEnv, parameter.asType(), payloadName); - if (getter.isEmpty()) { - String error = String.format("No getter found for payload parameter '%s' on method '%s' in class '%s'", - payloadName, methodElement.getSimpleName(), storeElement.getSimpleName()); - messager.printMessage(Diagnostic.Kind.ERROR, error); - continue; - } - processInfo.addPayload(getter.get(0).getSimpleName().toString()); - } - } - + if (parameters.size() == 2) { + // first parameter is the action, the second parameter the dispatcher channel + verifyProcessParameter(storeElement, methodElement, parameters.get(0), actionType); + verifyProcessParameter(storeElement, methodElement, parameters.get(1), Dispatcher.Channel.class.getCanonicalName()); + } else if (parameters.size() == 1) { + // if a single param is used it has to be the dispatcher channel + verifyProcessParameter(storeElement, methodElement, parameters.get(0), Dispatcher.Channel.class.getCanonicalName()); } else { // anything else is considered as an error - String error = String.format( - "No valid process method '%s' in class '%s'. Please provide at least a parameter of type '%s'", - methodElement.getSimpleName(), storeElement.getSimpleName(), Dispatcher.Channel.class.getSimpleName()); - messager.printMessage(Diagnostic.Kind.ERROR, error); - continue; + throw new GenerationException(methodElement, + String.format("Illegal number of arguments on method '%s' in class '%s'", + methodElement.getSimpleName(), storeElement.getSimpleName())); } // record dependencies in a different data structures to generate GraphViz... @@ -230,73 +227,43 @@ private Collection createProcessInfos(final Messager messager, fina } dag.putAll(storeDelegate, simpleDependencies); } - return processInfos; } - private void verifyDispatcherChannel(final Messager messager, final Types typeUtils, - TypeElement storeElement, ExecutableElement methodElement, VariableElement param) { - TypeElement paramType = (TypeElement) typeUtils.asElement(param.asType()); - if (!paramType.getQualifiedName().toString().equals(Dispatcher.Channel.class.getCanonicalName())) { - String error = String.format( - "Illegal type for parameter '%s' on method '%s' in class '%s'. Expected type '%s'", - param.getSimpleName(), methodElement.getSimpleName(), storeElement.getSimpleName(), - Dispatcher.Channel.class.getCanonicalName()); - messager.printMessage(Diagnostic.Kind.ERROR, error); - } - } + // ------------------------------------------------------ verify methods - // ------------------------------------------------------ generate methods - - private void generateFiles() throws Exception { - final Messager messager = processingEnv.getMessager(); - - // store delegates - for (StoreDelegateMetadata md : metadata) { - try { - messager.printMessage(NOTE, String.format("Generating code for [%s]", md.storeClassName)); - StoreGenerator generator = new StoreGenerator(); - final StringBuffer code = generator.generate(md); - writeCode(md.packageName, md.storeClassName, code); - - messager.printMessage(NOTE, - String.format("Successfully generated store implementation [%s]", md.storeClassName)); - } catch (GenerationException ge) { - final String msg = ge.getMessage(); - messager.printMessage(Diagnostic.Kind.ERROR, msg/* , storeElement*/); - } + private void verifyProcessParameter(TypeElement storeElement, ExecutableElement methodElement, + VariableElement parameter, String expected) { + TypeElement parameterType = MoreTypes.asTypeElement(typeUtils, parameter.asType()); + if (!parameterType.getQualifiedName().toString().equals(expected)) { + throw new GenerationException(parameter, + String.format("Illegal parameter '%s' on method '%s' in class '%s'. Expected type '%s'", + parameter.getSimpleName(), methodElement.getSimpleName(), storeElement.getSimpleName(), + expected)); } - - // GraphVIZ - String graphVizFile = writeGraphViz(); - validateDAG(graphVizFile); } - private String writeGraphViz() throws GenerationException, IOException { - final Messager messager = processingEnv.getMessager(); - GraphVizGenerator generator = new GraphVizGenerator(); - StringBuffer code = generator.generate(graphVizInfos.values()); - messager.printMessage(NOTE, - "Generating GraphViz file to visualize store dependencies [" + GRAPH_VIZ_OUTPUT + "]"); - FileObject fo = processingEnv.getFiler() - .createResource(StandardLocation.SOURCE_OUTPUT, "", GRAPH_VIZ_OUTPUT); - Writer w = fo.openWriter(); - BufferedWriter bw = new BufferedWriter(w); - bw.append(code); - bw.close(); - w.close(); - messager.printMessage(NOTE, "Successfully generated GraphViz file [" + GRAPH_VIZ_OUTPUT + "]"); - return fo.getName(); + private void validateProcessMethods(TypeElement storeElement, Collection processInfos) { + Map actionTypes = new HashMap<>(); + for (ProcessInfo processInfo : processInfos) { + ProcessInfo otherPi = actionTypes.get(processInfo.getActionType()); + if (otherPi != null) { + throw new GenerationException(processInfo.getMethodElement(), + String.format("Ambiguous process method %s in store %s. This method uses the same action type as %s. " + + "Please make sure that the action type is unique across all process method in one store.", + processInfo.getMethod(), storeElement.getSimpleName().toString(), otherPi.getMethod())); + } + actionTypes.put(processInfo.getActionType(), processInfo); + } } - private void validateDAG(final String graphVizFile) throws GenerationException { + private void validateDAG(final String graphVizFile) { boolean cyclesFound = false; - final Messager messager = processingEnv.getMessager(); for (Map.Entry> entry : dagValidation.entrySet()) { String payload = entry.getKey(); Multimap dependencies = entry.getValue(); - messager.printMessage(NOTE, "Check cyclic dependencies for action [" + payload + "]"); + info("Check cyclic dependencies for action [%s]", payload); DirectedGraph dg = new DefaultDirectedGraph<>(DefaultEdge.class); // vertices @@ -325,13 +292,57 @@ private void validateDAG(final String graphVizFile) throws GenerationException { cycleInfo.append(cycle).append(" -> "); } cycleInfo.append(cycles.get(0)); - messager.printMessage(ERROR, - "Cyclic dependencies detected for action [" + payload + "]: " + cycleInfo); - messager.printMessage(ERROR, "Please review [" + graphVizFile + "] for more details."); + error("Cyclic dependencies detected for action [%s]: %s. Please review [%s] for more details.", + payload, cycleInfo, graphVizFile); } if (!cyclesFound) { - messager.printMessage(NOTE, "No cyclic dependencies found for action [" + payload + "]"); + info("No cyclic dependencies found for action [%s]", payload); } } } + + // ------------------------------------------------------ generate methods + + private void writeCode(final String packageName, final String className, final StringBuffer code) + throws IOException { + JavaFileObject jfo = filer.createSourceFile(packageName + "." + className); + Writer w = jfo.openWriter(); + BufferedWriter bw = new BufferedWriter(w); + bw.append(code); + bw.close(); + w.close(); + } + + private String writeGraphViz() throws IOException { + GraphVizGenerator generator = new GraphVizGenerator(); + StringBuffer code = generator.generate(graphVizInfos.values()); + info("Generating GraphViz file to visualize store dependencies [%s]", GRAPH_VIZ_OUTPUT); + FileObject fo = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", GRAPH_VIZ_OUTPUT); + Writer w = fo.openWriter(); + BufferedWriter bw = new BufferedWriter(w); + bw.append(code); + bw.close(); + w.close(); + info("Successfully generated GraphViz file [%s]", GRAPH_VIZ_OUTPUT); + return fo.getName(); + } + + + // ------------------------------------------------------ logging + + private void info(String msg, Object... args) { + messager.printMessage(NOTE, String.format(msg, args)); + } + + private void error(String msg, Object... args) { + messager.printMessage(ERROR, String.format(msg, args)); + } + + private void error(GenerationException generationException) { + if (generationException.getElement() != null) { + messager.printMessage(ERROR, generationException.getMessage(), generationException.getElement()); + } else { + messager.printMessage(ERROR, generationException.getMessage()); + } + } } diff --git a/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor deleted file mode 100644 index 9b1077d..0000000 --- a/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ /dev/null @@ -1 +0,0 @@ -org.jboss.gwt.circuit.processor.StoreProcessor diff --git a/processor/src/main/resources/org/jboss/gwt/circuit/processor/templates/Store.ftl b/processor/src/main/resources/org/jboss/gwt/circuit/processor/templates/Store.ftl index 6dd1a48..047f1bb 100644 --- a/processor/src/main/resources/org/jboss/gwt/circuit/processor/templates/Store.ftl +++ b/processor/src/main/resources/org/jboss/gwt/circuit/processor/templates/Store.ftl @@ -2,9 +2,10 @@ <#-- @ftlvariable name="storeClassName" type="java.lang.String" --> <#-- @ftlvariable name="storeDelegate" type="java.lang.String" --> <#-- @ftlvariable name="changeSupport" type="java.lang.Boolean" --> -<#-- @ftlvariable name="processInfos" type="java.util.List" --> +<#-- @ftlvariable name="processInfos" type="java.util.Collection" --> package ${packageName}; +import java.util.logging.Logger; import javax.annotation.Generated; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; @@ -22,6 +23,7 @@ import org.jboss.gwt.circuit.StoreCallback; @Generated("org.jboss.gwt.circuit.processor.StoreProcessor") public class ${storeClassName} { + private final static Logger LOG = Logger.getLogger("org.jboss.gwt.circuit"); private final ${storeDelegate} delegate; @Inject @@ -60,12 +62,12 @@ public class ${storeClassName} { <#if processInfo.singleArg> delegate.${processInfo.method}(channel); <#else> - delegate.${processInfo.method}(<#list processInfo.getPayload() as payload>((${processInfo.actionType})action).${payload}(), channel); + delegate.${processInfo.method}((${processInfo.actionType})action, channel); } else { - channel.nack("Warning: Unmatched action type " + action.getClass().getName() + " in store " + delegate.getClass()); + channel.nack("Unmatched action type " + action.getClass().getName() + " in store " + delegate.getClass()); } } @@ -83,7 +85,7 @@ public class ${storeClassName} { handler.onChange(action); } <#else> - System.out.println("WARN: Cannot signal change event: " + ${storeDelegate}.class.getName() + " does not extend " + org.jboss.gwt.circuit.ChangeSupport.class.getName()); + LOG.warning("Cannot signal change event: " + ${storeDelegate}.class.getName() + " does not extend " + org.jboss.gwt.circuit.ChangeSupport.class.getName()); } }); diff --git a/samples/bookstore/src/main/java/org/jboss/gwt/circuit/sample/bookstore/BookStore.java b/samples/bookstore/src/main/java/org/jboss/gwt/circuit/sample/bookstore/BookStore.java index 729f3ca..e341b08 100644 --- a/samples/bookstore/src/main/java/org/jboss/gwt/circuit/sample/bookstore/BookStore.java +++ b/samples/bookstore/src/main/java/org/jboss/gwt/circuit/sample/bookstore/BookStore.java @@ -40,8 +40,8 @@ public BookStore() { } @Process(actionType = Rate.class) - public void rate(int stars, Book book, Dispatcher.Channel channel) { - failSafeGet(book).add(stars); + public void rate(Rate rate, Dispatcher.Channel channel) { + failSafeGet(rate.getBook()).add(rate.getStars()); channel.ack(); } @@ -58,7 +58,7 @@ public double getRating(Book book) { return ratings.containsKey(book) ? ratings.get(book).avg() : 0.0; } - private final static class Rating { + final static class Rating { private final List ratings; private Rating() { diff --git a/samples/bookstore/src/test/java/org/jboss/gwt/circuit/sample/bookstore/BookStoreTest.java b/samples/bookstore/src/test/java/org/jboss/gwt/circuit/sample/bookstore/BookStoreTest.java index 271ba78..65182bf 100644 --- a/samples/bookstore/src/test/java/org/jboss/gwt/circuit/sample/bookstore/BookStoreTest.java +++ b/samples/bookstore/src/test/java/org/jboss/gwt/circuit/sample/bookstore/BookStoreTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class BookStoreTest { diff --git a/samples/todo/pom.xml b/samples/todo/pom.xml index a5f5d70..9160be6 100644 --- a/samples/todo/pom.xml +++ b/samples/todo/pom.xml @@ -40,9 +40,22 @@ war + 3.1.2.Final ${project.build.directory}/${project.build.finalName} + + + + org.jboss.errai.bom + errai-bom + ${errai.version} + pom + import + + + + org.jboss.gwt.circuit diff --git a/samples/todo/src/main/java/org/jboss/gwt/circuit/sample/todo/client/stores/TodoStore.java b/samples/todo/src/main/java/org/jboss/gwt/circuit/sample/todo/client/stores/TodoStore.java index 474e882..3c7ca63 100644 --- a/samples/todo/src/main/java/org/jboss/gwt/circuit/sample/todo/client/stores/TodoStore.java +++ b/samples/todo/src/main/java/org/jboss/gwt/circuit/sample/todo/client/stores/TodoStore.java @@ -46,6 +46,7 @@ public class TodoStore extends ChangeSupport { private final UserStore userStore; @Inject + @SuppressWarnings("CdiInjectionPointsInspection") public TodoStore(final TodoServiceAsync todoService, UserStore userStore) { this.todos = new LinkedList<>(); this.todoService = todoService; @@ -56,7 +57,7 @@ public TodoStore(final TodoServiceAsync todoService, UserStore userStore) { // ------------------------------------------------------ user actions @Process(actionType = SelectUser.class, dependencies = UserStore.class) - public void onSelectUser(String user, final Dispatcher.Channel channel) { + public void onSelectUser(SelectUser action, final Dispatcher.Channel channel) { // reset selection selectedTodo = null; @@ -64,9 +65,9 @@ public void onSelectUser(String user, final Dispatcher.Channel channel) { } @Process(actionType = RemoveUser.class, dependencies = UserStore.class) - public void onRemoveUser(String user, final Dispatcher.Channel channel) { + public void onRemoveUser(RemoveUser action, final Dispatcher.Channel channel) { - todoService.removeForUser(user, new TodoCallback(channel) { + todoService.removeForUser(action.getUser(), new TodoCallback(channel) { @Override public void onSuccess(Void v) { onList(channel); @@ -91,8 +92,8 @@ public void onSuccess(final Collection result) { } @Process(actionType = ResolveTodo.class) - public void onResolve(Todo todo, final Dispatcher.Channel channel) { - todoService.save(todo, new TodoCallback(channel) { + public void onResolve(ResolveTodo action, final Dispatcher.Channel channel) { + todoService.save(action.getTodo(), new TodoCallback(channel) { @Override public void onSuccess(final Void result) { onList(channel); @@ -101,19 +102,19 @@ public void onSuccess(final Void result) { } @Process(actionType = SelectTodo.class) - public void onSelect(final Todo todo, final Dispatcher.Channel channel) { - this.selectedTodo = todo; + public void onSelect(final SelectTodo action, final Dispatcher.Channel channel) { + this.selectedTodo = action.getTodo(); channel.ack(); } @Process(actionType = SaveTodo.class) - public void onStore(final Todo todo, final Dispatcher.Channel channel) { + public void onStore(final SaveTodo action, final Dispatcher.Channel channel) { String assignee = userStore.getSelectedUser() != null ? userStore.getSelectedUser() : Todo.USER_ANY; - todo.setUser(assignee); + action.getTodo().setUser(assignee); - todoService.save(todo, new TodoCallback(channel) { + todoService.save(action.getTodo(), new TodoCallback(channel) { @Override public void onSuccess(final Void result) { onList(channel); @@ -122,12 +123,12 @@ public void onSuccess(final Void result) { } @Process(actionType = RemoveTodo.class) - public void onRemove(final Todo todo, final Dispatcher.Channel channel) { + public void onRemove(final RemoveTodo action, final Dispatcher.Channel channel) { - todoService.delete(todo, new TodoCallback(channel) { + todoService.delete(action.getTodo(), new TodoCallback(channel) { @Override public void onSuccess(final Void result) { - if (todo.equals(selectedTodo)) { selectedTodo = null; } + if (action.getTodo().equals(selectedTodo)) { selectedTodo = null; } onList(channel); } }); diff --git a/samples/todo/src/main/java/org/jboss/gwt/circuit/sample/todo/client/stores/UserStore.java b/samples/todo/src/main/java/org/jboss/gwt/circuit/sample/todo/client/stores/UserStore.java index cbfc111..d491dfd 100644 --- a/samples/todo/src/main/java/org/jboss/gwt/circuit/sample/todo/client/stores/UserStore.java +++ b/samples/todo/src/main/java/org/jboss/gwt/circuit/sample/todo/client/stores/UserStore.java @@ -45,6 +45,7 @@ public class UserStore extends ChangeSupport { private String selectedUser; @Inject + @SuppressWarnings("CdiInjectionPointsInspection") private Dispatcher dispatcher; public UserStore() { @@ -63,26 +64,26 @@ public void onLoad(final Dispatcher.Channel channel) { } @Process(actionType = SelectUser.class) - public void onSelect(String user, final Dispatcher.Channel channel) { - this.selectedUser = user; + public void onSelect(SelectUser action, final Dispatcher.Channel channel) { + this.selectedUser = action.getUser(); channel.ack(); } @Process(actionType = AddUser.class) - public void onAdd(String user, final Dispatcher.Channel channel) { - if (!users.contains(user)) { this.users.add(user); } + public void onAdd(AddUser action, final Dispatcher.Channel channel) { + if (!users.contains(action.getUser())) { this.users.add(action.getUser()); } channel.ack(); } @Process(actionType = RemoveUser.class) - public void onRemove(String user, final Dispatcher.Channel channel) { + public void onRemove(RemoveUser action, final Dispatcher.Channel channel) { - if (Todo.USER_ANY.equals(user)) { return; } + if (Todo.USER_ANY.equals(action.getUser())) { return; } - this.users.remove(user); + this.users.remove(action.getUser()); // update selection if necessary - if (user.equals(selectedUser)) { dispatcher.dispatch(new SelectUser(Todo.USER_ANY)); } + if (action.getUser().equals(selectedUser)) { dispatcher.dispatch(new SelectUser(Todo.USER_ANY)); } channel.ack(); }