Tuesday, February 12, 2013

Quick Start Cassandra with Java Client Hector


I found Cassandra is very useful data store for different kinds of applications. 

Apache Cassandra is an open source, distributed, decentralized, elastically scalable, highly available, fault-tolerant, tunably consistent, column-oriented database that bases its distribution design on Amazon’s Dynamo and its data model on Google’s Bigtable. Created at Facebook, it is now used at some of the most popular sites on the Web. It is distributed and decentralized, elastic scalable, and with high availability, fault tolerance, tuneable consistency. 

Data in Cassandra is designed to be distributed over several machines/nodes operating together that appear as a single instance to the end user. Cassandra assigns data to nodes in the cluster by arranging them in a ring. A cluster is a container for keyspaces. And keyspace consists of column families and columns.  I like to view Cassandra column families as a four-dimensional hash:

[Keyspace][ColumnFamily][Key][Column]

I am using Cassandra  version 0.8 for this posting and you can find more information at  http://www.datastax.com/docs/0.8/

I following the wiki page to install Cassandra 0.8 at localhost port 9160. 

There are number of choices of client, they are listed at http://wiki.apache.org/cassandra/ClientOptions.   Personally, I like Hector

  


Here is mvn project file:
    
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.lei.cassandra.sample</groupId>
  <artifactId>cassandra-quickstart-with-hector</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>cassandra-quickstart-with-hector</name>
  <url>http://maven.apache.org</url>

  <properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
 <dependency>
 <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
 </dependency>

 <dependency>
  <groupId>me.prettyprint</groupId>
  <artifactId>hector-core</artifactId>
  <version>1.0-2</version>
 </dependency>

 <dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.6.1</version>
 </dependency>

  </dependencies>
</project>



connect/disconnect from Cassandra cluster:
    
 /**
  * Connect to Cassandra's demo keyspace at localhost's cluster at 9160  
  */
 public void connect() {
  cluster = HFactory.getOrCreateCluster(DEF_CLUSTER, DEF_LOCALHOST_ID);
  System.out.println("Cluster instantiated");
  ConfigurableConsistencyLevel ccl = new ConfigurableConsistencyLevel();
  ccl.setDefaultReadConsistencyLevel(HConsistencyLevel.ONE);
  keyspace = HFactory.createKeyspace("demo", cluster, ccl);
 }

 /**
  * Disconnect from localhost's Cassandra cluster at 9160  
  */
 public void disconnect() {
  cluster.getConnectionManager().shutdown();
 }





To insert a row and its columns:
    
 /**
  * insert a row and its columns for the rowkey 
  * 
  * @return row's column values associated the rowkey
  */
    public List<Object> insertRow () {
     String rowkey = "mike_johnson";
     
     ColumnFamilyTemplate<String, String> userCFTemplate = new ThriftColumnFamilyTemplate<String, String>(
       this.getKeyspace(),
       DEF_USER_CF_NAME, StringSerializer.get(), StringSerializer.get());

     ColumnFamilyUpdater<String, String> updater = userCFTemplate.createUpdater(rowkey);
     updater.setString("full_name", "mike johnson");
     updater.setString("address", "90 Elm Street");
     updater.setString("state", "NY");
     updater.setString("gender", "male");
     updater.setString("email", "mikej@gmail.com");
     updater.setLong("birth_year", new Long(1980));
     try {
      userCFTemplate.update(updater);
      System.out.println("value inserted");
     } catch (HectorException e) {
      System.out.println("Error during insertion : " + e.getMessage() );

     }  
     
     return this.getColumnValues("mike_johnson");
    }


 /**
  * insert a row and its columns with the rowkey using Mutator  
  * 
  * @return row's column values associated the rowkey
  */
    public List<Object> insertRowV2 () {
     Mutator<String> mutator = HFactory.createMutator(this.getKeyspace(), StringSerializer.get() );
     mutator.addInsertion("michael_johnson", "users", HFactory.createStringColumn("full_name", "michael johnson"));
     mutator.addInsertion("michael_johnson", "users", HFactory.createStringColumn("address", "9890 Elm Street"));
     mutator.addInsertion("michael_johnson", "users", HFactory.createStringColumn("state", "CT"));
     mutator.addInsertion("michael_johnson", "users", HFactory.createStringColumn("gender", "male"));
     mutator.addInsertion("michael_johnson", "users", HFactory.createStringColumn("email", "michael_johnson@gmail.com"));
     mutator.addInsertion("michael_johnson", "users", HFactory.createColumn("birth_year", new Long(1978), StringSerializer.get(), LongSerializer.get()));
     
     MutationResult mr = mutator.execute();
     
     return this.getColumnValues("michael_johnson");
    }




To query a column value for a key:
    

 /**
  * build the query for fetching a row's column
  * 
  * @return QueryResult
  */
    public QueryResult<HColumn<String,String>> query() {        
        ColumnQuery<String, String, String> columnQuery = HFactory.createStringColumnQuery(this.getKeyspace());
        columnQuery.setColumnFamily(DEF_USER_CF_NAME);
        columnQuery.setKey("john_smith");
        columnQuery.setName("full_name");
        QueryResult<HColumn<String, String>> result = columnQuery.execute();
        return result;
    }




To query columnis for a key:
    

 /**
  * build the query for fetching a row's columns 
  * 
  * @return QueryResult
  */
    public QueryResult<ColumnSlice<String,String>> query() {
        SliceQuery<String, String, String> sliceQuery = 
            HFactory.createSliceQuery(this.getKeyspace(), StringSerializer.get(), StringSerializer.get(), StringSerializer.get());
        sliceQuery.setColumnFamily(DEF_USER_CF_NAME);
        sliceQuery.setKey("mary_leaman");
        sliceQuery.setColumnNames("email","full_name","gender","address", "state");
        QueryResult<ColumnSlice<String, String>> result = sliceQuery.execute();
        return result;
    }



