Wednesday, December 03, 2008

Blogger + syntax highlighting

I was frustrated to see my last post how blogger was displaying my source-codes for xml, properties file and bash script. It was just un-readable.

At last thought I'll clean stuff up a little bit, and also by the way thought of covering up the changes that I made to my blog template for syntax highlighting. Just wanted to keep my changes somewhere, last time forgot to keep a back-up of my working template before editing and had to start all over.

I'll be using the syntax-highlighter to highlight my source-codes in my blog.
Things that are needed for this are:
  1. Download syntax-highlighter and upload it somewhere in the internet where you have access to. I chose google-pages to host my files.
    The main files that need to be uploaded are:
    • dp.SyntaxHighlighter/Scripts/clipboard.swf
    • dp.SyntaxHighlighter/Scripts/shBrushJava.js
    • dp.SyntaxHighlighter/Scripts/shCore.js
    • dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css
    I uploaded only the shBrushJava.js brush at start as I thought I would normally be posting only java source-codes. Later I uploaded most of the brushes (including shBrushXml, which was why my earlier post was screwed up).... in fact I'll be creating some new brushes myself today ;-)
  2. Add links in you blog template to link to the files uploaded in the above step. For this add the following code snippet at the head of your template, just after the <head> tag:

    <link href='http://highlighter.abhi.sanoujam.googlepages.com/SyntaxHighlighter.css' rel='stylesheet' type='text/css'/>
    <script language='javascript' src='http://highlighter.abhi.sanoujam.googlepages.com/shCore.js'/>
    <script language='javascript' src='http://highlighter.abhi.sanoujam.googlepages.com/shBrushJava.js'/>
    <script language='javascript' src='http://highlighter.abhi.sanoujam.googlepages.com/shBrushJScript.js'/>
    <script language='javascript' src='http://highlighter.abhi.sanoujam.googlepages.com/shBrushXml.js'/>
    <script language='javascript' src='http://highlighter.abhi.sanoujam.googlepages.com/shBrushProperties.js'/>
    <script language='javascript' src='http://highlighter.abhi.sanoujam.googlepages.com/shBrushBash.js'/>
    <style type='text/css'>
    .console {
    background-color: black;
    color: #ffffff;
    font-family: courier;
    }
    </style>


    Now, replace http://highlighter.abhi.sanoujam.googlepages.com with your own url where you have uploaded your files.
    Again notice that shBrushProperties.js and shBrushBash.js are actually not present in the syntax-highlighter that you downloaded. We'll create them in this post :)
    Also we'll talk later about the .console CSS later.
  3. Activate (call the js function) the highlighter. You can do this by adding the following code snippet at the end of your template just before the </body>tag:
Now you are all done to start posting source codes. You can either use the <pre> or <textarea> to enclose your source-codes, and give the name="code" attribute and class="java" for your java source. eg.

<pre name="code" class="java">
public class HelloBlogger {
public static void main(String [] args) {
System.out.println("Hello Blogger!!");
}
}
</pre>

will look like:
public class HelloBlogger {
public static void main(String [] args) {
System.out.println("Hello Blogger!!");
}
}
I personally prefer using the <pre> tag for java and <textarea> tag for posting xml/html codes which normally contains characters like <, >

Now here is shBrushProperties.js

I just created this brush so that my source for properties file can look nice.
Same for my bash scripts, which is shBrushBash.js:


You can see that there are only a handful of keywords for the Bash script brush right now. I intend to expand as my blogs keep rolling. For starters, its kinda ok.

Now coming to the console CSS style, this is just a small hack for displaying command lines to get a feeling of console/terminals
You can use this like;

<pre class="console">
$ javac HelloBlogger.java
$ java HelloBlogger
Hello Blogger!!
</pre>

Which will look something like:


$ javac HelloBlogger.java
$ java HelloBlogger
Hello Blogger!!

You don't need to specify the name attibute :)

And ohh, forgot to mention and found out again when clicking "Publish Post", if you are posting XML/HTML, anything which has < or >, replace them with &lt; and &gt; am thinking of putting up a post that will specifically do that.

Enjoy...

Wednesday, November 26, 2008

