Spring Roo Tutorial: LongURL, Part 3

In my previous two tutorials we produced something that responds to REST requests like this:

  GET http://myhost:8080/longurl/urlmaps/[url_id]
  Accept: application/json

But that isn't very useful when looking back at my requirements. I really want to send a URL as a parameter - not its id.

What I really want is something like

  GET http://myhost:8080/longurl/lookup_longurl_from/[shorturl]
  Accept: application/json

ie I want to look up a short url and get the long url from it - I don't know what the id of the short url is.

A New Finder

First of all we need a finder - something to do some hibernate majick to retrieve objects with certain criteria. We want to find records in the UrlMaps entity tables based upon having the ShortUrl being equal to something passed in... So in Roo we have a useful command for listing what Finders are available...

    ~.domain.UrlMap roo> finder list --class ~.domain.UrlMap 
    findUrlMapsByLongUrlEquals(String longUrl)
    findUrlMapsByLongUrlIsNotNull()
    findUrlMapsByLongUrlIsNull()
    findUrlMapsByLongUrlLike(String longUrl)
    findUrlMapsByLongUrlNotEquals(String longUrl)
    findUrlMapsByShortUrlEquals(String shortUrl)
    findUrlMapsByShortUrlIsNotNull()
    findUrlMapsByShortUrlIsNull()
    findUrlMapsByShortUrlLike(String shortUrl)
    findUrlMapsByShortUrlNotEquals(String shortUrl)

The one we want is

    // Find long urls by specifying the short url 
    finder add --finderName findUrlMapsByShortUrlEquals

(If you skip the list finders command then Roo gets confused about which class of entities you are trying to find!)

And we also want to add some more web code - a controller which calls that finder:

    controller scaffold --entity ~.domain.UrlMap --class ~.web.UrlLookupController

Now this worked in Spring Roo 1.4, but you needed to edit the UrlLookupController.java found in src/main/java/uk/co/owal/longurl/web/

First of all you will want to change the URL fragment which corresponds to this lookup... eg

    @RequestMapping("/lookup")

This tutorial was written using Spring Roo version 1.4. Instead, in Roo 1.5, you now get an error like

    Your application already contains a mapping to 'urlmaps'. Please provide a different path.

So instead we should have tried something like

    ~.domain.UrlMap roo> controller scaffold --entity ~.domain.UrlMap --class ~.web.UrlLookupController --path /lookup
    Created SRC_MAIN_JAVA/uk/co/owal/longurl/web/UrlLookupController.java
    Created SRC_MAIN_WEBAPP/WEB-INF/views/lookup
    Created SRC_MAIN_WEBAPP/WEB-INF/views/lookup/views.xml
    Updated SRC_MAIN_WEBAPP/WEB-INF/views/lookup/views.xml
    Created SRC_MAIN_JAVA/uk/co/owal/longurl/web/UrlLookupController_Roo_Controller.aj
    Created SRC_MAIN_WEBAPP/WEB-INF/views/lookup/list.jspx
    Created SRC_MAIN_WEBAPP/WEB-INF/views/lookup/show.jspx
    Created SRC_MAIN_WEBAPP/WEB-INF/views/lookup/create.jspx
    Created SRC_MAIN_WEBAPP/WEB-INF/views/lookup/update.jspx
    Created SRC_MAIN_WEBAPP/WEB-INF/views/lookup/findUrlMapsByShortUrlEquals.jspx
    Created SRC_MAIN_JAVA/uk/co/owal/longurl/web/UrlLookupController_Roo_Controller_Finder.aj
    Created SRC_MAIN_JAVA/uk/co/owal/longurl/web/UrlLookupController_Roo_Controller_Json.aj

(Spring Roo 1.4 again!)

Now, lets create the method

    @RequestMapping(value = "/v1", method = RequestMethod.GET, headers = "Accept=application/json")
    @ResponseBody
      public ResponseEntity showJson(@RequestParam("url") String url ) {
        log.info("url is " + url);
        TypedQuery urlmaps = UrlMap.findUrlMapsByShortUrlEquals(url);
        List results = urlmaps.getResultList();
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/text; charset=utf-8");
        for (final UrlMap urlmap : results ) { 
                if (urlmap != null) {
                        return new ResponseEntity(urlmap.toJson(), headers, HttpStatus.OK);
                }
        };      
        // Some Code Missing Here  
        return new ResponseEntity(headers, HttpStatus.NOT_FOUND);
    }

OK? (I assume you've set up a "log" object :-)

So this is responding to urls like this...

  GET http://myhost:8080/longurl/lookup/v1/?url=[the_urlencoded_url]
  Accept: application/json

This isn't what I call REST because it isn't a clean URL - it has a query string. However lots of people tell me that is ok in REST. Hmmmm.

Fetching New Data

But the algorithm I selected is to check our own cache first and if it isn't in there go fetch the data from the third party external sources... So insert these lines where there was "// Some Code Missing Here"

       // It isn't in our cache, so now look elsewhere.        
        UrlMap freshUrlMap = checkExternalSources(url);
        if (freshUrlMap != null) {
                // SAVE freshUrlMap
                // ToDo
                freshUrlMap.persist();
                
                return new ResponseEntity(freshUrlMap.toJson(), headers, HttpStatus.OK);
        }

Eh? what? I've cheated a bit, haven't I. I've introduced a call to "checkExternalSources" without telling you what that is. Well, it is a call which performs a REST query on the realurl.org service... but that is best looked at in another article... Looking at REST Template in Spring