Search code examples
restlet

Is there a standard way to package many Restlets into a single Restlet?


I have a situation where the application developers and the framework provider are not the people. As a framework provider, I would like to be able to hand the developers what looks like a single Filter, but is in fact a chain of standard Filters (such as authentication, setting up invocation context, metrics, ++).

I don't seem to find this functionality in the standard library, but maybe there is an extension with it.


Solution

  • Instead of waiting for an answer, I went ahead with my own implementation and sharing here if some needs this.

    /**
     * Composes an array of Restlet Filters into a single Filter.
     */
    public class ComposingFilter extends Filter
    {
        private final Filter first;
        private final Filter last;
    
        public ComposingFilter( Filter... composedOf )
        {
            Objects.requireNonNull( composedOf );
            if( composedOf.length == 0 )
            {
                throw new IllegalArgumentException( "Filter chain can't be empty." );
            }
            first = composedOf[ 0 ];
            Filter prev = first;
            for( int i = 1; i < composedOf.length; i++ )
            {
                Filter next = composedOf[ i ];
                prev.setNext( next );
                prev = next;
            }
            last = composedOf[ composedOf.length - 1 ];
        }
    
        @Override
        protected int doHandle( Request request, Response response )
        {
            if( first != null )
            {
                first.handle( request, response );
                Response.setCurrent( response );
                if( getContext() != null )
                {
                    Context.setCurrent( getContext() );
                }
            }
            else
            {
                response.setStatus( Status.SERVER_ERROR_INTERNAL );
                getLogger().warning( "The filter " + getName() + " was executed without a next Restlet attached to it." );
            }
            return CONTINUE;
        }
    
        @Override
        public synchronized void start()
            throws Exception
        {
            if( isStopped() )
            {
                first.start();
                super.start();
            }
        }
    
        @Override
        public synchronized void stop()
            throws Exception
        {
            if( isStarted() )
            {
                super.stop();
                first.stop();
            }
        }
    
        @Override
        public Restlet getNext()
        {
            return last.getNext();
        }
    
        @Override
        public void setNext( Class<? extends ServerResource> targetClass )
        {
            last.setNext( targetClass );
        }
    
        @Override
        public void setNext( Restlet next )
        {
            last.setNext( next );
        }
    }
    

    NOTE: Not tested yet.