Maven + hibernate + jpa + ehcache + spring + terracotta + composite keys

Some user were having some problem using composite keys with hibernate and ehcache as second level cache with Terracotta.

I tried beefing up a sample app with composite keys so that I can run the app with Terracotta... and it worked out smoothly in a quite small amount of time.

I reused much of what we did for Examinator, and came up with the app without much pain in very small amount of time. Really, Examinator (source) contains quite a lot of things that can be re-used to come up with with these kind of apps.

I'll try to put in the main parts of the sample app that I came up here:

Used maven-quickstart archetype to generate a quick project skeleton.

First the domain classes -- Its a Product class, which is uniquely identified by a combination of its productId and groupId. It also has a description property.

package sample.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@IdClass(ProductCompositeKey.class)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product {

@Id
private Long productId;

@Id
private Long groupId;

@Column(name = "DESCRIPTION")
private String description;

//...getters and setters...

}



We are using JPA annotations to define the entities. We annotate the Product class with the @Entity annotation, and as normally, annotate the productId and groupId properties with @Id.
We are using hibernate annotation @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) to enable second-level caching on this entity.
The @IdClass annotation refers to a class which will be the composite-key class.
The ProductCompositeKey is this class and is as follows:

package sample.model;

import java.io.Serializable;

import javax.persistence.Embeddable;

@Embeddable
public class ProductCompositeKey implements Serializable {

private Long productId;
private Long groupId;

//...getters and setters...

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((groupId == null) ? 0 : groupId.hashCode());
result = prime * result + ((productId == null) ? 0 : productId.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ProductCompositeKey other = (ProductCompositeKey) obj;
if (groupId == null) {
if (other.groupId != null)
return false;
} else if (!groupId.equals(other.groupId))
return false;
if (productId == null) {
if (other.productId != null)
return false;
} else if (!productId.equals(other.productId))
return false;
return true;
}

}




The ProductCompositeKey class is annotated with the @Embeddable annotation. Note that it is not annotated with @Entity
We need to override equals() and hashcode() and also implement the Serializable interface to make hibernate happy.


Next we define DAO classes for the Product. Here's the ProductDao interface:

package sample.dao;

import java.util.List;

import sample.model.Product;

public interface ProductDao {

public boolean delete(final Product product);

public boolean deleteById(final Long id);

public Product findById(final Long id);

public Product findByName(final String productName);

public List getAllProducts();

public long getNumberOfProducts();

public Product saveOrUpdate(final Product product);

}


And the ProductDaoImpl class:

package sample.dao;

import java.util.List;

import org.apache.log4j.Logger;

import sample.model.Product;

public class ProductDaoImpl implements ProductDao {
private static final Logger logger = Logger.getLogger(ProductDaoImpl.class);

final DaoHelper daoHelper;

public ProductDaoImpl(final DaoHelper daoHelper) {
if (null == daoHelper) throw new IllegalArgumentException("daoHelper can't be null");
this.daoHelper = daoHelper;
}

public boolean delete(final Product product) {
if (logger.isDebugEnabled()) logger.debug("delete: " + product.getId());

return daoHelper.deleteById(Product.class, product.getId());
}

public boolean deleteById(final Long id) {
if (logger.isDebugEnabled()) logger.debug("deleteById: " + id);

return daoHelper.deleteById(Product.class, id);
}

public Product findById(final Long id) {
if (logger.isDebugEnabled()) logger.debug("findById: " + id);

return daoHelper.findById(Product.class, id);
}

public Product findByName(final String productName) {
if (logger.isDebugEnabled()) logger.debug("findByName: " + productName);

final List<Product> list = daoHelper.findByAttribute(Product.class, "name", productName);
if (null == list || 0 == list.size()) return null;
assert list.size() == 1;

return list.get(0);
}

public List<Product> getAllProducts() {
if (logger.isDebugEnabled()) logger.debug("getAllProducts");

return daoHelper.getAllEntities(Product.class);
}

public long getNumberOfProducts() {
if (logger.isDebugEnabled()) logger.debug("getNumberOfProducts");

return daoHelper.countEntities(Product.class);
}

public Product saveOrUpdate(final Product product) {
if (logger.isDebugEnabled()) logger.debug("saveOrUpdate: " + product);

if (null == product) throw new IllegalArgumentException("product can't be null");
if (product.getId() == null) {
return daoHelper.save(Product.class, product);
} else {
return daoHelper.update(Product.class, product);
}
}

// public PageData<Product> getProductsByPage(final PageRequest pageRequest) {
// if (logger.isDebugEnabled()) logger.debug("getProductsByPage: pageRequest=" + pageRequest);
// return daoHelper.getEntitiesByPage(Product.class, pageRequest, "isDeleted", Boolean.FALSE);
// }
}



