Dynamic Templates/Routing

classic Classic list List threaded Threaded
7 messages Options
Tim
Reply | Threaded
Open this post in threaded view
|

Dynamic Templates/Routing

Tim
I'm a new RESTlet user and I was wondering if it's possible to route dynamically based on templates retrieved from a database in order to allow new templates to be added without changing any code? My plan is to have a table of URL templates together with their mappings, per request type, to stored procedure calls. The resource would then construct the stored procedure call based on which template was matched and map the parameters in the template to the procedure parameters.

For example:

URL Request Stored Procedure
/customers GET CustomersGet()
                        POST CustomerCreate(name...)
/customers/{cus} GET CustomersGetById(cus)
/invoices GET InvoicesGet()
/invoices/{inv} GET InvoicesGetById(inv)

I wanted to have a single resource class that would be called for all templates that could, based on the template that matched and the request, look-up the stored procedure and construct the call to it dynamically by taking the parameters extracted by the framework and any supplied in the request body and matching them to the stored procedure parameters. Unfortunately I couldn't find a way to get the template that was matched.

The background behind this is that I am trying to create a system whereby new web applications can be developed without changing any Java code, simply by implementing the relevant stored procedures in the database and changing the configuration (it's not a problem if a server restart is required). All resources, such as HTML pages, JPEGs, Freemarker templates etc. are stored in the backend database as BLOBs are are served from there so that they can be easily modified and new ones uploaded.

Thanks in advance,

Tim.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3075356
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic Templates/Routing

Tim Peierls
You don't need to use Restlet ServerResource for this. You can write a custom Restlet with an initialization method that reads and caches the mapping from path to procedure, and a handle(Request, Resource) method that extracts path and parameters from the request and uses the cached mapping to call the appropriate procedure.

Restlet ServerResources are more appropriate when the behavior of resources is defined in code, rather than in a database:

public class MyResource extends ServerResource {
    /** How to handle GET requests on this resource. */
    @Get public String getSomeText() {
        String id = getAttribute("id");
        return lookupSomeTextFromId(id);
    }
    String lookupSomeTextFromId(String id) { ... }
}

so that the mapping from path to resource behavior can be set up with calls to Router.attach:

public class MyApplication extends Application {
    @Override public Restlet createInboundRoot() {
        Router router = new Router(getContext());
        ...
        router.attach("/path/to/my/resource/{id}", MyResource.class);
        ...
        return router;
    }
}

I think it's usually a very good idea to decouple the resource structure and behavior from the data. I find it surprising that you would want to do otherwise.

--tim



On Thu, Mar 27, 2014 at 1:33 PM, Tim <[hidden email]> wrote:
I'm a new RESTlet user and I was wondering if it's possible to route dynamically based on templates retrieved from a database in order to allow new templates to be added without changing any code? My plan is to have a table of URL templates together with their mappings, per request type, to stored procedure calls. The resource would then construct the stored procedure call based on which template was matched and map the parameters in the template to the procedure parameters.

For example:

URL                     Request         Stored Procedure
/customers              GET             CustomersGet()
                        POST            CustomerCreate(name...)
/customers/{cus}        GET             CustomersGetById(cus)
/invoices               GET             InvoicesGet()
/invoices/{inv}         GET             InvoicesGetById(inv)

I wanted to have a single resource class that would be called for all templates that could, based on the template that matched and the request, look-up the stored procedure and construct the call to it dynamically by taking the parameters extracted by the framework and any supplied in the request body and matching them to the stored procedure parameters. Unfortunately I couldn't find a way to get the template that was matched.

The background behind this is that I am trying to create a system whereby new web applications can be developed without changing any Java code, simply by implementing the relevant stored procedures in the database and changing the configuration (it's not a problem if a server restart is required). All resources, such as HTML pages, JPEGs, Freemarker templates etc. are stored in the backend database as BLOBs are are served from there so that they can be easily modified and new ones uploaded.

Thanks in advance,

Tim.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3075356

Tim
Reply | Threaded
Open this post in threaded view
|

RE: Re: Dynamic Templates/Routing

Tim
Hi Tim,

Thanks for the reply. I'll try to digest the technical details in the morning but as to why I'm wanting to do this - well, it's complicated! Basically I work on a legacy system (IBM system i aka, iSeries, aka AS/400) and there are very few people in the company with any sort of grasp of modern tools and languages - including Java. Furthermore, we have very strict compliance rules to adhere to which means that almost any software developed on the server must pass all sorts of hurdles before it can be created or changed. Because there is tight coupling between the user interface (green screens!) and the application logic there is little or no flexibility to change or add stuff once it's live. It's arguable whether the company's implementation of the compliance policy makes matters worse but it is what it is and it won't be changing so we have to work with it.

My idea is to develop a system whereby the existing business logic can be used or new logic developed on the server using languages and tools that everyone is familiar with and expose it as stored procedures that can be used to build a web frontend. I am trying to design a relatively thin and stable layer to glue the web frontend to the iSeries backend, which allows the front(ish) end (either Web APIs using JSON for example, static web pages or Freemarker templated pages) to be wired to the backend using configuration only. If each new API or app requires Java coding and redistribution of Java objects then it will be a non-starter because people simply don't have the knowledge or the inclination to learn it unfortunately.

Ultimately I would like people to be able to create a new web app by writing a few stored procedures on the iSeries to implement the business logic, wire up the RESTful URLs from the web server to the correct SPs and upload and HTML and other resources to be served from the database, so effectively the whole thing dynamic and configurable. I hope to be able to build a model dynamically from the SP result sets that can be used by Freemarker. Being data driven (rather than using static  objects in the file system) allows us greater freedom because static objects are audited and have to be accounted for, whereas data doesn't!

I'm not even sure if this is possible, but if nothing else it's a good learning exercise!

Tim.

PS. I don't quite understand what you mean here "I think it's usually a very good idea to decouple the resource structure and behavior from the data. I find it surprising that you would want to do otherwise."
I am only trying to replace the "hard coded" Java that would be implemented in the Resource classes for each URL with a single thing that can perform the gluing together based in configuration instead. I don't think it changes the coupling does it?

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3075384
Tim
Reply | Threaded
Open this post in threaded view
|

RE: Re: Dynamic Templates/Routing

Tim
Tim,

I'm wondering about your suggestion above, would I have to implement my own code to match the templates and extract the parameters or could I take advantage of the framework's ability to do that?

Tim.

PS. I understand now what you mean about the coupling, hopefully but explanation answers your query.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3075429
Reply | Threaded
Open this post in threaded view
|

Re: Re: Dynamic Templates/Routing

Tim Peierls
On Fri, Mar 28, 2014 at 9:48 AM, Tim <[hidden email]> wrote:
I'm wondering about your suggestion above, would I have to implement my own code to match the templates and extract the parameters or could I take advantage of the framework's ability to do that?

You could take advantage of the framework. Here's a sketch:

public class YourApplication extends Application {
    ...
    @Override public Restlet createInboundRoot() {
        // A map from path template (e.g. /path/to/resource/{resid}) to
        // metadata about the corresponding stored procedure.
        Map<String, YourStoredProcInfo> pathTemplateToStoredProcInfo = ... from db ...;

        Router router = new Router(getContext());
        for (String pathTemplate : pathTemplateToStoredProcInfo.keySet()) {
            YourStoredProcInfo storedProcInfo = pathTemplateToStoredProcInfo.get(pathTemplate);
            router.attach(pathTemplate, createStoredProcHandler(storedProcInfo));
        }
        return router;
    }

    private Restlet createStoredProcHandler(YourStoredProcInfo storedProcInfo) {
        final YourQueryTemplate queryTemplate = storedProcInfo.createQueryTemplate();
        final YourResultHandler resultHandler = storedProcInfo.createResultHandler();
        return new Restlet() {
            @Override public void handle(Request request, Response response) {
                // Combine queryTemplate with method, attributes, and parameters from request.
                YourQuery query = queryTemplate.createQuery(
                    request.getMethod(),     // i.e., GET, POST, PUT, or DELETE
                    request.getAttributes(), // e.g., { "resid" : "ABC123" }
                    request.getResourceRef().getQueryAsForm() // e.g., "?sortBy=date"
                );
                YourResults results = query.execute();
                response.setStatus(resultHandler.getStatus(results));
                if (response.getStatus().isSuccess()) {
                    Representation rep = resultHandler.createRepresentation(results);
                    response.setEntity(rep);
                } else {
                    // ... error handling
                }
            }
        };
    }
}

This of course just pushes the hard part into YourStoredProcInfo, YourQueryTemplate, YourQuery, YourResultHandler, and YourResults, but this way the hard parts are isolated from the Restlet framework code.

It also leaves out all sorts of details, like error handling, client-info, caching, etc., but it wouldn't be hard to adapt this basic idea to include them.


PS. I understand now what you mean about the coupling, hopefully but explanation answers your query.

I think so. Sounds like the legacy nature of the enterprise (and its people!) is so restrictive that you are forced into a design that you wouldn't consider under normal circumstances.

--tim
Tim
Reply | Threaded
Open this post in threaded view
|

RE: Re: Re: Dynamic Templates/Routing

Tim
Hi Tim,

Thanks very much for taking the time to knock up the example, I've made a very rough implementation and it seems to do exactly what I was after!

Tim.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3075533
Reply | Threaded
Open this post in threaded view
|

Re: Re: Re: Dynamic Templates/Routing

Tim Peierls
Glad to hear it!

Restlet isn't really doing much for you in this case, but at least this way you're in a perfect position to add new functionality or an alternative web API if the restrictions you currently operate under are ever lifted slightly.

--tim


On Mon, Mar 31, 2014 at 4:56 AM, Tim <[hidden email]> wrote:
Hi Tim,

Thanks very much for taking the time to knock up the example, I've made a very rough implementation and it seems to do exactly what I was after!

Tim.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3075533