To fetch column values:
    

 /**
  * getColumnVlues as a list objects from the rowkey
  *  
  * @param rowkey
  * 
  * @return row's column values associated the rowkey
  */
    public List<Object> getColumnValues (String rowkey) {
     List<Object> listColumnObj = new LinkedList<Object> ();
     
  try {
      ColumnFamilyTemplate<String, String> userCFTemplate = new ThriftColumnFamilyTemplate<String, String>(
        this.getKeyspace(),
        DEF_USER_CF_NAME, StringSerializer.get(), StringSerializer.get());

   ColumnFamilyResult<String, String> result = userCFTemplate.queryColumns(rowkey);
   String full_name = result.getString("full_name");
   String address = result.getString("address");
   String state = result.getString("state");
   String gender = result.getString("gender");
   String email = result.getString("email");
   Long birth_year = result.getLong("birth_year");
   
   System.out.println("full_name [" + full_name + "]");
   System.out.println("address [" + address + "]");
   System.out.println("state [" + state + "]");
   System.out.println("gender [" + gender + "]");
   System.out.println("birth_year [" + birth_year + "]");
   System.out.println("email [" + email + "]");
   
   listColumnObj.add(full_name);
   listColumnObj.add(address);
   listColumnObj.add(state);
   listColumnObj.add(gender);
   listColumnObj.add(email);
   listColumnObj.add(birth_year);
   
     } catch (HectorException e) {
      System.out.println("Error reading the column with key " + rowkey);
      System.out.println(e.getMessage());
     }

  return listColumnObj;
    }



To delete a row:
    

 /**
  * delete the row with rowkey
  * 
  * @return row's column values associated the rowkey
  */
    public List<Object> deleteRow() {
     
     List<Object> list = this.insertRow();
     String rowkey = "mike_johnson";
     ColumnFamilyTemplate<String, String> userCFTemplate = new ThriftColumnFamilyTemplate<String, String>(
       this.getKeyspace(),
       DEF_USER_CF_NAME, StringSerializer.get(), StringSerializer.get());
     try {
      userCFTemplate.deleteRow(rowkey) ;
      System.out.println("Row deleted .... ");
     } catch (HectorException e) {
      System.out.println("Error during deletion : " + e.getMessage() );
     }  
     
     return this.getColumnValues("mike_johnson");
    }


 /**
  * delete the row with rowkey using Mutator
  * 
  * @return row's column values associated the rowkey
  */
    public List<Object> deleteRowV2() {
     
     List<Object> list = this.insertRowV2();
     String rowkey = "michael_johnson";
     
        Mutator<String> mutator = HFactory.createMutator(this.getKeyspace(), StringSerializer.get() );
        mutator.addDeletion(rowkey, "users", null, StringSerializer.get() );
        MutationResult mr = mutator.execute();
     return this.getColumnValues(rowkey);
    }


To delete a row column :
    

 /**
  * delete a column value associated with rowkey 
  * 
  * @return row's column values associated the rowkey 
  */
    public List<Object> deleteRowColumn () {
     
     List<Object> list = this.insertRow();
     String rowkey = "mike_johnson";
     
     ColumnFamilyTemplate<String, String> userCFTemplate = new ThriftColumnFamilyTemplate<String, String>(
       this.getKeyspace(),
       DEF_USER_CF_NAME, StringSerializer.get(), StringSerializer.get());

     try {
      userCFTemplate.deleteColumn(rowkey, "email") ;
      System.out.println("Column deleted .... ");
     } catch (HectorException e) {
      System.out.println("Error during deletion : " + e.getMessage() );

     }  
     return this.getColumnValues("mike_johnson");
    }




To list all rows:
    

 /**
  * Demo to list all rows 
  * 
  * @param maxRowCount
  * @return
  */
 private List<Object> listAllEntries(int maxRowCount) {
     
     List<Object> list = new ArrayList<Object> ();

     RangeSlicesQuery<String, String, ByteBuffer> rangeSlicesQuery = 
          HFactory.createRangeSlicesQuery(this.getKeyspace(), StringSerializer.get(), StringSerializer.get(), ByteBufferSerializer.get())
            .setColumnFamily(DEF_USER_CF_NAME)
            .setRange(null, null, false, 10)
            .setRowCount(maxRowCount);

        String last_key = null;

        while (true) {
         rangeSlicesQuery.setKeys(last_key, null);

         QueryResult<OrderedRows<String, String, ByteBuffer>> result = rangeSlicesQuery.execute();
         OrderedRows<String, String, ByteBuffer> rows = result.get();
         Iterator<Row<String, String, ByteBuffer>> rowsIterator = rows.iterator();


            if (last_key != null && rowsIterator != null) {
             rowsIterator.next();   
            }

            while (rowsIterator.hasNext()) {
             Row<String, String, ByteBuffer> row = rowsIterator.next();
             last_key = row.getKey();

             if (row.getColumnSlice().getColumns().isEmpty()) {
              continue;
             }

             System.out.println("rowKey [" + row.getKey() + "]");
             
             for(HColumn<String, ByteBuffer> cols : row.getColumnSlice().getColumns())
             {
              String colName = cols.getName();
           System.out.println("colName [" + colName + "] colValue [" + getColumnValue(colName, cols.getValue() ) + "]");
             }
             list.add(row);
             
            }

            if (rows.getCount() < maxRowCount) {
                break;
            }
        }
        
        return list;
    }




Demo MultigetSliceQuery with multiple keys:
    

 /**
  * build the query with multiple keys
  * 
  * @return QueryResult
  */
    public QueryResult<Rows<String,String,String>> query() {        
        MultigetSliceQuery<String, String, String> multigetSlicesQuery =
          HFactory.createMultigetSliceQuery( this.getKeyspace(), StringSerializer.get(), StringSerializer.get(), StringSerializer.get());
        multigetSlicesQuery.setColumnFamily(DEF_USER_CF_NAME);
        multigetSlicesQuery.setColumnNames("email","full_name","gender","address", "state");        
        multigetSlicesQuery.setKeys("mary_leaman","john_smith");
        QueryResult<Rows<String, String, String>> results = multigetSlicesQuery.execute();
        return results;
    }