As you have seen, the ProductDaoImpl class delegates all its work to the DaoHelper class. The DaoHelper class is copied from the Examinator project (the api's related to paging are commented, as we don't need the paging api's here... hope to come up with a post for the paging soon).
The DaoHelper class is given here for reference:

package sample.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

/**
* The GenericDao is a helper class providing common data access functionality for use (via delegation) by Dao
* implementations.
*/
public class DaoHelper {

@PersistenceContext
private EntityManager entityManager;

/**
* Defines ASC and DESC sort orders for queries.
*/
public enum SortOrder {
ASC, DESC
}

public DaoHelper() {
// entityManager will be set via JSR250 injection
}

/**
* Custom entity manager that will not automatically be injected.
*/
public DaoHelper(final EntityManager entityManager) {
this.entityManager = entityManager;
}

public <T> T findById(final Class<T> entityClass, final Object id) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");
if (null == id) throw new IllegalArgumentException("id can't be null");

return entityManager.find(entityClass, id);
}

public boolean delete(final Object entity) {
if (null == entity) throw new IllegalArgumentException("entity can't be null");

entityManager.remove(entity);
return true;
}

public <T> boolean deleteById(final Class<T> entityClass, final Object id) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");
if (null == id) throw new IllegalArgumentException("id can't be null");

return delete(findById(entityClass, id));
}

public int deleteByAttribute(final Class entityClass, final String attributeName, final Object attributeValue) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");
if (null == attributeName) throw new IllegalArgumentException("attributeName can't be null");
if (null == attributeValue) throw new IllegalArgumentException("attributeValue can't be null");

return entityManager.createQuery(
"delete from " + entityClass.getSimpleName() + " e where e." + attributeName
+ " = ?1").setParameter(1, attributeValue).executeUpdate();
}

public <T> List<T> findByAttribute(final Class<T> entityClass, final String attributeName, final Object attributeValue) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");
if (null == attributeName) throw new IllegalArgumentException("attributeName can't be null");
if (null == attributeValue) throw new IllegalArgumentException("attributeValue can't be null");

return entityManager.createQuery(
"select e from " + entityClass.getSimpleName() + " e where e." + attributeName
+ " = ?1").setParameter(1, attributeValue).getResultList();
}

public <T> List<T> findByAttribute(final Class<T> entityClass, final String attributeName,
final Object attributeValue, final String orderByAttributeName,
final SortOrder sortOrder) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");
if (null == attributeName) throw new IllegalArgumentException("attributeName can't be null");
if (null == attributeValue) throw new IllegalArgumentException("attributeValue can't be null");
if (null == orderByAttributeName) throw new IllegalArgumentException("orderByAttributeName can't be null");

return entityManager.createQuery(
"select e from " + entityClass.getSimpleName() + " e where e." + attributeName
+ " = ?1 ORDER BY e." + orderByAttributeName + " " + sortOrder.name())
.setParameter(1, attributeValue).getResultList();
}

public <T> List<T> getAllEntities(final Class<T> entityClass) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");

return entityManager.createQuery("select e from " + entityClass.getSimpleName() + " e").getResultList();
}

public <T> List<T> getAllEntities(final Class<T> entityClass, final String orderByAttributeName,
final SortOrder sortOrder) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");
if (null == orderByAttributeName) throw new IllegalArgumentException("orderByAttributeName can't be null");

return entityManager.createQuery(
"select e from " + entityClass.getSimpleName() + " e order by e."
+ orderByAttributeName + " " + sortOrder.name()).getResultList();
}

public <T> T save(final Class<T> entityClass, final T entity) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");
if (null == entity) throw new IllegalArgumentException("entity can't be null");

