Skip to main content

Rails to Java via REST

Posted by bleonard on August 16, 2007 at 3:37 PM PDT

Since I've been studying the Rails framework, I've had an interest in integration with Java. Yes, JRuby is one possible solution, but if you have some large Java system that you want to integrate with, it' unlikely that you're just going to get the jar files to access that system's APIs. More likely is that system will expose itself as a service, and the service type du jour is REST.

So, in this entry I'm going to expose an entity class as a RESTful web service and then create a Rails client for that entity.

Setting Things Up

  • Download and install NetBeans 6.0 M10. Grab the Full distribution so you can get the Java IDE, Ruby and GlassFish.
  • Download and install the SWDP to GlassFish. This is required to create the RESTful web service.

Creating the RESTful Web Service

Here we'll expose the manufacturer table as a RESTful web service.

Create the Manufacturer Entity Class

  1. Start NetBeans 6.0 and create a new Web Application named Manufacturers.
  2. Right-click the project and select Entity Classes from Database.
  3. Select jdbc/sample as the Data Source and MANUFACTURER as the Table (your list of available tables will be different then mine, but you should have MANUFACTURER):






  4. Click Next and set the package to model.
  5. Click the Create Persistence Unit button and then Create on the Create Persistence Unit dialog:




  6. Click Finish to generate the Manufacturer entity class.

Create the REST Web Service from the Entity Class

  1. Right-click the project and select REST Web Services from Entity Classes
  2. Select the Manufacturer Entity Class and click Next.
  3. Set the package to rest and click Finish.

Test the REST Services

  1. Right-click the project and choose Test REST Services.
  2. This will deploy the Manufacturers application to GlassFish and launch a Test REST Services tool:







    This tool is very handy for working with the RESTful interface.


The Completed Project

Manufacturers.zip

Creating the Rails REST Client

If you know REST then you know that its operations are basically the HTTP methods: POST, GET, UPDATE and DELETE, which correspond nicely to SQL's Create, Read, Update and Delete. I will implement each one in turn.

GET the Manufacturers

Create the Project

Create a new Ruby on Rails Application named manufacturer_client.

Create a Model to Represent the Manufacturer

The Manufacturer entity contains a bunch of fields. For the purposes of this tutorial, we're only going to work with a handful of them: name, email and phone.

  1. Since we're not using ActiveRecord, we're not going to run the model generator. Instead, right-click the Models folder and select New > Ruby Class.
  2. Name the class Manufacturer.
  3. Add the following code:



    require 'open-uri'

    class Manufacturer
      BASE_URI = 'http://localhost:8080/Manufacturers/restbean/manufacturers/'
     
      # These accessor names match their corresponding element names. This is
      # important because I use these names to construct the xml to post back
      # to the service.
      attr_accessor :manufacturerId, :name, :email, :phone
       
      def initialize(id, name=nil, email=nil, phone=nil)
        @manufacturerId, @name, @email, @phone = id, name, email, phone  
      end    
     
      # A utility method getting this object's URI.
      def uri
        BASE_URI + manufacturerId
      end
     
      # Retrieves the Manufacturer data
      def get
        doc = REXML::Document.new(open(uri).read)    
        @name = REXML::XPath.first(doc, "//name").text
        @email = REXML::XPath.first(doc, "//email").text
        @phone = REXML::XPath.first(doc, "//phone").text 
      end  
    end  

Generate a Controller and View to Display the Manufacturers

  1. Right-click the project and choose Generate. Select Controller and set the Name to Manufacturer and the Views to index list.
  2. Redirect the index to the list:



      def index
        list
        render :action => 'list'
      end
  3. Define list as follows:



      def list
        # Build a list of Manufacturers
        @manufacturers = []   
       
        # Get the manufacturer list URI and read the XML document from it.
        doc = REXML::Document.new(open(Manufacturer::BASE_URI).read)   
       
        #For every manufacturer...
        REXML::XPath.each(doc, "//manufacturerId/") do |entry|
          id = entry.text             
          # Append to the manufactures list
          @manufacturers << Manufacturer.new(id)       
        end   
       
        # Now that the manufacturer objects all have ids, populate them...
        @manufacturers.each do | manufacturer |
          manufacturer.get
        end
       
        return @manufacturers   
      end  

Code the View the Display the Manufacturers

  1. Open list.rhtml and replace its contents with the following:



    Manufacturer List



       
                   
           
           
        <% for manufacturer in @manufacturers %>
               
           
           
       
        <% end %>
    Manufacturer NamePhone
    <%= manufacturer.name %><%= manufacturer.phone %>

Test

  1. Test F6 to run the project and hit: http://localhost:3000/manufacturer:






Getting the rest-open-uri Gem

Before we can continue, the open-uri library we're using above only supports HTTP GET. Fortunately for us, Leonard Richardson has extended the library (rest-open-uri) to accept the additional HTTP methods.

  1. Open the Gem Manager: Tools > Ruby Gems
  2. Select the New Gems tab and enter rest-open-uri in the Search field:






  3. Install the rest-open-uri gem:






  4. Restart your server. This is necessary for the new library to be picked up.