You can access the code at Github:
https://github.com/leizou123/cassandra-quickstart-with-hector.git

Have fun and enjoy the journey.

Wednesday, January 23, 2013

Quick Start Maven archetype for simple Servlet application - Hello World

Hey Joe,

Maven is the better way to go. For a very simple "Hello World!" Servlet app, you can get it going in 5 minutes or less. Just follow my steps below.

You need to install mvn. 3..0.3 is the version that I am using.


lei:webapps lei$ mvn -version
Apache Maven 3.0.3 (r1075438; 2011-02-28 09:31:09-0800)
Maven home: /usr/local/apache-maven-3.0.3


Prerequisite: assume you have a Servlet container installed on your system. I have apache-tomcat-7 installed at /usr/local/apache-tomcat-7.0.12.

Step 1: Use Maven archetype maven-archetype-webapp to start the mvn project. 


lei:tmp lei$ mvn archetype:create -DgroupId=com.lei.webapp.quickstart  -DartifactId=webapp-quick-start   -DarchetypeArtifactId=maven-archetype-webapp  
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:create (default-cli) @ standalone-pom ---
[WARNING] This goal is deprecated. Please use mvn archetype:generate instead
[INFO] Defaulting package to group ID: com.lei.webapp.quickstart
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-webapp:RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.lei.webapp.quickstart
[INFO] Parameter: packageName, Value: com.lei.webapp.quickstart
[INFO] Parameter: package, Value: com.lei.webapp.quickstart
[INFO] Parameter: artifactId, Value: webapp-quick-start
[INFO] Parameter: basedir, Value: /Users/lei/tmp
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /Users/lei/tmp/webapp-quick-start
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.860s
[INFO] Finished at: Wed Jan 23 10:55:02 PST 2013
[INFO] Final Memory: 7M/265M
[INFO] ------------------------------------------------------------------------
lei:tmp lei$ 


Step 2: Add servlet API javax.servlet as a Maven dependency to the mvn project file pom.xml. 


lei:tmp lei$ cd webapp-quick-start 
lei:webapp-quick-start lei$ vi pom.xml 

Add the following to pom.xml: 

<dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
</dependency>


Step 3: Add HelloServlet.java

lei:webapp-quick-start lei$ mkdir -p src/main/java/com/lei/webapp/quickstart 
lei:webapp-quick-start lei$ vi src/main/java/com/lei/webapp/quickstart/HelloServlet.java 

Add the following Java code:

package com.lei.webapp.quickstart;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// com.lei.webapp.quickstart.HelloServlet
public class HelloServlet extends HttpServlet {
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException {

        PrintWriter out = response.getWriter();
        out.println( "Hello World!" );
        out.flush();
        out.close();
    }
}


Step 4: Modify web.xml:

lei:webapp-quick-start lei$ vi ./src/main/webapp/WEB-INF/web.xml 

Add the following content: 

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <servlet>
        <servlet-name>HelloWorld</servlet-name>
        <servlet-class>com.lei.webapp.quickstart.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloWorld</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

Step 5: Compile and package:

lei:webapp-quick-start lei$ mvn compile test package
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building webapp-quick-start Maven Webapp 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ webapp-quick-start ---
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ webapp-quick-start ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ webapp-quick-start ---
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ webapp-quick-start ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ webapp-quick-start ---
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/lei/tmp/webapp-quick-start/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ webapp-quick-start ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ webapp-quick-start ---
[INFO] No tests to run.
[INFO] Surefire report directory: /Users/lei/tmp/webapp-quick-start/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
There are no tests to run.

Results :

Tests run: 0, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ webapp-quick-start ---
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ webapp-quick-start ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ webapp-quick-start ---
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/lei/tmp/webapp-quick-start/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ webapp-quick-start ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ webapp-quick-start ---
[INFO] No tests to run.
[INFO] Skipping execution of surefire because it has already been run for this configuration
[INFO] 
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ webapp-quick-start ---
[INFO] Packaging webapp
[INFO] Assembling webapp [webapp-quick-start] in [/Users/lei/tmp/webapp-quick-start/target/webapp-quick-start]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/lei/tmp/webapp-quick-start/src/main/webapp]
[INFO] Webapp assembled in [148 msecs]
[INFO] Building war: /Users/lei/tmp/webapp-quick-start/target/webapp-quick-start.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.091s
[INFO] Finished at: Wed Jan 23 11:13:02 PST 2013
[INFO] Final Memory: 5M/265M
[INFO] ------------------------------------------------------------------------
lei:webapp-quick-start lei$ 

Step 6: Deploy the webapp-quick-start.war to the weapps, where you have your Tomcat installed:

lei:webapp-quick-start lei$ cp -v /Users/lei/tmp/webapp-quick-start/target/webapp-quick-start.war /usr/local/apache-tomcat-7.0.12/webapps 
/Users/lei/tmp/webapp-quick-start/target/webapp-quick-start.war -> /usr/local/apache-tomcat-7.0.12/webapps/webapp-quick-start.war


Step 7: Start the Tomcat, and verify the following URL to verify it's working. 

http://localhost:4080/webapp-quick-start/hello

As you can tell, I have my Tomcat installed on part 4080. 

Have fun and enjoy the journey.




Saturday, January 5, 2013

Quick Start CEP with Esper - Stock Trading

Event processing is a method of tracking and analyzing (processing) streams of information (data) about things that happen (events), and deriving a conclusion from them. Complex event processing (CEP) is event processing that combines data from multiple sources to infer events or patterns that suggest more complicated circumstances. The goal of complex event processing is to identify meaningful events (such as opportunities or threats) and respond to them as quickly as possible. These events may be sales leads, orders or customer service calls. Or, they may be news items, text messages, social media posts, stock market feeds, traffic reports, weather reports, or other kinds of data. An event may also be defined as a "change of state," when a measurement exceeds a predefined threshold of time, temperature, or other value. You can find more about CEP at the wiki page