entityManager.persist(entity);
return entity;
}

public <T> T update(final Class<T> entityClass, final T entity) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");
if (null == entity) throw new IllegalArgumentException("entity can't be null");

return entityManager.merge(entity);
}

public long countEntities(final Class entityClass) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");

return (Long) entityManager.createQuery("select count(entity) from " + entityClass.getSimpleName() + " entity")
.getSingleResult();
}

public long countEntitiesByAttribute(final Class entityClass, final String attributeName, final Object attributeValue) {
if (null == entityClass) throw new IllegalArgumentException("entityClass can't be null");
if (null == attributeName) throw new IllegalArgumentException("attributeName can't be null");
if (null == attributeValue) throw new IllegalArgumentException("attributeValue can't be null");

return (Long) entityManager.createQuery(
"select count(e) from " + entityClass.getSimpleName() + " e where e."
+ attributeName + " = ?1").setParameter(1, attributeValue)
.getSingleResult();
}

// public <T> PageData<T> getEntitiesByPage(final Class<T> entityClass, final
// PageRequest pageRequest) {
// return getEntitiesByPage(entityClass, pageRequest, null, null, "id",
// SortOrder.ASC);
// }
//
// public <T> PageData<T> getEntitiesByPage(final Class<T> entityClass, final
// PageRequest pageRequest, final String attributeName,
// final Object attributeValue) {
// return getEntitiesByPage(entityClass, pageRequest, attributeName,
// attributeValue, "id", SortOrder.ASC);
// }
//
// public <T> PageData<T> getEntitiesByPage(final Class<T> entityClass, final
// PageRequest pageRequest, final String attributeName,
// final Object attributeValue, final String orderByAttributeName,
// final SortOrder sortOrder) {
// if (null == entityClass) throw new
// IllegalArgumentException("entityClass can't be null");
// if (null == pageRequest) throw new
// IllegalArgumentException("pageRequest can't be null");
// if (null == orderByAttributeName) throw new
// IllegalArgumentException("orderByAttributeName can't be null");
//
// String queryStr = "";
// if (attributeName != null) {
// queryStr = "select e from " + entityClass.getSimpleName() + " e where e." +
// attributeName + " = ?1 ORDER BY e."
// + orderByAttributeName + " " + sortOrder.name();
// } else {
// queryStr = "select e from " + entityClass.getSimpleName() +
// " e ORDER BY e." + orderByAttributeName + " "
// + sortOrder.name();
// }
// long total;
// if (attributeName != null) total = countEntitiesByAttribute(entityClass,
// attributeName, attributeValue);
// else total = countEntities(entityClass);
//
// final PageRequest newPageRequest =
// PageRequest.adjustPageRequest(pageRequest, total);
//
// Query query;
// if (attributeName != null) query =
// entityManager.createQuery(queryStr).setParameter(1, attributeValue);
// else query = entityManager.createQuery(queryStr);
// final List<T> data = query.setFirstResult(newPageRequest.getStart() -
// 1).setMaxResults(newPageRequest.getPageSize()).getResultList();
// return new PageData<T>(newPageRequest, total, data);
// }
}


This is the class that does the main work for talking with your DB through JPA. You can note that the class does not have any compile time dependency on Spring or any other external library except for JPA. So if you are going to work with JPA, this class will make you happy :).
If you are thinking about writing your own dao's, you can consider reusing this class, its cool.

Next, coming to the service classes, I'll add a ProductService class which can add/remove/list products (from the DB of course). Here's the interface:

package sample.service;

import java.util.List;

import sample.model.Product;

public interface ProductService {

public void addProduct(Product product);

public void deleteProduct(Product product);

public List<product> getAllProducts();
}


The ProductServiceImpl implements the above interface:

package sample.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import sample.dao.ProductDao;
import sample.model.Product;