POST new Manufacturers

  1. Add the following link to the bottom of list.rhtml:





    <%= link_to 'New Manufacturer', :action => 'new' %>

  2. Right-click the Views > manufacturer folder and create a new rhtml file named _form. We'll use this partial for both the new and edit pages. Populate it with the following:



    Name

    <%= text_field 'manufacturer', 'name' %>


    E-Mail

    <%= text_field 'manufacturer', 'email' %>


    Phone

    <%= text_field 'manufacturer', 'phone' %>

     
  3. Right-click the Views > manufacturer folder and create a new rhtml file named new. Populate it with the following:



    New Manufacturer


    <% form_tag :action => :create do %>
       

    Manufacturer ID

        <%= text_field 'manufacturer', 'manufacturerId' %>


        <%= render :partial => 'form' %>
        <%= submit_tag "Create" %>
    <% end %>

    <%= link_to 'Back', :action => 'list' %>
  4. Switch to the ManufacturerController and add the following create action:



      def create    
        values = params[:manufacturer]
        @manufacturer = Manufacturer.new(values[:manufacturerId], values[:name], values[:email], values[:phone])
       
        if @manufacturer.post
          redirect_to :action => 'list' 
        else   
          render :action => 'new'
        end
      end 
  5. Switch to the Manufacturer model and add the post method and and a couple of helper methods:



      # Creates a new Manufacturer
      def post          
       
        # Get an XML representation of this model
        value = get_as_xml
       
        # Gather the arguments required for the post
        args = prepare_args(:post, value)
         
        # Do the POST
        result = open(BASE_URI, args)     
       
        puts 'HTTP Status: '
        puts result.status
       
        return true if result.status[0] = 201
         
      end
     
      private
     
      #Return an XML representation of this model
      def get_as_xml
        result =  ""
        instance_variables.each do | var| var.sub!('@', '')
          puts send(var)
          result += "<#{var}>" + send(var) + "#{var}>"
        end
        result += ""
      end
     
      def prepare_args(http_method, body)
        # Set the arguments for the POST
        args = {:method => http_method}
        args["Content-Type"] = "application/xml"
        args["Content-Length"] = body.size.to_s
        args[:body] = body
        return args
      end
  6. Test:: http://localhost:3000/manufacturer/new








PUT Updates Back

  1. Open list.rhtml and add the following column to the end of the table:



      <%= link_to 'Edit', :action => 'edit', :id => manufacturer.manufacturerId %>

  2. Right-click the Views > manufacturer folder and create a new rhtml file named edit. Populate it with the following:



    Editing Manufacturer



    <% form_tag :action => "update", :id => @manufacturer.manufacturerId do %>
         <%= hidden_field 'manufacturer', 'manufacturerId' %>


        <%= render :partial => 'form' %>
        <%= submit_tag 'Edit' %>
    <% end %>

    <%= link_to 'Back', :action => 'list' %>



  3. Switch to the ManufacturerController and add the following edit and update actions:



      def edit
        @manufacturer = Manufacturer.new(params[:id])
        @manufacturer.get   # Populate the object
      end 
     
      def update
        values = params[:manufacturer]   
        @manufacturer = Manufacturer.new(values[:manufacturerId], values[:name], values[:email], values[:phone])
        @manufacturer.update
        redirect_to :action => 'list' 
      end 


  4. Switch to the Manufacturer and add the update method:



      def update    
       
        # Get the entire existing record. With this web service, if we only
        # pass in the new values, the other fields will be set to nil
        doc = REXML::Document.new(open(uri).read)
        manufacturer = REXML::XPath.first(doc, "//manufacturer/")
       
        # Interate through the document, updating the fields this application
        # exposes
        manufacturer.elements.each do |element|
          case element.name
          when 'name'
            element.text = @name
          when 'email'
            element.text = @email
          when 'phone'
            element.text = @phone
          end
        end
       
        # Revert back to an XML string
        value = manufacturer.to_s
       
        # Gather the arguments required for the post
        args = prepare_args(:put, value)
         
        # Do the PUT
        result = open(uri, args)     
       
        puts 'HTTP Status: '
        puts result.status   
       
      end 




  5. Test http://localhost:3000/manufacturer/edit/19985678








DELETE Manufacturers

This is the easiest one :-).

  1. Open list.rhtml and add the following column to the end of the table:



    <%= link_to 'Delete', :action => 'delete', :id => manufacturer.manufacturerId %>

  2. Press Ctrl+Shift+A to switch to the ManufacturerController and add the following delete action:



      def delete
        # Make a DELETE request
        uri = Manufacturer::BASE_URI + params[:manufacturerId]
        result =  open(uri, :method => :delete)
        puts result.status
        redirect_to :action => 'list' 
      end 
       
  3. Test: http://localhost:3000/manufacturer/list. Now you can delete all those entries you added above :-).

Summary

And there you have it, full CRUD operations against a RESTful Web Service. Now this application can use a lot of improvements, like basic error handling and validatation. Pagination on the list page would also be nice. All potential topics for a future blog :-).

The Completed Project

Resources

Related Topics >>