I use Esper as the CEP engine for this blogger to illustrate the complex event processing. Esper is open-source software available under the GNU General Public License (GPL). You can find it at http://esper.codehaus.org/. 

The financial services was an early adopter of CEP technology, using complex event processing to structure and contextualize available data so that it could inform trading behavior, specifically algorithmic trading, by identifying opportunities or threats that indicate traders (or automatic trading systems) should buy or sell.

Here is an example. Suppose a day trader Joe wants to buy Google, symbol GOOG. His target price is set at $90, and he likes to buy at a dip where price hits 10% below the moving average. Here is the Esper code to illustrate the stock trade:
    
public class StockTrade {

 private static final Log log = LogFactory.getLog(StockTrade.class);

  public static class StockTradeListener implements UpdateListener {
   private double avgPrice=-1.0;
   private double minPrice=-1.0;
   private double targetPrice=0.0;
   private double dropPercetage=0.0;
   private boolean buyNow=false;
   
   public void update(EventBean[] newEvents, EventBean[] oldEvents) {
          
    if (newEvents == null || newEvents.length == 0) return;

    EventBean event = newEvents[0];
    avgPrice = event.get("AvgPrice") == null ? 0.0 :  (double)(((Double)event.get("AvgPrice")).doubleValue());
    minPrice = event.get("MinPrice") == null ? 0.0 :  (double)(((Double)event.get("MinPrice")).doubleValue());
    
    if (avgPrice>0 && minPrice>0) {
     log.info("minPrice=" + minPrice + " avgPrice=" + avgPrice);
     if (minPrice<=targetPrice && minPrice<= (avgPrice* (100.00-dropPercetage) /100.00  ) ) {
      buyNow=true;
      log.info("buyNow=" + buyNow);
     }
    }
   }

   public StockTradeListener(double targetPrice, double dropPercetage) {
   super();
   this.targetPrice = targetPrice;
   this.dropPercetage = dropPercetage;
  }

  public boolean isBuyNow() { return buyNow; }
 }

 public boolean runStockTrade(String symbal, long interval, 
   double [] arrayPrice, double targetPrice, double dropPercetage ) throws InterruptedException {


  log.info("symbal=" + symbal);
  log.info("interval=" + interval);
  log.info("targetPrice=" + targetPrice);
  log.info("percetageDrop=" + dropPercetage);
  log.info("arrayPrice=" + Arrays.toString(arrayPrice));
  
        // Configuration
        Configuration config = new Configuration();
        config.addEventTypeAutoName("com.lei.cep.event");
        EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(config);
        epService.initialize();

        // Creating a Statement
        String expression = "select avg(price) as AvgPrice, min(price) as MinPrice from PriceEvent.win:time(30 sec)";
        EPStatement statement = epService.getEPAdministrator().createEPL(expression);

        // Adding a Listener
        StockTradeListener listener = new StockTradeListener(targetPrice, dropPercetage);
        statement.addListener(listener);

        for (int i=0; i<arrayPrice.length; i++) {
            // Sending events
            Thread.sleep(interval);
            PriceEvent event = new PriceEvent(symbal, arrayPrice[i]);
            epService.getEPRuntime().sendEvent(event);
            if ( listener.isBuyNow() ) return true; 
        }

  return false;
 }

}

Here is the event bean Java class:
 
public class PriceEvent {

 private String symbol;
    private double price;
 
    
    public PriceEvent(String symbol, double price) {
  super();
  this.symbol = symbol;
  this.price = price;
 }

 public double getPrice() { return price; }
 public void setPrice(double price) { this.price = price; }

 public String getSymbol() { return symbol; }
 public void setSymbol(String symbol) { this.symbol = symbol; }

 @Override
 public String toString() {
  return "PriceEvent [symbol=" + symbol + ", price=" + price + "]";
 }
    
    
}


Here is the how you can get the source:
 

$ git clone https://github.com/leizou123/CepSample.git 

$ cd CepSample

$ mvn compile test 




Here is the test result:

