R Integration

R Integration Overview

R is a language and environment for statistical computing and graphics.  It is used in many industries to analyze and visualize what are often very large datasets. For a more detailed overview see http://www.r-project.org/

We want to make it easy to interface SmartClient and SmartGWT applications with the R environment so that data available in the web application tier can be sent to R for analysis and the outputs of this analysis (whether text or graphical) can be made available to the web tier for post processing and ultimate display to the end user - in short to make it easy to use R with all its power in your application.  

Here we present a ready-to use example of this sort of integration in the form of a web interface that drives the generation of a sophisticated stock chart leveraging a third party library written natively in R.  The key components here are:

  - SmartClient or SmartGWT

  - R installed on some server

  - The quantmod R module (for stock data lookup and chart generation)

  - The Rserve R module (part of the Java->R interface)

  - The rJava library (part of the Java->R interface)

In the example below, we create a form that takes a stock ticker that is sent to the server along with the current size of the chart image.  This data is processed by a standard DataSource that uses JSR223 as a bridge to execute R code embedded directly in the DataSource.  This R script is sent via rJava to the Rserve module running in the R environment for execution and the results (a binary chart in this case) are streamed back to the browser for display.

With this, we think you should be able to use R in your own SmartClient/SmartGWT applications - let us know how it goes!  We would love to hear how you use this...

SmartClient with R

This sample uses the ChartPane.js and RChart.js scripts as the client-side code, and the RChartDS.ds.xml as the dataSource:

ChartPane.js
isc.defineClass("ChartPane", "VLayout").addProperties({
formDefaults: {
    _constructor: "DynamicForm",
    width: 200,
    wrapItemTitles: false,
    autoFocus: true,
    itemKeyPress: function (item, keyName, characterValue) {
        if (keyName == "Enter") this.creator.updateChart();
    },
    numCols: 3,
    items: [
        {name: "stockSymbol", title: "Stock Symbol", defaultValue: "GOOG"}, 
        {name: "Chart", type: "button", click: "form.creator.updateChart()", startRow: false}
    ]
},
updateChart : function () {
    this.rChart.setCriteria(this.form.getValues());
},
    
rChartDefaults: {
    _constructor: "RChart",
    name: "stockChart",
    width: "900",
    height: "360"
},
autoChildren: ["form", "rChart"],
    
initWidget : function () {
    this.Super("initWidget", arguments);
    
    
    this.addAutoChildren(this.autoChildren);
    this.updateChart();
}
});
RChart.js
isc.defineClass("RChart", "Img").addProperties({
imageType: "normal",
initWidget : function () {
    //this.doSetSrc();
    this.Super("initWidget", arguments);
},
resized : function () {
    this.doSetSrc();
    this.resetSrc();
},
draw : function () {
    this.Super("draw", arguments);
    this.doSetSrc();
    this.resetSrc();
},
getCriteria : function () {
    // clone any criteria passed to us initially (don't scribble on user-provided object)
    var criteria = isc.addProperties({}, this.criteria);
    
    isc.addProperties(criteria, {
        name: this.name, 
        // coerce to string for passing to REngine
        width: this.getVisibleWidth()+"",
        height: this.getVisibleHeight()+""
    });
    return criteria;
},
setCriteria : function (criteria) {
    this.criteria = criteria;
    
    this.doSetSrc();
    this.resetSrc();
},
 
doSetSrc : function () {
    this.src = RChartDS.getFetchDataURL(this.getCriteria(), {operationId: "getChart"});
}
});
RChartDS.ds.xml
<DataSource 
    ID="RChartDS" 