public class ProductServiceImpl implements ProductService {

@Autowired
private ProductDao productDao;

@Transactional(readOnly = false)
public void addProduct(Product product) {
try {
productDao.saveOrUpdate(product);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

@Transactional(readOnly = false)
public void deleteProduct(Product product) {
try {
productDao.delete(product);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

@Transactional(readOnly = true)
public List<product> getAllProducts() {
try {
return productDao.getAllProducts();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

}



You can see that I am using Spring's @Transactional annotation to demarcate my transactions. I will show you shortly how it is configured.
Am also using the @Autowired annotation, doing so I just need to declare a bean of type ProductDao and Spring will inject the bean into this ProductService bean. Saves me some xml in my application-context file from instead of explicitly setting the dao bean ;-)

And here's my application-context file:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">


<context:annotation-config />

<bean id="productService" class="sample.service.ProductServiceImpl" />

<bean id="daoHelper" class="sample.dao.DaoHelper" />
<bean id="productDao" class="sample.dao.ProductDaoImpl">
<constructor-arg ref="daoHelper" />
</bean>
<import resource="data-access.xml"/>
</beans>


This just declares my beans and imports the data-access.xml which configures my settings for talking with the DB.

Here's the data-access.xml:


The tag tells Spring to provide transactions to my annotated classes (ProductServiceImpl class).

I am configuring my properties from a properties file called "jdbc.properties" from the classpath.
I am using commons-dbcp connection pooling library and hence the org.apache.commons.dbcp.BasicDataSource datasource property for the LocalContainerEntityManagerFactoryBean

We are using ehcache as the hibernate second-level cache provider.
We are setting hibernate.cache.use_second_level_cache to true to enable hibernate second level caching and using net.sf.ehcache.hibernate.SingletonEhCacheProvider as the cache provider.

The name of the Persistence unit is samplewebapp as defined in the META-INF/persistence.xml (which basically contains nothing other than the PU name)


Here's my jdbc.properties:

## Properties file for JDBC settings

##-----------------
# MySQL DB Settings
##-----------------
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/samplewebapp?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=


##--------------------
# Hibernate properties
##--------------------
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.hbm2ddl.auto=validate

# Debugging
hibernate.show_sql=false
hibernate.format_sql=true
hibernate.use_sql_comments=true
hibernate.generate_statistics=false




We need to add dependency on all these libraries that we are using - spring, persistence-api (JPA), hibernate, ehcache, dbcp, mysql connector classes etc. This is declared in pom.xml



Now the last thing that we need is the main class that will demonstrate all these glued together.
Here's the main class that I am using to drive the App,


package sample;

import java.util.List;
import java.util.Random;
import java.util.UUID;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import sample.model.Product;
import sample.service.ProductService;

public class App {

private static Random random = new Random();

public static final ApplicationContext ctxt;
static {
ctxt = new ClassPathXmlApplicationContext("application-context.xml");
}

private ProductService productService = (ProductService) ctxt.getBean("productService");

public static void main(String[] args) {
new App().test();
}

private void test() {
listProducts();
addRandomProducts(random.nextInt(10) + 1);
listProducts();
}

private void addRandomProducts(int numProducts) {
System.out.println("======= Adding " + numProducts + " random products...");
for (int i = 0; i <> allProducts = productService.getAllProducts();
System.out.println("Number of products: " + allProducts.size());
for (Product prod : allProducts) {
System.out.println("Product: " + prod);
}
}

private Product getRandomProduct() {
Product product = new Product();
product.setProductId(Long.valueOf(UUID.randomUUID().getLeastSignificantBits()));
product.setGroupId(Long.valueOf(UUID.randomUUID().getLeastSignificantBits()));
product.setDescription("A description : " + System.currentTimeMillis());
return product;
}
}



You can try out running this class using


$ mvn exec:java -Dexec.mainClass=sample.App





Now the interesting part: clustering the app with Terracotta.
For this we create a tc-config.xml and just tell Terracotta that we are using hibernate and ehcache and it will automatically cluster the app.


That's all that's needed to cluster with Terracotta.
Here's a script that will launch the app with Terracotta, just replace the TC_INSTALL_DIR with the location where you have downloaded and installed Terracotta:


#!/bin/bash

TC_INSTALL_DIR=/Users/asingh/terracottaKit/terracotta-2.7.1

mvn compile

CP_FILE=cp.txt
mvn dependency:build-classpath -Dmdep.outputFile=$CP_FILE
echo ":./target/classes" >> $CP_FILE

$TC_INSTALL_DIR/bin/dso-java.sh -cp `cat $CP_FILE` sample.App

rm $CP_FILE




I am sure I must have missed some parts of explaining the glue-points here and there.
You can download the attached tarball from here and play around with this simple app.

Looks like this was quite a long (hopefully not boring) post. Hope you are still with me and reading this :)... and do leave a comment if you are not reading this ;-)

Friday, November 21, 2008

Just a quick note.... Maven + dependencies + classpath

Just wanted to capture this for personal purpose...

How to find out/export the classpath from a maven project so that it can be used from scripts etc?

-- Using the build-classpath goal of the Maven dependency plugin.

The parameter name
maven.dep.cpFile
given at the usage of this goal is deprecated and does not work. Its recommended to use the ouputFile parameter instead.

So some examples...
mvn dependency:build-classpath

prints the classpath in the console... and
mvn dependency:build-classpath -Dmdep.outputFile=cp.txt

puts the classpath in the file called cp.txt

Tuesday, November 18, 2008

The webapp -- starting the engine

I plan to create a simple webapp and illustrate using maven, hibernate, jpa, spring mvc, spring security, spring webflow etc.
And at the last point add Terracotta and see how easy it is to cluster a webapp and scale out.

Basically most of the stuffs that I am going to post here are things that I learnt when building http://reference.terracotta.org/examinator.
Yes the Examinator is live. And now you can play around with it.
You can find out more about it here.

I do not plan to rewrite the whole Examinator here, but show the glue points of making those cool technologies together.

Lets get going...

First we need a project. I am going to use the webapp-archetype to generate a skeleton for our project.
Using the quickstart guide, I changed the groupId and the artifactId to generate a project for myself like this:

mvn org.apache.maven.plugins:maven-archetype-plugin:1.0-alpha-7:create \
-DarchetypeGroupId=org.terracotta.maven.archetypes \
-DarchetypeArtifactId=webapp-archetype \
-DarchetypeVersion=1.2.1 \
-DgroupId=org.sanoujam \
-DartifactId=sample-tc-webapp \
-Dversion=1.0.0 \
-DremoteRepositories=http://www.terracotta.org/download/reflector/maven2

This created a project for me called sample-tc-webapp and generated a working project skeleton for me.

If you want, you can verify its running by:

cd sample-tc-webapp
mvn tc:run

This will bring up two tomcat nodes on your localhost. Accessing http://localhost:8080/sample-tc-webapp should show you a working web-app which is clustered with Terracotta. The other tomcat is running at http://localhost:8081/sample-tc-webapp


You can find the source code of this project at http://code.google.com/p/sample-tc-webapp/
The source can be browsed at http://sample-tc-webapp.googlecode.com/svn/tags/day1/

Stopping here for today...
Lets see if we can add Spring next time....

Wednesday, October 29, 2008

ASM Eclipse plugin + Ganymede + Not on its project's build path

ASM plugin for eclipse found here works neat upto Eclipse 3.3
If you have updated to Eclipse Ganymede, you will see that it no longer works.
It complains something like:

Error (Bytecode Outline) src/Foo.java [in BarProject] is not on its project's build path.


Where Foo.java is the java class for which you want to view the bytecode and MyProject is the eclipse project in which it is present.

After a little bit of googling found out that a new version of the plugin is available for Eclipse Ganymede. 
You can get the new version at http://andrei.gmxhome.de/bytecode/index.html.
Use http://andrei.gmxhome.de/eclipse/ as the update URL with the update manager and install the new version.

Works cool now :)

Friday, October 03, 2008

Definitely Terracotta


Imagine being able to call wait() on a Java object in one JVM, and imagine that later a thread in another JVM on another computer calls notify() on that same object and that notify() call penetrates the impermeable process boundary between the two JVMs to wake your thread from its slumber. Further imagine that all of the changes to that object made in the other JVM while you were waiting are now visible to your thread. Imagine now that the code you wrote to make this happen looked no different than code you would have written were this program to run only on a single JVM—no concessions to special frameworks, no callouts to special APIs, no stubs, no skeletons, no copies, no put-backs, and no magic beans.

What would it be like if you could automatically share Java heap between virtual machines so that threads running in different JVMs could interact with each other exactly as if they were running in the same JVM? Imagine what you could do with that kind of power -— the power to express a computing problem in the simplest possible terms that stay true to the problem at hand yet be able to run that program on multiple computers at once while the program thinks it’s running on one big computer. What would such a thing be? And wouldn’t you really, really want it if you could have it?

Terracotta was born out of just such imaginings.
Terracotta is Java infrastructure software that allows you to scale your application to as many computers as needed, without expensive custom code or databases. Throughout this book, it is referred to as JVM-level clustering or network-attached memory. The question you now face is how to understand and leverage this technology best.


Doesn't that sound cool? Its an excerpt from the introduction in The Definitive Guide to Terracotta about Terracotta from the same people who made all this imaginations a reality.

I am working for Terracotta these days, and since joining had been doing real cool stuff.

We are developing a reference app these days on the sessions-clustering use case. Its not only about developing a web-app and clustering a web application with Terracotta, but get out the best practices and design patterns and best ways to solve problems for similar use cases, to say the least. We had focussed down on the technology stack and almost done developing the app.

In the process, I learnt a lot personally and got exposed to cool new technologies and how to leverage Terracotta. I plan to share my experience and hope that it helps others out there.

More posts coming up on Spring, Spring MVC, Spring Web-flow, Spring security, JPA, hibernate, maven... and other cool stuffs!!

Stay tuned... ;-)

Wednesday, September 10, 2008

ASM - Searching methods for specific Opcodes

Doing bytecode manipulation is cool, and ASM makes the job quite easy. Its a cool framework that lets us do bytecode manipulation, analyse, modify or even dynamically generate bytecodes on the fly.
The User guide on their web-site is quite a nice doc and explains things neatly.

ASM is quite simple and easy to use. Following is a simple Class adapter that can be used to search for methods which contains specific Opcodes. I actually intend to write a use case about resolving types of AALOAD, but I guess I'll cover this simple Adapter first which will be used later on.

So here goes...

package adapters;

import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.MethodAdapter;

import java.util.HashMap;
import java.util.Map;

//a class adapter that will search for specific opcode in all its methods and remember those
//methods
public class OpcodeSearchClassAdapter extends ClassAdapter implements Opcodes {

// the opcode to be searched as defined in com.objectweb.asm.Opcodes
private int searchOpcode;
// mapping of method names -> method signatures
private Map methodsWithSearchOpcode;
private boolean done;

public OpcodeSearchClassAdapter(final ClassVisitor classVisitor, int searchOpCode) {
super(classVisitor);
methodsWithSearchOpcode = new HashMap();
searchOpcode = searchOpCode;
}

public MethodVisitor visitMethod(final int access, final String name, final String desc,
final String signature, final String[] exceptions) {
final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return new OpcodeSearchMethodAdapter(access, name, desc, mv, searchOpcode,
methodsWithSearchOpcode);
}

public void visitEnd() {
this.done = true;
super.visitEnd();
}

public int getSearchOpcode() {
return searchOpcode;
}

public Map getMethodsWithSearchOpcode() {
if (!done) {
return null;
}
return methodsWithSearchOpcode;
}

private static class OpcodeSearchMethodAdapter extends MethodAdapter implements Opcodes {

private Map methodsWithOpcode;
private int searchOpcode;
private String name;
private String desc;

private OpcodeSearchMethodAdapter(final int access, final String name, final String desc,
final MethodVisitor mv, final int searchOpcode, final Map methodsWithOpcode) {
super(mv);
this.name = name;
this.desc = desc;
this.searchOpcode = searchOpcode;
this.methodsWithOpcode = methodsWithOpcode;
}

public void visitInsn(final int opcode) {
if (opcode == searchOpcode) {
this.methodsWithOpcode.put(name + " " + desc, name);
}
super.visitInsn(opcode);
}
}
}



Lets write a simple example that demonstrates this class adapter. We'll write a simple Test class that will contain some methods with the instruction that we want to search for. We will then load the bytes of this Test class, go through the bytecode of this class and look inside all the methods searching for the required Opcode instruction.
For example, lets say we are going to search for the AALOAD instruction, lets write our Test class as follows:

package test;

public class TestClass {

public void fooSquare(Integer[] bar, int i) {
System.out.println("Method with AALOAD");
bar[i] = new Integer(bar[i].intValue() * bar[i].intValue());
}

public int getFoo(int[] bar, int i) {
System.out.println("Method with IALOAD, not with AALOAD");
return bar[i];
}

public void foobar() {
System.out.println("Method with no AALOAD");
}

public void foo(Foo[] fooArr, int i) {
System.out.println("Another method with AALOAD");
System.out.println("Invokes method on the array element");
fooArr[i].bar();
}

public static class Foo {
public void bar() {
System.out.println("Foo.bar()");
}
}

}

The TestClass contains some methods with AALOAD. It also contains other methods which doesn't contain it. The methods public void fooSquare(Integer[] bar, int i) and public void foo(Foo[] fooArr, int i) are the methods that contain the AALOAD instruction as seen in the code.

Lets now write the Main class that will load the bytes of TestClass and run the OpcodeSearchClassAdapter


package test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.EmptyVisitor;

import adapters.OpcodeSearchClassAdapter;

public class MainTest {

public static void main(String[] args) throws IOException {
new MainTest().test();
}

private void test() throws IOException {
String testClassName = TestClass.class.getName().replace('.', '/');
String fileName = testClassName.substring(testClassName.lastIndexOf('/') + 1) + ".class";
InputStream is = this.getClass().getResourceAsStream(fileName);
ClassReader reader = new ClassReader(is);
ClassVisitor emptyVisitor = new EmptyVisitor();
OpcodeSearchClassAdapter searchClassAdapter = new OpcodeSearchClassAdapter(emptyVisitor,
Opcodes.AALOAD);
reader.accept(searchClassAdapter, ClassReader.SKIP_FRAMES);

System.out.println("Methods with AALOAD: ");
Map methods = searchClassAdapter.getMethodsWithSearchOpcode();
for (Iterator it = methods.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
String val = (String) methods.get(key);
System.out.println("method name: " + val + " desc:" + key);
}
}
}


The MainTest class loads the TestClass and runs our class adapter through the TestClass bytecode.
It first creates an InputStream of the TestClass bytecodes by loading the bytes using getResourceAsStream method in ClassLoader. A ClassReader is instantiated using the inputstream which "accepts" an instance of OpcodeSearchClassAdapter. We create an instance of OpcodeSearchClassAdapter which takes an EmptyVisitor and the Opcode we are searching for -- which is AALOAD in this example.
We are using an EmptyVisitor as we are only reading through the bytecode and not changing anything. The methods containing the Opcode we are searching is remembered by the OpcodeSearchClassAdapter.

After the reader accepts the Adapter, we use getMethodsWithSearchOpcode() in the OpcodeSearchClassAdapter to get the methods which contain AALOAD and simply print it out.

If you compile and run the above, you will get an output like:

Methods with AALOAD:
method name: foo desc:foo ([Ltest/TestClass$Foo;I)V
method name: fooSquare desc:fooSquare ([Ljava/lang/Integer;I)V


In the next post I will show how to analyse bytecode and get the type reference of AALOAD which can be used to instrument this access.

Thursday, June 26, 2008

"bash: clear: command not found" --- clear not working on cygwin

Ok so you installed Cygwin and try out your first commands.... u cluttered ur screen and its time for doing a "clear".

Ooops... its giving you a "bash: clear: command not found". You just can't clear the dumb screen. I know thats frustrating....

So which dumb package did you miss when installing cygwin --- its the ncurses package. Open cygwin setup again and choose ncurses library from the "Lib" package. Once you've installed that, clear will start working.

If you are lazy enough and don't want to download stuff, just use "Ctrl+L" to clear your screen.


Keep clearing...

Setting up ssh server on cygwin

Here's a nice post I came up with when I was looking on how to set up ssh server up and running on my win box with Cygwin.

Setting up ssh server on cygwin

After setting up ssh server and starting it, use putty to connect to ur own localhost. It feels much better using putty than cmd.exe to use the shell. cmd sux !!

Enjoy