......
Running com.lei.cep.StockTradeTest 19:39:25,419 INFO [StockTrade] start testStockTradeNotBuy() 19:39:25,419 INFO [StockTrade] symbal=GOOG 19:39:25,419 INFO [StockTrade] interval=1000 19:39:25,419 INFO [StockTrade] targetPrice=90.0 19:39:25,419 INFO [StockTrade] percetageDrop=10.0 19:39:25,420 INFO [StockTrade] arrayPrice=[100.0, 99.0, 100.5, 101.01, 100.0, 102.01, 101.0, 99.5, 100.0, 101.0, 102.0, 100.0, 99.99, 98.87, 93.99, 95.09, 90.99, 89.8, 93.9] 19:39:26,919 INFO [StockTrade] minPrice=100.0 avgPrice=100.0 19:39:27,921 INFO [StockTrade] minPrice=99.0 avgPrice=99.5 19:39:28,923 INFO [StockTrade] minPrice=99.0 avgPrice=99.83333333333333 19:39:29,924 INFO [StockTrade] minPrice=99.0 avgPrice=100.1275 19:39:30,925 INFO [StockTrade] minPrice=99.0 avgPrice=100.102 19:39:31,927 INFO [StockTrade] minPrice=99.0 avgPrice=100.42 19:39:32,928 INFO [StockTrade] minPrice=99.0 avgPrice=100.50285714285714 19:39:33,930 INFO [StockTrade] minPrice=99.0 avgPrice=100.3775 19:39:34,931 INFO [StockTrade] minPrice=99.0 avgPrice=100.33555555555556 19:39:35,932 INFO [StockTrade] minPrice=99.0 avgPrice=100.402 19:39:36,934 INFO [StockTrade] minPrice=99.0 avgPrice=100.54727272727273 19:39:37,935 INFO [StockTrade] minPrice=99.0 avgPrice=100.50166666666667 19:39:38,937 INFO [StockTrade] minPrice=99.0 avgPrice=100.46230769230769 19:39:39,938 INFO [StockTrade] minPrice=98.87 avgPrice=100.34857142857143 19:39:40,940 INFO [StockTrade] minPrice=93.99 avgPrice=99.92466666666668 19:39:41,941 INFO [StockTrade] minPrice=93.99 avgPrice=99.6225 19:39:42,943 INFO [StockTrade] minPrice=90.99 avgPrice=99.11470588235295 19:39:43,944 INFO [StockTrade] minPrice=89.8 avgPrice=98.59722222222223 19:39:44,946 INFO [StockTrade] minPrice=89.8 avgPrice=98.35000000000001 19:39:44,946 INFO [StockTrade] end testStockTradeNotBuy() buy GOOG? no 19:39:44,947 INFO [StockTrade] start testStockTradeBuy() 19:39:44,947 INFO [StockTrade] symbal=GOOG 19:39:44,947 INFO [StockTrade] interval=1000 19:39:44,947 INFO [StockTrade] targetPrice=90.0 19:39:44,947 INFO [StockTrade] percetageDrop=10.0 19:39:44,947 INFO [StockTrade] arrayPrice=[100.0, 99.0, 100.5, 106.01, 103.0, 107.01, 107.0, 108.5, 109.0, 105.0, 105.01, 102.0, 103.91, 105.0, 107.5, 102.0, 101.0, 104.01, 101.0, 102.91, 101.0, 99.5, 106.0, 101.0, 102.0, 100.0, 98.0, 89.0, 93.0] 19:39:46,265 INFO [StockTrade] minPrice=100.0 avgPrice=100.0 19:39:47,266 INFO [StockTrade] minPrice=99.0 avgPrice=99.5 19:39:48,268 INFO [StockTrade] minPrice=99.0 avgPrice=99.83333333333333 19:39:49,270 INFO [StockTrade] minPrice=99.0 avgPrice=101.3775 19:39:50,271 INFO [StockTrade] minPrice=99.0 avgPrice=101.702 19:39:51,273 INFO [StockTrade] minPrice=99.0 avgPrice=102.58666666666666 19:39:52,275 INFO [StockTrade] minPrice=99.0 avgPrice=103.21714285714286 19:39:53,277 INFO [StockTrade] minPrice=99.0 avgPrice=103.8775 19:39:54,279 INFO [StockTrade] minPrice=99.0 avgPrice=104.44666666666666 19:39:55,281 INFO [StockTrade] minPrice=99.0 avgPrice=104.502 19:39:56,282 INFO [StockTrade] minPrice=99.0 avgPrice=104.54818181818182 19:39:57,284 INFO [StockTrade] minPrice=99.0 avgPrice=104.33583333333333 19:39:58,286 INFO [StockTrade] minPrice=99.0 avgPrice=104.30307692307693 19:39:59,288 INFO [StockTrade] minPrice=99.0 avgPrice=104.35285714285715 19:40:00,290 INFO [StockTrade] minPrice=99.0 avgPrice=104.56266666666667 19:40:01,292 INFO [StockTrade] minPrice=99.0 avgPrice=104.4025 19:40:02,293 INFO [StockTrade] minPrice=99.0 avgPrice=104.20235294117647 19:40:03,294 INFO [StockTrade] minPrice=99.0 avgPrice=104.19166666666666 19:40:04,296 INFO [StockTrade] minPrice=99.0 avgPrice=104.02368421052631 19:40:05,297 INFO [StockTrade] minPrice=99.0 avgPrice=103.968 19:40:06,299 INFO [StockTrade] minPrice=99.0 avgPrice=103.82666666666667 19:40:07,300 INFO [StockTrade] minPrice=99.0 avgPrice=103.63000000000001 19:40:08,302 INFO [StockTrade] minPrice=99.0 avgPrice=103.73304347826088 19:40:09,304 INFO [StockTrade] minPrice=99.0 avgPrice=103.61916666666667 19:40:10,306 INFO [StockTrade] minPrice=99.0 avgPrice=103.5544 19:40:11,308 INFO [StockTrade] minPrice=99.0 avgPrice=103.4176923076923 19:40:12,310 INFO [StockTrade] minPrice=98.0 avgPrice=103.21703703703704 19:40:13,311 INFO [StockTrade] minPrice=89.0 avgPrice=102.70928571428571 19:40:13,311 INFO [StockTrade] buyNow=true 19:40:13,311 INFO [StockTrade] end testStockTradeBuy() buy GOOG? yes Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 47.893 sec
......


I hope you like this quick start on CEP with Esper post, and I certainly enjoy the software journey.



Monday, October 15, 2012

JVM Tuning for Low Latency High Throughput on Multi Core Linux Box

Recently, I have been tuning JVM parameters to achieve low latency & high throughput on HotSpot VM. For server side Java application, low latency is a very important requirement. In many cases, Java garbage collector pause is the serious threat. Under a very strict SLA, let's say response time under 100ms or less, it requires good amount of engineering effort to make the application behave under high stress environment .  

In my experience, before any JVM parameters tuning, I have spent lots of time to identify and remove thread contention points in the application. My development environment is 4 core Linux machine, JDK 1.6,  JBoss, and required SLA less than 100ms. There are 600 plus different JVM parameters. This article is about tuning GC for low latency for server side Java  applications, and the focus will be on those parameters that have bigger impact on the achieving low latency & high throughput.


    
$ /usr/java/jdk1.6.0_29/bin/java -XX:+PrintFlagsFinal -version 
     


The Java heap is divided into three main sections: Young Generation, Old Generation and the Permanent Generation.











Young Generation: The Eden Space of the Young Generation holds all the newly created objects. When this section fills, the Scavenge Garbage Collector clears out of memory all objects that are unreferenced. Objects that survive this scavenge moved to the "From" Survivor Space. The Survivor Space is a section of the Young Generation for these intermediate‐life objects. It has two equally‐sized subspaces "To" and “From” which are used by its algorithm for fast switching and cleanup. Once the Scavange GC is complete, the pointers on the two spaces are reversed: "To" becomes "From" and "From" becomes "To".