>
    <fields>
        <field name="name" primaryKey="true"/>
    </fields>
    
    <operationBindings>
      <operationBinding operationType="fetch" operationId="getChart">
          <script language="groovy"><![CDATA[
            def chartRequest = new DSRequest("RChartDS", "fetch", rpc);
            
            // create a unique string for the temporary file used for the R-generated image
            criteria.tmpFilename = "/tmp/"+criteria.name+"_"+UUID.randomUUID()+".png";
            
            def chartResponse = chartRequest.setCriteria(criteria)
                                            .setOperationId(criteria.name)
                                            .execute();
            rpc.doCustomResponse();
            requestContext.setContentType("image/png");
            def os = response.getOutputStream();
            os.write(chartResponse.data);
            os.flush();
          ]]></script>
      </operationBinding>  
      <operationBinding operationType="fetch" operationId="stockChart">
          <script language="REngine"><![CDATA[
            library('quantmod')
          
            png(filename=criteria$tmpFilename, height=as.numeric(criteria$height), width=as.numeric(criteria$width))
          
            symbols <- getSymbols(criteria$stockSymbol, auto.assign=FALSE)
            chartSeries(symbols, name=criteria$stockSymbol, subset='last 3 months', TA="addVo();addBBands()")
          
            # Turn off device driver (to flush output to png)
            dev.off();
            
            # Read the generated image, delete it and return the binary stream
            returnVal <- readBin(criteria$tmpFilename,'raw',1024*1024); 
            unlink(criteria$tmpFilename);
            returnVal;
          ]]></script>
      </operationBinding>   
    </operationBindings>
</DataSource>

Finally, this is the code in the .html:

<SCRIPT SRC="../../isomorphic/DataSourceLoader?dataSource=RChartDS"></SCRIPT>
<SCRIPT src="scripts/ChartPane.js"></SCRIPT>
<SCRIPT src="scripts/RChart.js"></SCRIPT>
<SCRIPT>
isc.ChartPane.create({ 
    ID : "chartPane",
    width: "100%",
    height: "100%"
});
</SCRIPT>

This screenshot shows you the sample in action:

SmartGWT with R

The client-side code of this sample is the following:

public void onModuleLoad() {
    ChartPane chartPane = new ChartPane();
        chartPane.draw();
    }
    class ChartPane extends VLayout {
        private RChart rChart;
        private DynamicForm form;
        private Criteria criteria;
        
        public ChartPane () {
            this.form = new DynamicForm();
            this.form.setWidth(200);
            this.form.setWrapItemTitles(false);
            this.form.setAutoFocus(true);
            this.form.setNumCols(3);
            TextItem text = new TextItem();
            text.setName("stockSymbol");
            text.setTitle("Stock Symbol");
            text.setDefaultValue("YHOO");
            ButtonItem button = new ButtonItem();
            button.setTitle("Chart");
            button.setStartRow(false);
            button.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(
                        com.smartgwt.client.widgets.form.fields.events.ClickEvent event) {
                    updateChart();
                }
            });
            this.form.setItems(text, button);
            
            this.form.addItemKeyPressHandler(new ItemKeyPressHandler() {
                @Override
                public void onItemKeyPress(ItemKeyPressEvent event) {
                    if (event.getKeyName().equalsIgnoreCase("Enter")) {
                        updateChart();
                    }
                }
            });
            
            this.rChart = new RChart();
            this.criteria = new Criteria();
            this.criteria.addCriteria("name", "stockChart");
            this.criteria.addCriteria("stockSymbol", this.form.getValueAsString("stockSymbol"));
            this.criteria.addCriteria("width", "900");
            this.criteria.addCriteria("height", "360");
            this.rChart.setCriteria(criteria);
            
            this.addMembers(form, rChart);
        }
        
        private void updateChart () {
            this.criteria.addCriteria("stockSymbol", this.form.getValueAsString("stockSymbol"));
            this.rChart.setCriteria(criteria);
        }
    }
    
    class RChart extends Img {
        Criteria criteria = new Criteria();
        
        public RChart () {
            this.setImageType(ImageStyle.NORMAL);
            this.setName("stockChart");
            this.setWidth("900");
            this.setHeight("360");
            this.doSetSrc();
            this.resetSrc();
        }
        
        private void doSetSrc() {
            DataSource ds = DataSource.get("RChartDS");
            DSRequest properties = new DSRequest();
            properties.setOperationId("getChart");
            this.setSrc(ds.getFetchDataURL(this.getCriteria(), properties));
        }
        private void setCriteria(Criteria criteria) {
            this.criteria = criteria;
            this.doSetSrc();
            this.resetSrc();
        }
        private Criteria getCriteria() {
            return this.criteria;
        }
    }

