Rewrite Annotations

Splash Forums Rewrite Users Rewrite Annotations

This topic contains 10 replies, has 3 voices, and was last updated by  Christian Kaltepoth 3 years, 1 month ago.

Viewing 11 posts - 1 through 11 (of 11 total)
  • Author
    Posts
  • #24201

    Oleg
    Participant

    Hello,

    I would like to use Rewrite Annotations to implement a RESTful service. Is it possible at all? This page doesn’t describe all annotations http://ocpsoft.org/rewrite/docs/configuration/annotations/index

    Assume we have this class with Spring annotations to map incoming requests

    
    @Controller
    @RequestMapping("/")
    public class ExportController extends HttpServlet {
        @Autowired
        private ServletContext servletContext;
    
        @RequestMapping(method = RequestMethod.POST) {
        public void catchAll(
             @RequestParam(value = "type", required = false) String type,
             @RequestParam(value = "filename", required = false) String filename) {
    
             ...
        }
    
        @RequestMapping(value = "/demo", method = RequestMethod.GET)
        public String catchDemo() {
            
            ...
        }
    
        @RequestMapping(value = "/test/{fileName}", method = RequestMethod.GET)
        public ResponseEntity<byte[]> imageDownload(
             @PathVariable("fileName") String fileName) throws IOException {
    
            ...
        }
    }
    

    What would be a replacement of this code with Rewrite?

    Thanks a lot in advance.

    #24203

    Hey Oleg,

    You can do this, but you’d probably need to implement your own Annotation handlers, since at the moment we don’t support REST-style annotations.

    However, if you look in our showcase, you can see how I’ve implemented this using the Rewrite Configuration itself:

    https://github.com/ocpsoft/rewrite/blob/master/showcase/rest-ws/src/main/java/org/ocpsoft/rewrite/showcase/rest/RestRewriteConfiguration.java#L106

    We could probably add a few annotations to make this easier, but I have to ask, why not just use the JAX-RS specification from Java EE? RESTEasy is really quite nice to use!

    ————–
    This is actually very easy to do, even in your own code; all you need to do is create a custom annotation, and implement an AnnotationHandler:

    https://github.com/ocpsoft/rewrite/blob/master/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/Join.java#L35

    https://github.com/ocpsoft/rewrite/blob/master/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/handler/JoinHandler.java#L39
    ————–

    Once this is done, you register your annotation handler as a service and you’re done! You now have an annotation that will use Rewrite to add behavior to your classes!

    https://github.com/ocpsoft/rewrite/blob/master/config-annotations/src/main/resources/META-INF/services/org.ocpsoft.rewrite.annotation.spi.AnnotationHandler

    This is the mechanism we use internally to create all annotations, and it’s also a public SPI that anyone can use for their own needs. The whole philosophy of Rewrite is to use the same APIs internally that users would use to extend the framework.

    What do you think?

    Does that help? Are your maybe interested in adding REST support to Rewrite?

    #24204

    A sample annotation, handler, and registration:

    The annotation:

    @Inherited
    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Join
    {
       /**
        * The external path to which the resource will be exposed.
        */
       String path();
    
       /**
        * The internal resource to be exposed.
        */
       String to();
    }

    The handler:

    public class JoinHandler implements AnnotationHandler<Join>
    {
    
       @Override
       public Class<Join> handles()
       {
          return Join.class;
       }
    
       @Override
       public int priority()
       {
          return HandlerWeights.WEIGHT_TYPE_STRUCTURAL;
       }
    
       @Override
       public void process(ClassContext context, Join annotation, HandlerChain chain)
       {
          context.setBaseRule(org.ocpsoft.rewrite.servlet.config.rule.Join
                   .path(annotation.path())
                   .to(annotation.to()));
          chain.proceed();
       }
    
    }

    The registration:
    META-INF/services/org.ocpsoft.rewrite.annotation.spi.AnnotationHandler

    org.ocpsoft.rewrite.annotation.handler.DomainHandler
    org.ocpsoft.rewrite.annotation.handler.JoinHandler
    #24210

    Oleg
    Participant

    Hi Lincoln,

    I have understood. Many thanks! I will give it a try.

    The problem with JAX-RS 1.x is that the spec. doesn’t specify client API and you will end up with implementation specific code because you have to configure JAX-RS Application, etc. in web.xml. So, it is not portable across multiple app servers. JBoss has RESTeasy as JAX-RS impl. and WebLogic has Jersey. We have to support both. I could not manage this. JAX-RS 2.0 specifies client API and you are portable, but JAX-RS 2.0 only works in JEE 7 complaint servers (GlassGish 4 currenty) :-).

    Therefore, I thought about some simple JAX-RS like annotations. Maybe we can provide 4-6 main annotations for that. We will see.

    #24211

    It would be interesting to attempt to implement the JAX-RS 2 annotations in Rewrite, actually. I think it could be done, actually. The only tricky bit would be Marshalling/Unmarshalling of objects with JAXB or etc.

    #24214

    Oleg
    Participant

    Yes, it would be interesting. For XML response I would probably take XStream, for JSON Gson and for binary data quite normally Java. But it will require at least two extra dependencies…

    #24216

    Oleg
    Participant

    Your example https://github.com/ocpsoft/rewrite/blob/master/showcase/rest-ws/src/main/java/org/ocpsoft/rewrite/showcase/rest/RestRewriteConfiguration.java is really good! But how can we combine annotations on class level with annotations on method level? In my Spring example above the class is annotated with @RequestMapping(“/”) and the methods e.g. with @RequestMapping(value = “/demo”, method = RequestMethod.GET). The method’s annotations should complement the class’ one. Do you know what I mean?

    • This reply was modified 3 years, 1 month ago by  Oleg.
    #24220

    So.. the way combining annotations works is this (as far as I know… Christian did most of the work on annotations; I just refined the API:)

    Order of annotation processing:

    1. Class
    2. Method
    3. Parameter

    Each handler is provided a Context variable:

    public void process(ClassContext context, Join annotation, HandlerChain chain)
    public void process(MethodContext context, URLAction annotation, HandlerChain chain)
    public void process(ParameterContext context, Param annotation, HandlerChain chain)

    As you can see here, each sub-context also has access to the parent contexts:

    https://github.com/ocpsoft/rewrite/blob/master/annotations-api/src/main/java/org/ocpsoft/rewrite/annotation/api/ParameterContext.java

    Additionally, you can store variables in the contexts for use in sub-parameter handlers:

    https://github.com/ocpsoft/rewrite/blob/master/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/handler/ParameterHandler.java#L88

    This is how we implemented Join with Parameter and URLAction.

    #24221

    Oleg
    Participant

    I saw ClassContext, but MethodContext and ParameterContext with access to parent contextes are really nice. Thanks for examples. By the way, what is HandlerWeights.WEIGHT_TYPE_STRUCTURAL in public int priority()? What does it mean and what is it good for?

    #24222

    For that you’ll need to ask Christian. Lower priorities are evaluated first, but I believe these constants were set up to give a generic reference point, and a common set of priorities that can be used to simplify guesswork about which priority is which.

    #24288

    Hey all,

    I’m back from my vacation. So I can join the discussion now. 🙂

    Regarding the HandlerWeights constants. IMHO there are typically two types of annotation handlers:

    The first type builds the basic structure of the rules. It creates for example conditions, operations or bindings. The other type of handler enriches/modifies the conditions/operations/parameters created by the first type of handlers. So the enriching handlers must always execute AFTER the structural ones.

    Example: @Parameter creates a parameter binding. @Matches modifies the parameter binding by specifying a regular expression for it.

    Lincoln is right. You can choose your own constants for ordering. These default ones are just there to simplify your life. 🙂

Viewing 11 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic.

Comments are closed.