Old Generation: Once an object survives a given number of Scavenge GCs, it is promoted (or tenured) from the "To" Space to the Old Generation. Objects in this space are never garbage collected except in the two cases: Full Garbage Collection or Concurrent Mark‐and‐Sweep Garbage Collection. If the Old Generation is full and there is no way for the heap to expand, an Out‐of‐Memory error (OOME) is thrown and the JVM will crash. 


Permanent Generation: The Permanent Generation is where class files are kept. These are the result of compiled classes and jsp pages. If this space is full, it triggers a Full Garbage Collection. If the Full Garbage Collection cannot clean out old unreferenced classes and there is no room left to expand the Permanent Space, an Out‐of‐ Memory error (OOME) is thrown and the JVM will crash. 



HotSpot JVM may use one of 6 combinations of garbage collectors listed below.


Young collector
Old collector
JVM option
Serial (DefNew)
Serial Mark-Sweep-Compact
-XX:+UseSerialGC
Parallel scavenge (PSYoungGen)
Serial Mark-Sweep-Compact (PSOldGen)
-XX:+UseParallelGC
Parallel scavenge (PSYoungGen)
Parallel Mark-Sweep-Compact (ParOldGen)
-XX:+UseParallelOldGC
Serial (DefNew)
Concurrent Mark Sweep
-XX:+UseConcMarkSweepGC
-XX:-UseParNewGC
Parallel (ParNew)
Concurrent Mark Sweep
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
G1
-XX:+UseG1GC




A List of Stop the World Pauses:

  • Young space collections 
  • Full GCs – All collectors 
  • System GCs – Called via JMX or the application 
  • CMS Initial Mark Phase 
  • CMS Remark Phase 
  • CMS Concurrent Mode Failure



In my experiment, CMS gives the best results for low latency & high throughput. Here is summary of what I have learned. 

  1. JVM tuning is application specific. In depth knowledge of the application will help. And one needs to take a holistic when tuning. 
  2. Young Collections are fast and efficient. It is important to give objects the opportunity to die young. Smaller Young Space helps. 
  3. CMS is concurrent and requires CPU and it will compete with the application during collections.
  4. CMS fragments the Old Space and it makes Object allocations are more complicated. 
  5. Sizing the heap correctly is critical. Undersized heaps will make CMS work overtime, and worse it would cause CMS Concurrent Mode Failure. 
  6. Sizing the young ratio is important: 1) Size the survivor spaces appropriately 2) Configure the Tenuring Threshold appropriately  
  7. CMS to wait for a Young GC before starting. 

Here is the list of the recommended JVM settings for low latency & high throughput:


-server
-Xms2048m 
-Xmx2048m 
-XX:+UseConcMarkSweepGC 
-XX:+UseParNewGC
-XX:+AggressiveOpts
-XX:+CMSParallelRemarkEnabled
-XX:+CMSScavengeBeforeRemark
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=65
-XX:CMSWaitDuration=300000
-XX:GCTimeRatio=19
-XX:NewSize=128m
-XX:MaxNewSize=128m
-XX:PermSize=64m
-XX:MaxPermSize=64m
-XX:SurvivorRatio=88
-XX:TargetSurvivorRatio=88
-XX:MaxTenuringThreshold=15
-XX:MaxGCMinorPauseMillis=1
-XX:MaxGCPauseMillis=5
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./gc_heap_dump/
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-XX:+PrintTenuringDistribution
-Xloggc:./gc_log.log




Compare with 600 plus parameters to play with, this is a much shorter list. I hope you like this post, and enjoy your journey.

Sunday, September 23, 2012

Object oriented programing technique to replace if...then...else

If...then...else block could get out of control if used in many levels deep. The block becomes hard to maintain.


Here is one example:
    
static public String handleReturnString2 (String stringData) {
     
     if (stringData==null) {
      return "No handler found!";
     } else if (stringData.contains("ERROR") ) {
      return "Error case handled!";
     } else if (stringData.contains("FAILED") ) {
      return "Failed case handled!";
     } else if (stringData.contains("SKIPPED") ) {
      return "Skipped case handled!";
     } else if (stringData.contains("PASSED") ) {
      return "Passed case handled!";
     }
     
     return "No handler found!";
    }



Here is one way to replace it. There is trick to use Pattern match to replace string.contains():
   

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


// object oriented way to handle if then else  
public class SwitchOnSubstring {

 // define all ReturnCode enum
 public static enum ReturnCode {
  PASSED, SKIPPED, FAILED, ERROR; 
  
  // build pattern string, for string contains  
  static String getAllStringPattern() {
   StringBuilder sb = new StringBuilder(16);
   boolean firstone = true;
   for (ReturnCode p : ReturnCode.values()) {
    if (firstone) {
     firstone = false;
    } else {
     sb.append("|");
    }
    sb.append(p.name());
   }
   return sb.toString();
  }
 }
 
 // define handler interface 
 static private interface CaseHandler {
  public String handleReturn ();
 }

 static private class PassedCaseHandler implements CaseHandler {
  public String handleReturn () {
   return "Passed case handled!";
  }
 }


 // default handler
 final private static CaseHandler HandlerNone = new CaseHandler() {
  public String handleReturn () {
   return "No handler found!";
  }
 };
 
 final private static Pattern ReturnCodePattern = Pattern.compile( ReturnCode.getAllStringPattern());
 
 // use java regex to match string containing "PASSED" or "SKIPPED" "FAILED" "ERROR" to enum PASSED, SKIPPED, FAILED, ERROR;
 static private ReturnCode matchStringToReturnCode (String stringData)  {
  String strRequest = null;
  Matcher matcher = ReturnCodePattern.matcher(stringData);
  if (matcher.find()) {
   if (matcher.start()>=0 && matcher.end()>matcher.start()) {
    strRequest = stringData.substring(matcher.start(), matcher.end());
   }
  }
  return  strRequest==null? null : ReturnCode.valueOf(strRequest);
 }

