Spring Roo
  1. Spring Roo
  2. ROO-812

Op4J add-on to significantly reduce volume of Java code users have to write

    Details

    • Type: New Feature New Feature
    • Status: Resolved
    • Priority: Critical Critical
    • Resolution: Complete
    • Affects Version/s: None
    • Fix Version/s: 1.1.3.RELEASE
    • Component/s: GENERAL OBJECTS
    • Labels:
      None

      Description

      Op4j (http://www.op4j.org/) offers some nice syntax improvements for Java programmers.

      I have done some experimentation with the output of "script vote.roo". I edited ChoiceIntegrationTest to create 12 votes across 3 different choices. The objective was to try out Op4j and compare the code sizes "before Op4j" and "after Op4j" for a simple collection-related operation.

      To this end, the following represents the Java syntax to iterate over a List<Vote> collection and produce a Map<Choice,List<Vote>> that groups the votes for each Choice:

      Map<Choice,List<Vote>> votesByChoice = new HashMap<Choice, List<Vote>>();
      for (Vote vote : votes) {
        List<Vote> currentVotes = votesByChoice.get(vote.getChoice());
        if (currentVotes == null) {
          currentVotes = new ArrayList<Vote>();
          votesByChoice.put(vote.getChoice(), currentVotes);
        }
        currentVotes.add(vote);
      }
      

      The above is 9 lines of code with normal formatting. With Op4j one can replace the above with a single line:

      Map<Choice,List<Vote>> votesByChoice = Op.on(votes).zipAndGroupKeysBy(Get.attrOf(Types.forClass(Choice.class), "choice")).get();
      

      However, two issues with the Op4j native code are (a) many new developers would consider that Get.attrOf(...) fragment a fairly complicated (and long) piece of code and (b) it's not type safe (the use of a "choice" literal).

      With Roo, we can simplify the code and also make it type safe, with all the nice code assist and literal-avoidance features we've come to expect in Roo apps. This works:

      Map<Choice,List<Vote>> votesByChoice = Op.on(votes).zipAndGroupKeysBy(Choice.Keys.CHOICE).get();
      

      Thus we've gone from 9 lines of type-safe, literal-free Java code (Raw java) to 1 line of type-safe, literal-free Java code (Op4j with Roo).

      To make this work I needed to produce 3 new compilation units, the contents of which are:

      public class ChoiceKeys {}
      
      privileged aspect ChoiceKeys_Roo_Op4J {
        public final Function<Object, Choice> ChoiceKeys.CHOICE = Get.attrOf(Types.forClass(Choice.class), "choice");
        public static final ChoiceKeys ChoiceKeys.instance = new ChoiceKeys();
      }
      
      privileged aspect Choice_Roo_Op4J {
        public static final ChoiceKeys Choice.Keys = ChoiceKeys.instance;
      }
      

      As usual the *_Roo_Op4J files are managed by Roo and thus are correctly maintained without user intervention. It would be preferable to have simply added a Keys inner type within Choice_Roo_Op4J, but this is not presently possible with ITDs. Andy Clement is looking into the latter. Once AspectJ offers this feature the code users have written will not require any changes, as the usage pattern for these inner types would be identical from an invocation perspective to the static final field model used above.

      It would be nice if we wrote a Roo add-on that made all of this tick. I'll attach my vote sample so anyone who wants to play with Op4j can do so. Just run the ChoiceIntegrationTest to see it in action and enjoy 1/9th the amount of code.

        Activity

        Hide
        Andy Clement added a comment -

        AspectJ changes related to inter type declared inner classes are progressing under https://bugs.eclipse.org/bugs/show_bug.cgi?id=310501

        Today, this compiles:

        public class Simple{
          public static void main(String []argv) {
            System.out.println(Inner.number);
          }
        }
        
        aspect X {
          public static class Simple.Inner {
            static int number = 42;
          }
        }
        

        The Roo scenario is now also working for me:

        aspect X {
          public static class Choice.Keys {
            public static final Function<Object,Choice> CHOICE = Get.attrOf(Types.forClass(Choice.class),"choice");
          }
        }
        

        It would be a world of work to solve the general case and support all possibilities (non static inner types for example shudder) but what I have so far does seem to be just enough for Roo. There is still some work to do before it will be generally usable in AJDT (sort out code assist, check incremental compilation, etc) - that is what I'll look at next.

        Show
        Andy Clement added a comment - AspectJ changes related to inter type declared inner classes are progressing under https://bugs.eclipse.org/bugs/show_bug.cgi?id=310501 Today, this compiles: public class Simple{ public static void main(String []argv) { System.out.println(Inner.number); } } aspect X { public static class Simple.Inner { static int number = 42; } } The Roo scenario is now also working for me: aspect X { public static class Choice.Keys { public static final Function<Object,Choice> CHOICE = Get.attrOf(Types.forClass(Choice.class),"choice"); } } It would be a world of work to solve the general case and support all possibilities (non static inner types for example shudder ) but what I have so far does seem to be just enough for Roo. There is still some work to do before it will be generally usable in AJDT (sort out code assist, check incremental compilation, etc) - that is what I'll look at next.
        Hide
        Robert Oschwald added a comment -

        is this a critical ticket? I think it's a request for enhancement.

        Show
        Robert Oschwald added a comment - is this a critical ticket? I think it's a request for enhancement.
        Hide
        Stefan Schmidt added a comment -

        The addon has been added to Spring Roo master with commit a220a2d2ffec67347ca3b765ab75de7e4485965a.

        Two commands are available:

        roo> op4j setup
        roo> op4j add --class ...
        

        There is also a @RooOp4j annotation available to indicate the generation of a MyType_Roo_Op4j.aj ITD.

        This addon is not (yet) linked to the Spring Roo root pom (therefore it will not be included in release builds for now) pending support for simple static inner types in ITDs through AspectJ and AJDT.

        Show
        Stefan Schmidt added a comment - The addon has been added to Spring Roo master with commit a220a2d2ffec67347ca3b765ab75de7e4485965a. Two commands are available: roo> op4j setup roo> op4j add --class ... There is also a @RooOp4j annotation available to indicate the generation of a MyType_Roo_Op4j.aj ITD. This addon is not (yet) linked to the Spring Roo root pom (therefore it will not be included in release builds for now) pending support for simple static inner types in ITDs through AspectJ and AJDT.
        Hide
        Stefan Schmidt added a comment -

        Removed JSR 250 dependency with commit 80ad9d665326fd9e1fd6e4331535fa67557f07a0.

        Show
        Stefan Schmidt added a comment - Removed JSR 250 dependency with commit 80ad9d665326fd9e1fd6e4331535fa67557f07a0.
        Hide
        Enrique Ruiz (DiSiD) added a comment -

        Hi guys, is Op4J really needed? IMHO:

        • Op4J could introduce runtime overload.
        • You reduce the size of source code but it is more complicated to read it. Now all Java developers are able to read the source code generated by Roo. With Op4J only those developers that learn Op4J API will be able to read the source code generated by Roo.
        Show
        Enrique Ruiz (DiSiD) added a comment - Hi guys, is Op4J really needed? IMHO: Op4J could introduce runtime overload. You reduce the size of source code but it is more complicated to read it. Now all Java developers are able to read the source code generated by Roo. With Op4J only those developers that learn Op4J API will be able to read the source code generated by Roo.
        Hide
        Stefan Schmidt added a comment -

        This add-on is already complete. It will be hosted in Roo master but not shipped with Roo distributions. It will be made available as a separate add-on, so it is the users choice to install and use it or not.

        Show
        Stefan Schmidt added a comment - This add-on is already complete. It will be hosted in Roo master but not shipped with Roo distributions. It will be made available as a separate add-on, so it is the users choice to install and use it or not.
        Hide
        Stefan Schmidt added a comment -

        I'll go ahead and resolve this ticket given the add-on is now generally available via the Roo 'addon install' command. It is recommended for projects using this add-on to use the latest STS release as it offers improved AspectJ integration for static inner types.

        Show
        Stefan Schmidt added a comment - I'll go ahead and resolve this ticket given the add-on is now generally available via the Roo 'addon install' command. It is recommended for projects using this add-on to use the latest STS release as it offers improved AspectJ integration for static inner types.

          People

          • Assignee:
            Stefan Schmidt
            Reporter:
            Ben Alex
          • Votes:
            5 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: