Monday, May 12, 2014

Java Asynchronous NIO File Reading Example

If you want to read a file using Java AsynchronousFileChannel, following sample code might help you to get started.
package com.ruchira.samples;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class AsyncReader {
    public static void main(String[] args) throws
            IOException, InterruptedException, ExecutionException {
        String fileName = "/Users/ruchira/Downloads/README.md";
        completionHandler(fileName);
        future(fileName);
    }

    private static void completionHandler(String fileName)
            throws InterruptedException, IOException, ExecutionException {
        final Thread current = Thread.currentThread();
        final StringBuilder content = new StringBuilder();
        Path path = Paths.get(fileName);
        final AsynchronousFileChannel ch = AsynchronousFileChannel.open(path);
        final ByteBuffer buf = ByteBuffer.allocate(1024);
        ch.read(buf, 0, 0,
                new CompletionHandler() {
                    public void completed(Integer result, Integer length) {
                        if (result == -1) {
                            try {
                                ch.close();
                            } catch (IOException e) {
                                //ignored
                            }
                            current.interrupt();
                            return;
                        }
                        buf.flip();
                        content.append(new String(buf.array()));
                        /*while (buf.hasRemaining()) {
                            content.append((char) buf.get());
                        }*/
                        buf.clear();
                        ch.read(buf, length, length + result, this);
                    }
                    
                    public void failed(Throwable exc, Integer length) {
                        System.out.println("Failure: " + exc.toString());
                    }
                }
        );

        System.out.println("You can use this thread for another task");
        try {
            current.join();
        } catch (InterruptedException e) {
            //ignored
        }
        System.out.println(content);
    }

    private static void future(String fileName)
            throws InterruptedException, IOException, ExecutionException {
        StringBuilder content = new StringBuilder();

        int start = 0;
        Path path = Paths.get(fileName);
        AsynchronousFileChannel ch = AsynchronousFileChannel.open(path);
        ByteBuffer buf = ByteBuffer.allocate(1024);
        Future result = ch.read(buf, start);
        int length;
        while ((length = result.get()) != -1) {
            while (!result.isDone()) {
                System.out.println("You can use this thread for another task");
                Thread.sleep(100);
            }
            buf.flip();
            content.append(new String(buf.array()));
            /*while (buf.hasRemaining()) {
                content.append((char) buf.get());
            }*/
            buf.clear();
            result = ch.read(buf, start);
            start += length;
        }
        ch.close();
        System.out.println(content);
    }
}

Sunday, March 16, 2014

Generate MD5 Sum of Multiple Files

If you want to get the MD5 sum of multiple files at once, following command can be used. This could be useful when you want to compare two set of directories/products to see the difference between them.

i.e.  You can create jarsums1.txt, jarsums2.txt and check the difference using diff command.
find . -iname "*.jar" | sort | xargs md5sum > jarsums.txt

Thursday, February 27, 2014

Pub-Sub with WSO2 User Engagement Server

If you are writing any custom dashboards with WSO2 UES which requires inter-gadget communication or dashboard-gadgets communications, then you may find following codes helpful.
  1. Create a Jaggery app named pubsub-dashboard.
  2. Create g1.xml, g2.xml and index.html with following contents in pubsub-dashboard app.
  3. Go to http://localhost:9763/pubsub-dashboard.
  4. You will see g1 is publishing messages while g2 is receiving.
  5. If you click on the "Clear" button, then the log at g2 will be cleared.
g1.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
    <ModulePrefs title="G1" height="350" description="g1">
        <Require feature="pubsub-2"/>
        <Require feature="dynamic-height"/>
    </ModulePrefs>
    <Content type="html">
        <![CDATA[
        <head>
            <style type="text/css">
                .log {
                    width: 400px;
                    height: 400px;
                    background-color: #415b76;
                    color: #fff;
                }
            </style>
            <script language="javascript" type="text/javascript" src="/portal/js/flot/jquery.js"></script>
            <script>
                gadgets.HubSettings.onConnect = function() {
                    var id = 0;
                    setInterval(function() {
                        $('.log').append('<div>publishing message from g1, id : ' + (++id) + '</div>');
                        gadgets.Hub.publish('org.wso2.ues.samples.ch', {
                            msg : 'A message from g1',
                            id: id
                        });
                    }, 2000);
                };
            </script>
        <head>
        <body>
            <div class="log"></div>
        </body>
        ]]>
    </Content>
</Module>

g2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
    <ModulePrefs title="G2" height="350" description="g2">
        <Require feature="pubsub-2">
            <Param name="topics">
                <![CDATA[
                <Topic title="geodata"
                    name="org.wso2.ues.samples.ch"
                    description="sample channel to demonstrate intergadget communication"
                    type="object"
                    subscribe="true"/>
               ]]>
            </Param>
        </Require>
        <Require feature="dynamic-height"/>
    </ModulePrefs>
    <Content type="html">
        <![CDATA[
        <head>
            <style type="text/css">
                .log {
                    width: 400px;
                    height: 400px;
                    background-color: #1abc9c;
                    color: #fff;
                }
            </style>
            <script language="javascript" type="text/javascript" src="/portal/js/flot/jquery.js"></script>
            <script>
                gadgets.HubSettings.onConnect = function() {
                    gadgets.Hub.subscribe('org.wso2.ues.samples.ch', function(topic, data, subscriberData) {
                        if(data.type === 'clear') {
                            $('.log').empty();
                            return;
                        }
                        $('.log').append('<div>message received, id: ' + data.id + '</div>');
                    });
                };
            </script>
        <head>
        <body>
            <div class="log"></div>
        </body>
        ]]>
    </Content>
</Module>

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <style type="text/css">
        #gadget-1, #gadget-2 {
            width: 500px;
            height: 400px;
            margin-bottom: 20px;
        }
    </style>
    <script language="javascript" type="text/javascript" src="/portal/js/flot/jquery.js"></script>
    <script src="/portal/themes/portal/js/shindig.js"></script>
    <script src="/portal/themes/portal/js/UESContainer.js"></script>
</head>
<body>
<div>
    <button class="clear" type="button">Clear</button>
</div>
<h4>g1</h4>
<div id="gadget-1"></div>
<h4>g2</h4>
<div id="gadget-2"></div>
<script>
    UESContainer.renderGadget('gadget-1', 'http://localhost:9763/pubsub-dashboard/g1.xml');
    UESContainer.renderGadget('gadget-2', 'http://localhost:9763/pubsub-dashboard/g2.xml');

    $(function () {
        $('.clear').click(function () {
            UESContainer.inlineClient.publish('org.wso2.ues.samples.ch', {
                msg: 'publishing message from dashboard',
                type: 'clear'
            });
        });
    });
</script>
</body>
</html>

Wednesday, February 26, 2014

Embedding gadgets in WSO2 User Engagement Server

WSO2 User Engagement Server aims towards creating any of your complex dashboards very easily. It has a build-in dashboard designer where you can create dynamic layouts and embed gadgets.

But in case you have a requirement of doing it manually, the following code will help you to get started. Put following code as an HTML/Jag file in UES and access through HTTP.
<!DOCTYPE html>
<html lang="en">
<head>
    <script src="/portal/themes/portal/js/shindig.js"></script>
    <script src="/portal/themes/portal/js/UESContainer.js"></script>
    <style type="text/css">
        #gadget-1, #gadget-2, #gadget-3 {
            width: 500px;
            height: 400px;
        }
    </style>
</head>
<body>
<div id="gadget-1"></div>
<div id="gadget-2"></div>
<!-- Embedding gadget via url -->
<iframe id="gadget-3"
        src="http://localhost:9763/gadgets/ifr?url=http://localhost:9763/portal/gadgets/pie-chart/pie-chart.xml">
</iframe>
<script>
    //UES gadget
    UESContainer.renderGadget('gadget-1', 'http://localhost:9763/portal/gadgets/bar-chart/bar-chart.xml');
    //External gadget
    UESContainer.renderGadget('gadget-2', 'http://hosting.gmodules.com/ig/gadgets/file/103035663984448200053/Google_Currency_Converter.xml');
</script>
</body>
</html>

Monday, February 10, 2014

What caramel is really

For anyone who doesn't know the context of the subject, it is not about the caramel that you taste, it is about a framework developed to write web application using a Jaggery server. Again, if you are not aware of Jaggery, it is a server side JavaScript runtime where you can write your whole web application using JavaScript.

In WSO2, we initiated this new server runtime named Jaggery to take the advantage of JavaScript expertise to write the backend of a web application as well. In today’s web pages, JavaScript can be seen everywhere. Hence, we wanted to take it to the server side, making JavaScript everywhere in the server side as well.

Yes, we made it. Thanks to Jaggery, now JavaScript is everywhere in the server side too. You neither need to learn a completely different language nor to earn a higher skill set to write your web applications. If you know HTML, CSS and JavaScript, then you are ready to go. You can straightaway start writing your whole web application.

We started developing our web apps. Like the frontend JavaScript codes, our server side code base too grew up day by day. Just a single developer couldn't handle it anymore. Many developers engaged on app developments. There were many repeating UI elements with same HTML, CSS and JavaScript. People started duplicating same thing here and there making it very very hard to maintain.

Hence, we tried to come up with a solution for immediate problems that we had. i.e.

  • Reducing UI code duplication
  • Multiple theme support
  • Dynamic theme switching based on tenant etc.
  • Theme customizing and extending

We initially came up with something based on Drupal's UI block concept, multiple theme support and later improved to suit our requirements and Jaggery runtime.

In Jaggery, all requests end up in *.jag files(Note: There is an ongoing development which improve this model by making it more extensible). It goes through url mappings that you defined in jaggery.conf and reaches a *.jag file at the end. In initial caramel version, we didn't want to change that routing mechanism and our only concern was about theming our data. Routing mechanism was out of the scope from initial caramel version and users were allowed go with their preferences.

caramel introduces multiple themes support where you can switch them dynamically for the request, based on your logic. i.e. Either admin users or tenants might have a completely different theme of their preference. With caramel core, we tries to minimize the conventions/restrictions it imposes on your apps. All you need to do is, passing the data that need to be themed into caramel.render() method from whatever the page you need. After that caramel flow gets the theme for that specific request and allow the theme to render as it wants. 

A theme, can decide either to send the data as a JSON or generate HTML from that data and send. Simply, the theme has complete control over what user sees or receives for that request. For the same application, you can define multiple themes based on different templating languages. e.g. One theme based on Handlebars, one theme based on Dustjs etc. or a theme without any templating language at all. This nature allows any developer to re-theme the whole caramel app using his own templating preferences, without touching anything of the web application code.

As I mentioned earlier, caramel core hands everything over to the selected theme and themes have the freedom of rendering those data. For our apps, the templating framework we selected is Handlebars. Hence, we came up with a Handlebars theme engine for caramel core, which enforces a well defined structure for our themes, reuses repeating UI elements across each page etc. 

As an example, in one of our web applications, we wanted to include a tag cloud in each page. We could have easily done it with just a Handlebars partial and include it wherever it needs. But that is not the case, tag cloud is not just the HTML, it contains its own CSS and optionally JavaScript codes as well. We will have to always make sure that, those are also included along with tag cloud. Whenever we fix something in the tag cloud, we will have to make sure it is reflected in each place. Hence, we tried to address this aspect through Handlebars engine by virtually grouping HTML, CSS, JavaScript together. i.e. When the tag cloud is added into a page, Handlebars engine will automatically add the relevant CSS and JavaScript too into that page.

That’s a brief information, but not everything about the Handlebars engine that I mentioned here and there. Anyway, if anyone is not happy with the Handlebars engine that we have, they can always write their own engines and themes for the same app, neither modifying existing themes nor web application source.

Let’s say someone wanted to do several minor modifications to one of the existing themes. Then he will have to write a completely new theme with those changes. Fortunately, with caramel, you can create a sub theme with an existing theme. Whatever the files you need to override, can be placed in your sub theme. caramel will first try to load any file from your sub theme and then from the parent theme. With this model, one can customize an existing theme up to some extent even without touching it at all.

I hope, above would be enough for you to understand what and why caramel is. We developed caramel with the experiences and the ideas we had, which neither mean it is right nor the best. Probably you might have better suggestions and experiences. Hence, please contribute your ideas and improvements back to fix what is wrong and what is missing.

You can find the caramel source at [2] and a sample application at [3]. Diagrams at [4] and [5] will help you to understand caramel core and Handlebars engine respectively. 

[1] http://jaggeryjs.org
[2] https://github.com/wso2/caramel
[3] https://github.com/ruchiraw/wso2-samples-store
[4] https://raw.github.com/ruchiraw/wso2-samples-store/master/docs/caramel-core.jpg
[5] https://raw.github.com/ruchiraw/wso2-samples-store/master/docs/caramel.handlebars.jpg

Friday, April 12, 2013

Creating CSV Files from a Proxy Service

If you want to write a CSV file from a proxy service in WSO2 ESB, then following sample would help you to understand how it works.

  1. Uncomment the following lines in wso2esb-4.x.x/repository/conf/axis2/axis2.xml.
    <transportreceiver class="org.apache.synapse.transport.vfs.VFSTransportListener" name="vfs"/>
    <transportsender class="org.apache.synapse.transport.vfs.VFSTransportSender" name="vfs"/>
  2. Add below content into the resource called /_system/governance/xml2csv.xslt
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" encoding="iso-8859-1"/>
    
    <xsl:strip-space elements="*" />
    
    <xsl:template match="/*/child::*">
     <xsl:for-each select="child::*">
      <xsl:if test="position() != last()"><xsl:value-of select="normalize-space(.)"/>,</xsl:if>
      <xsl:if test="position()  = last()"><xsl:value-of select="normalize-space(.)"/><xsl:text>
    </xsl:text></xsl:if>
     </xsl:for-each>
    </xsl:template>
    
    </xsl:stylesheet>
    
  3. Create a proxy service named CSVProxy with the following content.
    <proxy xmlns="http://ws.apache.org/ns/synapse" name="CSVProxy" transports="https,http" 
        statistics="disable" trace="disable" startOnLoad="true">
       <target>
          <inSequence>
             <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/>
             <payloadFactory>
                <format>
                   <data>
                      <row>
                         <firstName>Ruchira</firstName>
                         <lastName>Wageesha</lastName>
                      </row>
                      <row>
                         <month>May</month>
                         <date>31</date>
                         <year>1984</year>
                      </row>
                      <row>
                         <street>Flower Road</street>
                         <province>Western</province>
                         <city>Colombo</city>
                         <country>Sri Lanka</country>
                         <postalCode>0007</postalCode>
                      </row>
                   </data>
                </format>
             </payloadFactory>
             <xslt key="gov:/xml2csv.xslt"/>
             <property name="transport.vfs.ReplyFileName" expression="fn:concat(fn:substring-after(get-property('MessageID'), 'urn:uuid:'), '.csv')" scope="transport"/>
             <property name="OUT_ONLY" value="true"/>
             <send>
                <endpoint>
                   <address uri="vfs:file:///home/ruchira/csv"/>
                </endpoint>
             </send>
             <drop/>
          </inSequence>
       </target>
    </proxy>                               
    
    Note : vfs:file:///home/ruchira/csv is the path where generated csv files are stored. Hence replace it with a valid path in your machine.
  4. Invoke the CSVProxy service by issuing an HTTP GET to http://localhost:8280/services/CSVProxy
  5. CSV files will be created in the above specified path.

Thursday, April 11, 2013

Sending JSON Responses from a Proxy Service in WSO2 ESB

If you have come up with the requirement of sending JSON responses from a proxy service, then following proxy configuration can be used straightaway to try that out.

i.e. Create a proxy service named JSONProxy with the following content. Then, you can try that out by issuing HTTP GET to http://localhost:8280/services/JSONProxy

Proxy Configuration
<proxy xmlns="http://ws.apache.org/ns/synapse" name="JSONProxy" transports="https,http" 
       statistics="disable" trace="disable" startOnLoad="true"> 
   <target> 
      <inSequence> 
         <header name="To" action="remove"/> 
         <property name="RESPONSE" value="true"/> 
         <property name="NO_ENTITY_BODY" scope="axis2" action="remove"/> 
         <payloadFactory> 
            <format> 
               <person> 
                  <fullName> 
                     <firstName>Ruchira</firstName> 
                     <lastName>Wageesha</lastName> 
                  </fullName> 
                  <birthDate> 
                     <month>May</month> 
                     <date>31</date> 
                     <year>1984</year> 
                  </birthDate> 
                  <address> 
                     <street>Flower Road</street> 
                     <province>Western</province> 
                     <city>Colombo</city> 
                     <country>Sri Lanka</country> 
                     <postalCode>0007</postalCode> 
                  </address> 
               </person> 
            </format> 
         </payloadFactory> 
         <property name="messageType" value="application/json" scope="axis2"/> 
         <send/> 
      </inSequence> 
   </target> 
   <description></description> 
</proxy>
JSON Response
{
    "person": {
        "fullName": {
            "firstName": "Ruchira",
            "lastName": "Wageesha"
        },
        "birthDate": {
            "month": "May",
            "date": "31",
            "year": "1984"
        },
        "address": {
            "street": "Flower Road",
            "province": "Western",
            "city": "Colombo",
            "country": "Sri Lanka",
            "postalCode": "0007"
        }
    }
}