 // setup the map structure to map enum PASSED, SKIPPED, FAILED, ERROR to their corresponding handlers 
 static private Map<ReturnCode, CaseHandler> mapHandlers = new HashMap<ReturnCode, CaseHandler> () {
  private static final long serialVersionUID = -80L;
  {
   // ErrorCaseHandler for string contains "ERROR"
            put(ReturnCode.ERROR , new CaseHandler() {
          public String handleReturn () {
           return "Error case handled!";
          }
         });

   // FailedCaseHandler for string contains "FAILED"
            put(ReturnCode.FAILED , new CaseHandler() {
          public String handleReturn () {
           return "Failed case handled!";
          }
         });

   // SkippedCaseHandler for string contains "SKIPPED"

            put(ReturnCode.SKIPPED , new CaseHandler() {
          public String handleReturn () {
           return "Skipped case handled!";
          }
         });
            
   // PassedCaseHandler for string contains "PASSED"
            put(ReturnCode.PASSED, new CaseHandler() {
          public String handleReturn () {
           return "Passed case handled!";
          }
         });
           
        }
    };


    // main method to handle the string 
    // if string contains PASSED, return PassedCaseHandler's result
    // if string contains ERROR, return ErrorCaseHandler's result
    // if string contains FAILED, return FailedCaseHandler's result
    // if string contains SKIPPED, return SkippedCaseHandler's result
    static public String handleReturnString (String stringData) {
     ReturnCode code = stringData==null ? null : matchStringToReturnCode (stringData);
     CaseHandler handle = ( code==null ) ? HandlerNone : mapHandlers.get(code);
     return handle.handleReturn() ;
    }
    

}



If you want the source, you can get it from github and perform the following step to compile & test:
   

git clone https://github.com/leizou123/SwitchOnSubstring.git

cd SwitchOnSubstring

mvn compile test 

Saturday, May 19, 2012

Project Euler is awesome, part 2

Project Euler is cool,  awesome. Solutions to the problems force me to come with efficient algorithms.

Here is Problem # 12:

The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be:

1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...

Let us list the factors of the first seven triangle numbers:

 1: 1
 3: 1,3
 6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28
We can see that 28 is the first triangle number to have over five divisors.

What is the value of the first triangle number to have over five hundred divisors?

Here is the Java solution to this problem:

public class Problem012 {
 
 static private long findPrimeFactors(long number, Long[] primelist) {
     int numOfFactors = 1;
     int exponent;
     long remain = number;
  
     for (int i = 0; i < primelist.length; i++) {
         if (primelist[i] * primelist[i] > number) {
             return numOfFactors * 2;
         }
  
         exponent = 1;
         while (remain % primelist[i] == 0) {
             exponent++;
             remain = remain / primelist[i];
         }
         numOfFactors *= exponent;
  
         //If there is no remainder, return the count
         if (remain == 1) {
             return numOfFactors;
         }
     }
     return numOfFactors;
 }
 
 
 public static void findNumber(int count) { 
  
  long startTime = System.nanoTime();
  
  long number = 1;
  long i = 2;
  long cnt = 0;
  long factorCount1 = 2;
  long factorCount2 = 2;
  
  Long[] primes = runEratosthenesSieve(1000);
  
  while (cnt < count) {
   if (i % 2 == 0) {
    factorCount2 = findPrimeFactors(i + 1, primes);
    cnt = factorCount2 * factorCount1;
   } else {
    factorCount1 = findPrimeFactors((i + 1) / 2, primes);
    cnt = factorCount2*factorCount1;
   }
   i++;
  }
  number = i * (i - 1) / 2;
  long elapsedTime = (System.nanoTime() - startTime) / 1000000; // ms
  
  System.out.println("The number with five hundred divisors is " + number + " time took to find it : " + elapsedTime + " ms");
 }
 
 
 public static void findNumberV2(int count) { 
  
  long startTime = System.nanoTime();
  
  long number = 1;
  long i = 2;
  long cnt = 0;
  
  Long[] primes = runEratosthenesSieve(1000);
  
  while (cnt < count) {
   number += i++;
   cnt = findPrimeFactors(number, primes);
  }

  long elapsedTime = (System.nanoTime() - startTime) / 1000000; // ms
  
  System.out.println("The number with five hundred divisors is " + number + " time took to find it : " + elapsedTime + " ms");
 }

 static public Long[] runEratosthenesSieve(int upperBound) {
  ArrayList<Long> listPrim = new ArrayList<Long> ();
  
  int upperBoundSquareRoot = (int) Math.sqrt(upperBound);
  boolean[] isComposite = new boolean[upperBound + 1];
  for (int m = 2; m <= upperBoundSquareRoot; m++) {
   if (!isComposite[m]) {
            //System.out.print(m + " ");
    listPrim.add(new Long(m));
    for (int k = m * m; k <= upperBound; k += m)
             isComposite[k] = true;
   }
  }
     
  for (int m = upperBoundSquareRoot; m <= upperBound; m++) {
   if (!isComposite[m]) {
    listPrim.add(new Long(m));
   }
  }
  return listPrim.toArray(new Long[listPrim.size()]) ;
 }


 

 public static void main(String[] args) { 

  findNumber(500);
  findNumberV2(500);
 }
}




When you run the code, you will find out the method findNumber() is more efficient than findNumberV2()

Sunday, May 13, 2012

Project Euler is cool, awesome

Project Euler is cool,  awesome. Solutions to the problems force me to come with efficient algorithms.

Here is one example:

The first known prime found to exceed one million digits was discovered in 1999, and is a Mersenne prime of the form 269725931; it contains exactly 2,098,960 digits. Subsequently other Mersenne primes, of the form 2p1, have been found which contain more digits.

However, in 2004 there was found a massive non-Mersenne prime which contains 2,357,207 digits: 28433*2^7830457+1.

Find the last ten digits of this prime number.