This screenshot shows you the sample in action:

Either SmartClient or SmartGWT sample uses the same .ds.xml as the dataSource.

Installing the R engine and the packages needed.

1.- Directions for installing R: http://www.r-project.org/
2.- After installing R, run it and install the following packages:
2.1.- quantmod:
install.packages("quantmod")
2.2.- Rserve. Further information: http://www.rforge.net/Rserve/
install.packages("Rserve")
2.3.- rJava (JRI). Further information: http://rforge.net/JRI/  
install.packages("rJava")
3.- Download the libraries needed:
Rserve: JRS.jar, REngine.jar, RserveEngine.jar
JRI: JRS.jar, REngine.jar, JRI.jar, JRIEngine.jar  
Further information: http://www.rforge.net/rscript/index.html

Setting up the samples

SmartClientR sample

You can download the sample from here. Unzip it and put the SmartClientR folder into the smartclientSDK/examples/ folder and follow these steps:

1.- Copy the RChartDS.ds.xml file into the smartclientSDK/shared/ds/ folder.

2.- Copy the libraries of Rserve or JRI into the smartclientSDK/WEB-INF/lib/ folder.

3.- Open a console and run:

Windows machine: set path=%path%;C:\Program Files\R\R-2.15.3\bin\(x64 or i386);C:\Program Files\R\R-2.15.3\library\rJava\jri\(x64 or i386) (You need to verify this path in your R installation)

Linux machine: setenv R_HOME /usr/lib64/R (You need to verify this path in your R installation)

4.- If you are going to use Rserve, you'll need to do the following:

Windows machine:

- Run R, once you are in R's console, run these commands:

library(Rserve) (Enter)

Rserve() (Enter)

- Open the embeddedTomcat.bat file located in the smartclientSDK/WEB-INF/bin/ folder. Search this line:

"%JAVA%" -Xmx512m -Duser.timezone=GMT -Djava.awt.headless=true -cp

Add -Dorg.rosuda.jrs.host=localhost before -cp.

- Finally, run embeddedTomcat.bat from the opened console.

Linux machine:

- Run 'R CMD Rserve' from the opened console.

- Open the embeddedTomcat.sh file located in the smartclientSDK/WEB-INF/bin/ folder. Search this line:

$JAVA -Xmx512m -Duser.timezone=GMT -Djava.awt.headless=true -cp

Add -Dorg.rosuda.jrs.host=localhost before -cp.

- Finally, run embeddedTomcat.sh from the opened console.

5.- If you are going to use JRI, you'll need to run the embeddedTomcat.sh/embeddedTomcat.bat file from the opened console.

SmartGwtR sample

You can download the sample from here. Unzip it and put the SmartGwtR folder into the samples/ folder of a SmartGWT SDK.

To run the sample, you need to have ANT configured. Then open a console and run the following:

1.- Copy the libraries of Rserve or JRI into the WEB-INF/lib/ folder of the sample.

2.- Open a console and go to the SmartGwtR folder. Run the following:

Windows machine: set path=%path%;C:\Program Files\R\R-2.15.3\bin\(x64 or i386);C:\Program Files\R\R-2.15.3\library\rJava\jri\(x64 or i386) (You need to verify this path in your R installation)

Linux machine: setenv R_HOME /usr/lib64/R (You need to verify this path in your R installation)

3.- Compile the project by running: ant

4.- If you are going to use Rserve, you'll need to do the following:

Windows machine:

- Run R, once you are in R's console, run these commands:

library(Rserve) (Enter)

Rserve() (Enter)

- Finally, run: 'ant -buildfile build-rserve.xml hosted'.

Linux machine:

- Run 'R CMD Rserve' from the opened console.

- Finally, run: 'ant -buildfile build-rserve.xml hosted'.

5.- If you are going to use JRI, you'll need to run: 'ant -buildfile build-jri.xml hosted'.

 

Finally, if you are testing the samples on a Windows machine, you'll need to make a change in the RChartDS.ds.xml dataSource. Open it and search '/tmp/', finally replace it with for example: 'c:/myFolder/'.