Here is the code:
  public static void main(String[] args) {
   long start = System.nanoTime() ;
   long value = 1;
   long LIMIT = 10000000000l;
   for (int i = 1 ; i <=7830457 ; i++) {
    value = value * 2;
    value =value%LIMIT;
   }
   value = value * 28433;
   value =value%LIMIT;
   value = value + 1 ;
   value =value%LIMIT;
   
   long end = System.nanoTime() ;
   System.out.println("result = " + value);
   System.out.println("computation time = " + ( (end - start) / 1000 ) + " ms");
  }

See, the solution is short and very efficient.

Here is another one:

Triangle, pentagonal, and hexagonal numbers are generated by the following formulae:

Triangle T(n)=n(n+1)/2 1, 3, 6, 10, 15, ...
Pentagonal P(n)=n(3n-1)/2 1, 5, 12, 22, 35, ...
Hexagonal H(n)=n(2n-1) 1, 6, 15, 28, 45, ...

It can be verified that T(285) = P(165) = H(143) = 40755.

Find the next triangle number that is also pentagonal and hexagonal.

/**

http://projecteuler.net/problem=45
 
Triangle, pentagonal, and hexagonal numbers are generated by the following formulae:

Triangle   T(n)=n(n+1)/2   1, 3, 6, 10, 15, ...
Pentagonal   P(n)=n(3n-1)/2 1, 5, 12, 22, 35, ...
Hexagonal   H(n)=n(2n-1)   1, 6, 15, 28, 45, ...

It can be verified that T(285) = P(165) = H(143) = 40755.

Find the next triangle number that is also pentagonal and hexagonal.

 * 
 * @author lei zou
 *
 */
public class Problem045 {
 
 private static final BigInteger TWO = new BigInteger("2");

 static public BigInteger[] setTriangleArray(int base, BigInteger[] triangles) {
  for (int i=1; i<=triangles.length; i++) {
   BigInteger n = new BigInteger( Integer.toString(i+base) );
   BigInteger n2 = new BigInteger( Integer.toString( (i+base)+1) ) ;
   triangles[i-1] = n.multiply(n2);
   triangles[i-1] = triangles[i-1].divide(TWO);
  }
  //System.out.println("Triangle is " + Triangle[Triangle.length-1] );
  return triangles;
 }

 static public BigInteger[] setPentagonalArray(int base, BigInteger[] pentagonals) {
  for (int i=1; i<=pentagonals.length; i++) {
   BigInteger n = new BigInteger( Integer.toString(i+base) );
   BigInteger n2 = new BigInteger( Integer.toString( ( 3* (i+base) -1 ) )  ) ;
   pentagonals[i-1] = n.multiply(n2);
   pentagonals[i-1] = pentagonals[i-1].divide(TWO);
  }
  //System.out.println("Pentagonal is " + pentagonals[pentagonals.length-1] );
  return pentagonals;
 }

 static public BigInteger[] setHexagonalArray(int base, BigInteger[] hexagonals) {
  for (int i=1; i<=hexagonals.length; i++) {
   BigInteger n = new BigInteger( Integer.toString(i+base) );
   BigInteger n2 = new BigInteger( Integer.toString( ( 2* (i+base) -1 ) )  ) ;
   hexagonals[i-1] = n.multiply(n2);
   
  }
  return hexagonals;
 }

 
 public static void main(String[] args) {
  int limit = 1000;
  BigInteger[] Triangle = new BigInteger[limit];
  BigInteger[] Pentagonal = new BigInteger[limit];
  BigInteger[] Hexagonal = new BigInteger[limit];
  
  setTriangleArray(0, Triangle);
  setPentagonalArray(0, Pentagonal);
  setHexagonalArray(0, Hexagonal);

  int cnt=0;
  int upperTriangle=limit, upperPentagonal=limit, upperHexagonal=limit;
  int lowTriangle=0, lowPentagonal=0, lowHexagonal=0;
  int i=0, j=0, k=0;
  BigInteger max = new BigInteger("0");
  while (cnt<3) {
   while (i<upperTriangle && j<upperPentagonal && k<upperHexagonal) {
    if (Triangle[i-lowTriangle].equals(Pentagonal[j-lowPentagonal]) 
      && Pentagonal[j-lowPentagonal].equals(Hexagonal[k-lowHexagonal]) ) {
    
     System.out.print( (cnt+1) + "th" + " number is " + Triangle[i-lowTriangle] );
     System.out.println(" Triangle index: " + (++i) + " Pentagonal index: " + (++j) + " Hexagonal index: " + (++k) );
     cnt++;
    
    }
    max = Triangle[i-lowTriangle].compareTo( Pentagonal[j-lowPentagonal] ) > 0  ? Triangle[i-lowTriangle] : Pentagonal[j-lowPentagonal]; 
    max = Hexagonal[k-lowHexagonal].compareTo(max) > 0  ? Hexagonal[k-lowHexagonal] : max;
    // 
    while (i<upperTriangle && Triangle[i-lowTriangle].compareTo(max) <0  ) i++;
    while (j<upperPentagonal && Pentagonal[j-lowPentagonal].compareTo(max)<0  ) j++;
    while (k<upperHexagonal && Hexagonal[k-lowHexagonal].compareTo(max)<0  ) k++;
   }
   //System.out.println("Triangle: " + (i) + " Pentagonal: " + (j) + " Hexagonal: " + (k) + " max " + max );
   if (i==upperTriangle) { 
    lowTriangle = i;
    setTriangleArray(i, Triangle);
    upperTriangle+=Triangle.length;
   }
   if (j==upperPentagonal) {
    lowPentagonal = j;
    setPentagonalArray(j, Pentagonal);
    upperPentagonal += Pentagonal.length;
   }
   if (k==upperHexagonal) {
    lowHexagonal = k;
    setHexagonalArray(k, Hexagonal);
    upperHexagonal += Hexagonal.length;
   }
  }
 }
}



Not sure that is the best solution. But it does solve the problem.