Mostrando postagens com marcador java. Mostrar todas as postagens
Mostrando postagens com marcador java. Mostrar todas as postagens

quinta-feira, 13 de outubro de 2011

Générer des fichiers checksum pour Maven

Dans un répertoire Maven v2 les fichiers JAR ont l'intégrité contrôlée par des fichiers contenant des sommes MD5 et SHA1. En général ces fichiers auxiliaires sont appelés comme le nom du fichier JAR accompagnés des extensions ".md5" ou ".sha1". Par exemple, pour la librairie "jcommon-0.9.6.jar" il doit exister "jcommon-0.9.6.jar.md5" et "jcommon-0.9.6.jar.sha1".

Ces ".md5" et ".sha1" ne sont que des fichiers texte avec une chaîne string correspondant à la somme de contrôle calculée à partir de l'archive. Cette somme représente une signature unique pour chaque fichier et peut garantir qu'il ne soit pas craqué ou corrompu.

Pour générer ces fichiers au moment de la livraison d'une librairie Java, ils existent des plugins Maven pour les créer automatiquement. Néanmoins, on peut avoir le cas où des librairies JAR n'aient pas ses fichiers de contrôle (par exemple, si on a créé cette partie du répo manuellement). Cette façon, lors d'une résolution de dépendance au Maven, on va avoir des notifications telles qu'au-dessous :

[WARNING] *** CHECKSUM FAILED - Checksum failed on download

Pour illustrer ce soucis, voici un exemple d'un arbre partiel au Maven :

|-- jfree-former
|   |-- jcommon
|   |   `-- 0.9.6
|   |       |-- jcommon-0.9.6.jar
|   |       `-- jcommon-0.9.6.pom
|   `-- jfreechart
|       `-- 0.9.21
|           |-- jfreechart-0.9.21.jar
|           `-- jfreechart-0.9.21.pom

Il faut d'abord créer un script Shell appelée checksum.sh avec le contenu ci-dessous :

#!/bin/bash

if [ $# -ne 2 ]
then
 echo "Usage : checksum [md5|sha1] <file-name>"
 echo "Sample: checksum sha1 /tmp/dir/myfile.jar"
 exit 1
fi

format=$1
file="$2"

if [ ! -f $file ]
then
 echo "File not found: $file"
 exit 2
fi

if [ "$format" == "md5" -o "$format" == "sha1" ]
then
 ${format}sum "$file" | cut -d' ' -f1 | tr -d "\n" > "$file.$format"
else
 echo "Please choose a format: md5 or sha1"
 exit 2
fi

echo "Created checksum file $file.$format"

Ensuite, on doit créer un autre script nommé generate-checksums.sh contenant ces lignes :

#!/bin/bash

EXTENSIONS="jar pom"
ALGORITHMS="sha1 md5"

PROGDIR=`dirname $0`
export PATH="$PATH:$PROGDIR"

for ext in $EXTENSIONS
do
 for alg in $ALGORITHMS
 do
  find -type f -name "*.$ext" -exec checksum.sh $alg {} \;
 done
done

N'oubliez pas de donner des permissions d'exécution à ses fichiers .sh, en roulant le commande chmod +x *.sh.

Maintenant, il faut seulement aller au répertoire désiré et ensuite exécuter le script generate-checksums.sh. Voyez :

$ cd /var/maven/repo/

$ /home/user/scripts/generate-checksums.sh
Created checksum file ./jfree-former/jcommon/0.9.6/jcommon-0.9.6.jar.sha1
Created checksum file ./jfree-former/jfreechart/0.9.21/jfreechart-0.9.21.jar.sha1
Created checksum file ./jfree-former/jcommon/0.9.6/jcommon-0.9.6.jar.md5
Created checksum file ./jfree-former/jfreechart/0.9.21/jfreechart-0.9.21.jar.md5
Created checksum file ./jfree-former/jcommon/0.9.6/jcommon-0.9.6.pom.sha1
Created checksum file ./jfree-former/jfreechart/0.9.21/jfreechart-0.9.21.pom.sha1
Created checksum file ./jfree-former/jcommon/0.9.6/jcommon-0.9.6.pom.md5
Created checksum file ./jfree-former/jfreechart/0.9.21/jfreechart-0.9.21.pom.md5

Et voici le résultat pour le cas d'exemple :

|-- jfree-former
|   |-- jcommon
|   |   `-- 0.9.6
|   |       |-- jcommon-0.9.6.jar
|   |       |-- jcommon-0.9.6.jar.md5
|   |       |-- jcommon-0.9.6.jar.sha1
|   |       |-- jcommon-0.9.6.pom
|   |       |-- jcommon-0.9.6.pom.md5
|   |       `-- jcommon-0.9.6.pom.sha1
|   `-- jfreechart
|       `-- 0.9.21
|           |-- jfreechart-0.9.21.jar
|           |-- jfreechart-0.9.21.jar.md5
|           |-- jfreechart-0.9.21.jar.sha1
|           |-- jfreechart-0.9.21.pom
|           |-- jfreechart-0.9.21.pom.md5
|           `-- jfreechart-0.9.21.pom.sha1

Voilà ! Désormais votre répertoire Maven contient des informations de vérification pour les fichiers et la résolution de dépendances ne lancera plus le message "CHECKSUM FAILED".

Si vous voulez, le code source complet peut être obtenu dans cette adresse : https://gitorious.org/shell-scripts/checksum

quinta-feira, 2 de setembro de 2010

Simplifying Cassandra interactions with Helena



Introduction


In the previous article "Talking to Cassandra using Java and DAO pattern" [1] we saw an introduction to NoSQL-based databases and especially one called Apache Cassandra [2]. After a quick explanation of its data model, we analyzed what it takes to implement a Java code which goal was to access and yet modify key-value pairs stored on a Cassandra keyspace. At that time we used a low-level interface API named Thrift.

You might have noticed that a large amount of code was needed to build an entire and yet simple persistence class, namely a structure based on Data Access Object design pattern. If we compare it to currently most used persistence technologies and frameworks in Java (i.e. JPA and Hibernate), we would probably get crazy!

Is this the price we pay for high-availability and scalability when changing from a traditional relational database model to a newly key-value data store? Poor developers... Well, that's not the end, fortunately! There are already available several high level clients for Cassandra in multiple programming languages (see [3]).

One of these clients, HelenaORM [4] (created by Marcus Thiesen), is the study subject this time. As this library is addressed to Java language, our aim will be to implement the persistence of a simple object using Helena's support.


Creating the entity


As in the other article, we'll choose the "group" entity to exemplify our codes. This time, in addition to the plain old Java class elements, in the Group class we need to annotate it with @HelenaBean and @KeyProperty.

The annotation @HelenaBean is used to specify keyspace and column family for the class, whereas @KeyProperty must indicate which field in the class should be considered the row key in Cassandra - currently annotation only works at getter or setter methods, not in the variable itself. Both annotations are analogous to JPA's @Entity and @Id annotations.

Take a look at the Group entity class properly prepared to be used by HelenaORM:


@HelenaBean(keyspace="ContactList", columnFamily="Groups")
public class Group {

private Integer id;
private String name;

public Group() {
}

public Group(Integer id, String name) {
this.id = id;
this.name = name;
}

@KeyProperty
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Group [id=" + id + ", name=" + name + "]";
}

}



Creating the tests


The most interesting new is about to come: with Helena there's no need of traditional building a DAO interface and class!

Helena brings a factory HelenaORMDAOFactory designed to create ready-to-use DAO classes type-safely pointed to a given entity. The classes it produces, of HelenaDAO type, provides these methods: insert(), delete(), and get(). They are out-of-box implementations respectively for inserting (or editing), removing, and retrieving entire object instances from Cassandra.

So, here is the corresponding unitary test class in JUnit reserved for invoking inserts, deletions, and retrievals of Java object instances into Cassandra with the support of Helena:


public class GroupTest {

static HelenaORMDAOFactory factory;
private HelenaDAO<Group> dao;

private static final Integer GROUP_ID = 123;
private static final String GROUP_NAME = "Test Group";

@BeforeClass
public static void setUpBeforeClass() throws Exception {
factory = HelenaORMDAOFactory.withConfig(
"localhost", 9160, SerializeUnknownClasses.YES);
}

@AfterClass
public static void tearDownAfterClass() throws Exception {
factory = null;
}

@Before
public void setUp() throws Exception {
dao = factory.makeDaoForClass(Group.class);
}

@After
public void tearDown() throws Exception {
dao = null;
}

@Test
public void testSave() {

System.out.println("GroupDAOTest.testSave()");

Group group = new Group();
group.setId(GROUP_ID);
group.setName(GROUP_NAME);

System.out.println("Saving group: " + group);
dao.insert(group);
Assert.assertTrue(true);

Group retrieved = dao.get(GROUP_ID.toString());
System.out.println("Retrieved group: " + retrieved);
Assert.assertNotNull(retrieved);
Assert.assertEquals(GROUP_ID, retrieved.getId());
Assert.assertEquals(GROUP_NAME, retrieved.getName());
}

@Test
public void testRetrieve() {

System.out.println("GroupDAOTest.testRetrieve()");

System.out.println("Saving groups");
for (int i = 1; i <= 10; i++) {
Group group = new Group();
group.setId(GROUP_ID * 100 + i);
group.setName(GROUP_NAME + " " + i);
dao.insert(group);
}

List<Group> list = dao.getRange("", "", 10);
System.out.println("Retrieving groups");
Assert.assertNotNull(list);
Assert.assertFalse(list.isEmpty());
Assert.assertTrue(list.size() >= 10);

System.out.println("Retrieved list:");
for (Group group : list) {
System.out.println("- " + group);
}
}

@Test
public void testRemove() {

System.out.println("GroupDAOTest.testRemove()");

Group group = new Group();
group.setId(GROUP_ID);
group.setName(GROUP_NAME);

System.out.println("Saving group: " + group);
dao.insert(group);

System.out.println("Removing group: " + group);
dao.delete(group);
Assert.assertTrue(true);

Group retrieved = dao.get(GROUP_ID.toString());
System.out.println("Retrieved group: " + retrieved);
Assert.assertNull(retrieved);
}

}



Checking the results


After properly executing the tests, you should check the keyspace contents inside Cassandra. In order to do that, you can use Cassandra's client by issuing the instructions described below:


cassandra> count ContactList.Groups['12305']
1 column

cassandra> get ContactList.Groups['12305']
=> (column=name, value=Test Group 5, timestamp=1283287882927)
Returned 1 result.

cassandra> get ContactList.Groups['12305']['name']
=> (column=name, value=Test Group 5, timestamp=1283287882927)



Conclusions


Traditional relational databases took more than 20 years of evolution to reach the state they are now. Aside, object-oriented programming languages and techniques conquered companies and developers forcing the creation of persistence frameworks in order to link both worlds. Soon as Internet usage grew, RDBMSs were not able to efficiently scale, thus a new paradigm was conceived (or reborn): the key-value distributed database model!

Cassandra, one of those distributed databases, is not trivial to talk to, from a developer perspective. In opposition to use a low-level interface API (i.e. Thrift), a lot of high-level clients were created by individual developers (thanks to open source initiatives!), and one of them was HelenaORM.

Thus, in the present article we saw how to leverage and simplify our Java code which handles persistence in Cassandra with the aid of HelenaORM libraries. A lot of work was reduced, isn't it? :D


References

[1] Talking to Cassandra using Java and DAO pattern
[2] Apache Cassandra
[3] Cassandra high level clients
[4] HelenaORM

quarta-feira, 1 de setembro de 2010

Talking to Cassandra using Java and DAO pattern


Introduction


It is definitely unavoidable and not just another hype this wave of NoSQL - an alternative to rigid persistence models provided by RDBMS (Relational Database Management Systems). In a connected world that needs more and more data in a continuous growth and speed, it is proved by huge technology companies like Google that changes need to be done in order to accomodate this. One of these turns was indeed NoSQL databases, such as Bigtable, Hypertable, and Cassandra. The latter will be the subject of this article.

As described in its home page [1]:

"The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model. Cassandra was open sourced by Facebook in 2008, and is now developed by Apache committers and contributors from many companies."

So far, Cassandra is in use at Digg, Facebook, Twitter, Reddit, Rackspace, Cloudkick, Cisco, SimpleGeo, Ooyala, OpenX, and more companies that have large, active data sets. The largest production cluster has over 100 TB of data in over 150 machines!

Therefore, let's take a walk-through on how can this new storing and programming model be achieved, particularly using a client on Java language.

Database keyspace setup


We are considering Cassandra's stable version on the time of this document: 0.6.5. Yet, we'll run the server in single mode for simplification purposes. Remember that JDK 1.6 (Java Development Kit) is needed to run Cassandra server.

The first step on this study will be to include the keyspace "ContactList". In order to to this, add the following lines into CASSANDRA_HOME/conf/storage-conf.xml, inside <Keyspaces> tag:

<Keyspace Name="ContactList">
<ColumnFamily CompareWith="UTF8Type" Name="Groups"/>
<ColumnFamily CompareWith="UTF8Type" Name="Contacts"/>
<ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy>
<ReplicationFactor>1</ReplicationFactor>
<EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch>
</Keyspace>

Start (or restart) your Cassandra server (by issuing "CASSANDRA_HOME/bin/cassandra -f") after editing that file. The proper data files and index structures should be created.

Please consider reading Cassandra's Getting Started guide [2] for further information on this process.

Once the server is online, check the new keyspace availability by using the client application invoking the following command:

CASSANDRA_HOME/bin/cassandra-cli --host localhost --port 9160

The resulting expected screen is shown below:

Connected to: "Test Cluster" on localhost/9160
Welcome to cassandra CLI.

Type 'help' or '?' for help. Type 'quit' or 'exit' to quit.
cassandra>

You could then try to show available keyspaces on the server, as illustrated:

cassandra> show keyspaces
ContactList
Keyspace1
system

Note that "ContactList" should be included. If not, you should review the previous steps.

Now invoke the instruction below in order to explain detailed information on the new keyspace:

cassandra> describe keyspace ContactList
ContactList.Groups
Column Family Type: Standard
Columns Sorted By: org.apache.cassandra.db.marshal.UTF8Type@33b121

Column Family Type: Standard
Column Sorted By: org.apache.cassandra.db.marshal.UTF8Type
flush period: null minutes
------
ContactList.Contacts
Column Family Type: Standard
Columns Sorted By: org.apache.cassandra.db.marshal.UTF8Type@1b22920

Column Family Type: Standard
Column Sorted By: org.apache.cassandra.db.marshal.UTF8Type
flush period: null minutes
------

OK, apparently our key-value storage is working well. It's time for coding! :D

Starting the use case


We'll start by implementing a simple "group" entity, represented in Java by a POJO-based class named Group with the content below:

public class Group {

private Integer id;
private String name;

public Group() {
}

public Group(Integer id, String name) {
this.id = id;
this.name = name;
}

public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Group [id=" + id + ", name=" + name + "]";
}

}

Concerns must be separated in different layers. In order to persist the "group" entity, it is highly recommended to have a DAO pattern-based interface. That interface, named IGroupDAO, is destinated to exclusively handle operations on that entity. Here is its corresponding Java code:

public interface IGroupDAO {

void startup();

void shutdown();

List<Group> findAll();

Group findById(Integer id);

void save(Group group);

void remove(Group group);

}

Simply put, save() and remove() methods are enough to perform modifications on the data, whereas findAll() and findById() are reserved for retrieving purposes. You might then ask: "what da hell are startup() and shutdown() doing there"? Hold on, they are a necessary "gambiarra" to show stuff working and might not be inserted on a real application. :)

Developers, developers, developers: do not forget to write tests before actual codes..! Test Driven Development (TDD) is definitely important and worthful besides being so funny to use at coding time!

As we are using Java, let's create unitary a test against that DAO interface using the JUnit [3] testing framework. The resulting class, named GroupDAOTest has the code shown below:

public class GroupDAOTest {

private static IGroupDAO dao;

private static final Integer GROUP_ID = 123;
private static final String GROUP_NAME = "Test Group";

@BeforeClass
public static void setUpBeforeClass() throws Exception {
dao = new GroupDAO();
dao.startup();
}

@AfterClass
public static void tearDownAfterClass() throws Exception {
dao.shutdown();
dao = null;
}

@Test
public void testSave() {

System.out.println("GroupDAOTest.testSave()");

Group group = new Group();
group.setId(GROUP_ID);
group.setName(GROUP_NAME);

System.out.println("Saving group: " + group);
dao.save(group);
Assert.assertTrue(true);

Group retrieved = dao.findById(GROUP_ID);
System.out.println("Retrieved group: " + retrieved);
Assert.assertNotNull(retrieved);
Assert.assertEquals(GROUP_ID, retrieved.getId());
Assert.assertEquals(GROUP_NAME, retrieved.getName());
}

@Test
public void testRetrieve() {

System.out.println("GroupDAOTest.testRetrieve()");

System.out.println("Saving groups");
for (int i = 1; i <= 10; i++) {
Group group = new Group();
group.setId(GROUP_ID * 100 + i);
group.setName(GROUP_NAME + " " + i);
dao.save(group);
}

List<Group> list = dao.findAll();
System.out.println("Retrieving groups");
Assert.assertNotNull(list);
Assert.assertFalse(list.isEmpty());
Assert.assertTrue(list.size() >= 10);

System.out.println("Retrieved list:");
for (Group group : list) {
System.out.println("- " + group);
}
}

@Test
public void testRemove() {

System.out.println("GroupDAOTest.testRemove()");

Group group = new Group();
group.setId(GROUP_ID);
group.setName(GROUP_NAME);

System.out.println("Saving group: " + group);
dao.save(group);

System.out.println("Removing group: " + group);
dao.remove(group);
Assert.assertTrue(true);

Group retrieved = dao.findById(GROUP_ID);
System.out.println("Retrieved group: " + retrieved);
Assert.assertNull(retrieved);
}

}


The goal of this GroupDAOTest test class is to invoke the methods belonging to IGroupDAO interface. For each DAO functionality a method on GroupDAOTest must be created and annotated with @Test. The expected results might be tested using Assert class.

Actual persistence


Base codes were built along with testing classes. However, persistence against Cassandra is not being made yet. Thus, how could we get access to a Cassandra database from a client perspective?

Cassandra uses a more general API called Thrift [4] for its external client-facing interactions. Cassandra's main API/RPC/Thrift port is 9160. Thrift supports a wide variety of languages so you can code your application to use Thrift directly (take a look at [5]), or use a high-level client where available.

Of course we're gonna use the low-level Thrift interface. :)

The first thing to do is to add several Java libraries related to Cassandra and Thrift to the project's classpath. They are released along with Cassandra binaries, especifically at CASSANDRA_HOME/lib directory.

And now we finally implement the GroupDAO class, which must provide actual instructions assigned by IGroupDAO interface.

In this code, we'll need to manually handle the connection to Cassandra through its binary protocol on TCP port 9160. This is exactly where startup() and shutdown() get in.

If you're still not familiar with terms used at key-value databases, such as keystore, column families, etc, don't worry. I suggest you further reading of some articles about Cassandra's Data Model [6, 7]. For the moment, just know that Thrift interface for Java provides support classes like Cassandra.Client, ColumnParent, ColumnOrSuperColumn, KeyRange, SliceRange, and SlicePredicate.

Take a look at the code below for GroupDAO implementation class:

public class GroupDAO implements IGroupDAO {

private static final String KEYSPACE = "ContactList";
private static final String COLUMN_FAMILY = "Groups";
private static final String ENCODING = "utf-8";

private static TTransport tr = null;

/**
* Close the connection to the Cassandra Database.
*/
private static void closeConnection() {
try {
tr.flush();
tr.close();
} catch (TTransportException exception) {
exception.printStackTrace();
}
}

/**
* Open up a new connection to the Cassandra Database.
*
* @return the Cassandra Client
*/
private static Cassandra.Client setupConnection() {
try {
tr = new TSocket("localhost", 9160);
TProtocol proto = new TBinaryProtocol(tr);
Cassandra.Client client = new Cassandra.Client(proto);
tr.open();
return client;
} catch (TTransportException exception) {
exception.printStackTrace();
}
return null;
}

private Cassandra.Client client;

@Override
public void startup() {
this.client = setupConnection();
}

@Override
public void shutdown() {
closeConnection();
}

@Override
public List<Group> findAll() {
List<Group> list = new ArrayList<Group>();
try {
KeyRange keyRange = new KeyRange();
keyRange.setStart_key("");
keyRange.setEnd_key("");

SliceRange sliceRange = new SliceRange();
sliceRange.setStart(new byte[] {});
sliceRange.setFinish(new byte[] {});

SlicePredicate slicePredicate = new SlicePredicate();
slicePredicate.setSlice_range(sliceRange);

ColumnParent columnParent = new ColumnParent(COLUMN_FAMILY);
List<KeySlice> keySlices = client.get_range_slices(KEYSPACE,
columnParent, slicePredicate, keyRange,
ConsistencyLevel.ONE);

if (keySlices == null || keySlices.isEmpty())
return list;

for (KeySlice keySlice : keySlices) {

Group group = new Group();
group.setId(Integer.parseInt(keySlice.getKey()));

for (ColumnOrSuperColumn c : keySlice.getColumns()) {
if (c.getColumn() != null) {
String name = new String(c.getColumn().getName(),
ENCODING);
String value = new String(c.getColumn().getValue(),
ENCODING);
// long timestamp = c.getColumn().getTimestamp();
if (name.equals("name")) {
group.setName(value);
}
}
}

list.add(group);
}
return list;

} catch (Exception exception) {
exception.printStackTrace();
}
return null;
}

@Override
public Group findById(Integer id) {
try {
SlicePredicate slicePredicate = new SlicePredicate();
SliceRange sliceRange = new SliceRange();
sliceRange.setStart(new byte[] {});
sliceRange.setFinish(new byte[] {});
slicePredicate.setSlice_range(sliceRange);

ColumnParent columnParent = new ColumnParent(COLUMN_FAMILY);
List<ColumnOrSuperColumn> result = client.get_slice(KEYSPACE, id
.toString(), columnParent, slicePredicate,
ConsistencyLevel.ONE);

if (result == null || result.isEmpty())
return null;

Group group = new Group();
group.setId(id);

for (ColumnOrSuperColumn c : result) {
if (c.getColumn() != null) {
String name = new String(c.getColumn().getName(), ENCODING);
String value = new String(c.getColumn().getValue(), ENCODING);
// long timestamp = c.getColumn().getTimestamp();
if (name.equals("name")) {
group.setName(value);
}
}
}

return group;
} catch (Exception exception) {
exception.printStackTrace();
}
return null;
}

@Override
public void save(Group group) {
try {
long timestamp = System.currentTimeMillis();
Map<String, List<ColumnOrSuperColumn>> job = new HashMap<String, List<ColumnOrSuperColumn>>();

List<ColumnOrSuperColumn> columns = new ArrayList<ColumnOrSuperColumn>();
Column column = new Column("name".getBytes(ENCODING), group
.getName().getBytes(ENCODING), timestamp);
ColumnOrSuperColumn columnOrSuperColumn = new ColumnOrSuperColumn();
columnOrSuperColumn.setColumn(column);
columns.add(columnOrSuperColumn);

job.put(COLUMN_FAMILY, columns);

client.batch_insert(KEYSPACE, group.getId().toString(), job,
ConsistencyLevel.ALL);
} catch (Exception exception) {
exception.printStackTrace();
}
}

@Override
public void remove(Group group) {
try {
ColumnPath columnPath = new ColumnPath(COLUMN_FAMILY);
client.remove(KEYSPACE, group.getId().toString(), columnPath,
System.currentTimeMillis(), ConsistencyLevel.ALL);
} catch (Exception exception) {
exception.printStackTrace();
}
}

}

Now make sure you project is fully "buildable" and make the testing class run! Unless your Cassandra server is offline, the resulting on JUnit is "evergreen".

Here are the tasks supposed to be performed by the given unitary test:
  1. instantiate a group identified by the key 123 and persist it, searching it and comparing to the expected object;
  2. persist a group, remove it on the database and then try to search for its key expecting not to find it;
  3. insert 10 instances of groups with keys numbered from 12301 to 12310 and test their retrieval.

If you're so skeptical as me, you'll love to search for those persisted data by hands. In that case, prompt to the Cassandra's client and try sending some commands to retrieve groups and their columns recently stored. Here are some suggestions:

cassandra> count ContactList.Groups['12301']
1 columns

cassandra> get ContactList.Groups['12301']
=> (column=name, value=Test Group 1, timestamp=1283287745613)
Returned 1 results.

cassandra> get ContactList.Groups['12301']['name']
=> (column=name, value=Test Group 1, timestamp=1283287745613)


Conclusion


We have reached the end of this study, at last! In this article some concepts about a NoSQL-based database called Cassandra were introduced. Besides, we implemented in Java a very simple example of persistence into a Cassandra keyspace using a low-level API named Thrift. In the end we just searched for the stored values in the database through Cassandra's client.

References


[1] Apache Cassandra
[2] Getting Started with Cassandra
[3] JUnit - Unit Testing Framework
[4] Apache Thrift
[5] Cassandra Wiki - Thrift Examples
[6] Cassandra Wiki - Data Model
[7] A Quick Introduction to the Cassandra Data Model

terça-feira, 1 de dezembro de 2009

Creating Demoiselle JPA-compliant DAOs



This article aims at providing a walk through on building Data Access Object (DAO) [1] components using Java Persistence API (JPA 1.0) [2], since the latter is now fully supported in the 1.1 release of Demoiselle Framework [3].

The reference source codes used in the following sections were retrieved from Auction5 [4], a sample web application specifically designed to exemplify JPA usage along with Demoiselle.


i. Introducing support elements


The main characters on the new Demoiselle persistence handling mechanism are IDAO [5] and JPAGenericDAO [6].

IDAO is a simple interface containing data manipulation and retrieval methods. It is, above all, a markup interface intended to be implement by whoever class playing a DAO role. Therefore, no matter whether the final class will handle objects from a DBMS or an XML file, using pure JDBC, Hibernate or JPA, it must necessarily implement IDAO.

On the other hand, JPAGenericDAO is an abstract class intented to leverage productivity by providing the derived class with a bunch of persistence-related ready-to-use methods such as: findById(), findByJPQL(), insert(), update(), and remove().


ii. Defining entities


Entity classes are usually the first elements to be created on a Java application powered by JPA persistence.

In the class diagram below the whole application entities considered in the examples are depicted.




iii. Designing interfaces


In order to build DAO artifacts on Demoiselle, one needs to previously create their respective interfaces. Besides, these interfaces must extend IDAO by providing the targeted entity class in the generics brackets (e.g. <Auction>).

The signatures of additional needed methods must be indicated on each interface, remembering that all methods provided by IDAO are already implicitly included.

For instance, for the Auction entity, there must be created IAuctionDAO interface by extending IDAO and declaring additional listing methods. Its final code is shown below:


public interface IAuctionDAO extends IDAO<Auction> {
public Auction findById(Long id);
public List<Auction> listOpenAuctionsByCategory(Category category);
public List<Auction> listNewestAuctions(int quantity);
public List<Auction> listMostOfferedAuctions(int quantity);
public List<Auction> listEndingSoonAuctions(int seconds, int quantity);
public List<Auction> listCheapestPriceAuctions(int quantity);
public List<Auction> listOpenAuctionsByItem(Item item);
public List<Auction> listOpenEndedAuctions(Date timestamp);
}

Likewise, interfaces for Bid, Category, Item, and Order entities suggest the structures below:


public interface IBidDAO extends IDAO<Bid> {
public List<Bid> listLastBidsForAuction(Auction auction, int quantity);
}

public interface ICategoryDAO extends IDAO<Category> {
public Category findById(Short id);
public List<Category> listAvailableCategories();
public List<Category> listAllCategories();
}

public interface IItemDAO extends IDAO<Item> {
public Item findById(Integer id);
public List<Item> listByCategory(Category category);
}

public interface IOrderDAO extends IDAO<Order> {
public Order findById(Long id);
public List<Order> listOrdersByLogin(String login);
}

On an application powered by Demoiselle all those interfaces are usually placed on a subpackage named "persistence.dao" inside the main package tree (e.g. "br.gov.demoiselle.sample.auction5.persistence.dao").


iv. Designing implementation classes


After creating the DAO interfaces, the next step is to build their respective implementation classes. For each interface, there must be created a concrete class declared with a reference to that interface.

In addition, in order to boost development time and yet help the developer with several coding facilities, these new classes should extend JPAGenericDAO superclass described previously.

Thus, DAO implementation classes should contain the declarations below:


public class AuctionDAO extends JPAGenericDAO<Auction> implements IAuctionDAO { ... }

public class BidDAO extends JPAGenericDAO<Bid> implements IBidDAO { ... }

public class CategoryDAO extends JPAGenericDAO<Category> implements ICategoryDAO { ... }

public class ItemDAO extends JPAGenericDAO<Item> implements IItemDAO { ... }

public class OrderDAO extends JPAGenericDAO<Order> implements IOrderDAO { ... }


As a result of "implements", every method declared in an interface must be implemented and fulfilled with actual code by the developer in the class. However, there are exceptions: methods belonging to IDAO are already implemented in JPAGenericDAO, and therefore do not need to be reimplemented.

Demoiselle Framework suggests hosting these classes onto a subpackage named "implementation" right inside the subpackage containing the interfaces (e.g. "br.gov.demoiselle.sample.auction5.persistence.dao.implementation").


v. Implementing classes methods


In the next sections there will be explored several ways of implementing the methods declared in the interfaces, by using existing code, by issuing JPQL queries and even through SQL native queries.


1. Superclass methods


The first alternative on building persistence methods in the DAO is the simplest one: to directly invoke existing methods of JPAGenericDAO superclass.

For example, a listAllCategories() method designed to bring all categories could be coded in CategoryDAO using the following contents:


public List<Category> listAllCategories() {
return findAll();
}

If the method name is to be kept, one should use the "super" reference variable to prevent infinite loops, as illustrated below in findById() method for CategoryDAO:


public Category findById(Short id) {
return super.findById(id);
}



2. JPQL fixed string


As an evolution to EJB QL, JPA 1.0 introduces a query language called JPQL (Java Persistence Query Language). This language's synthax is similar to Hibernate's HQL and offers the same purpose: to send instructions to the DBMS by using a language closer to object-oriented world.

It is made available to the JPAGenericDAO class a method called findByJPQL(), which internally creates a JPQL query according to a given string, executes the query, builds a list of entities and finally returns the latter in a type-safe fashion.

When coding a DAO method on Demoiselle, we could use fixed JPQL queries whenever there is no need to give parameters to them. The CategoryDAO implementation class exhibits the usage of fixed string JPQL queries through findByJPQL():


public List<category> listAvailableCategories() {
return findByJPQL("select c from Category c where c.active = true order by c.name");
}



3. JPQL with positional parameters


As most of our real-world queries involve passing parameters to them, JPQL also offers the possibility of doing this. Actually, there are two ways of passing parameters to a query: using positional (numbered) or named parameters.

Positional parameters will be explored in the current section. They are expressed in the JPQL string with the "?" character followed by an integer number starting from 1.

On JPAGenericDAO the method findByJPQL() is overloaded so that it can handle positional parameters as well. The AuctionDAO implementation class exhibits the usage of JPQL queries with positional parameters on listAllAuctionsByItem() method:


public List<Auction> listAllAuctionsByItem(Item item) {

String jpql = "select a from Auction a where a.item = ?1";

List<Auction> result = findByJPQL(jpql, item);

return result;
}

Demoiselle Framework introduces pagination mechanisms through Page and PagedResult classes. In order to use them, one needs to invoke an other overloaded version of findByJPQL(), which accepts a Page object as the second argument. The BidDAO implementation class comes with listLastBidsForAuction() method, which exemplifies pagination of results using the classes described and yet accepts a positional parameter:


public List<Bid> listLastBidsForAuction(Auction auction, int qty) {

Page page = new Page(qty);

String jpql = "select b from Bid b where b.auction.id = ?1 order by b.timestamp desc";
PagedResult<Bid> pr = findByJPQL(jpql, page, auction.getId());

return (List<Bid>) pr.getResults();
}



4. JPQL with named parameters


There are some situations where numbered parameters in a query might be a confusing or not preferred alternative. In order to handle it, JPA specification foresees named parameters on JPQL queries.

According to JPA, a named parameter in JPQL is indicated with the ":" character followed by an unique string which specifies it.

Inside Demoiselle, using named parameters consists of a map instantiation and the execution of another overloaded findByJPQL(), this time receiving in addition a Map object as argument. The AuctionDAO implementation class exhibits the usage of JPQL queries with bound named parameters:


public List<Auction> listOpenAuctionsByCategory(Category category) {

String jpql =
"select a from Auction a " +
"where a.status = :status " +
" and a.deadline > :deadline " +
" and a.item.category = :category " +
"order by a.item.description";

Map<String, Object> params = new HashMap<String, Object<();
params.put("status", Status.OPEN);
params.put("deadline", new Date());
params.put("category", category);

List<Auction> result = findByJPQL(jpql, params);

return result;
}



5. Named JPQL query


As an alternative introduced by Hibernate, JPA brings a feature called named queries, which aims at writing complex JPQL statements inside entity classes in the form of annotations. These so-called annotated queries are then referenced back when issuing JPQL queries.

JPAGenericDAO comes handy once again by providing another method: findByNamedQuery(). This method receives a string containing the name of a JPQL query whose statement is already on an entity. For instance, let the Item entity with a @NamedQuery annotation right above its class declaration:


@NamedQuery(
name = "itemsByCategory",
query = "select i from Item i where i.category.id = ?1 order by i.description"
)
public class Item implements IPojoExtension { ... }

Then, the respective ItemDAO implementation class should make use of JPAGenericDAO's findByNamedQuery() as shown in the code belonging to listByCategory() method:


public List<Item> listByCategory(Category category) {
return findByNamedQuery("itemsByCategory", category.getId());
}

Note that in the previous example a positional parameter was placed. But what if we need named parameters alongside named queries? The answer is: an overloaded version of findByNamedQuery() might take place! For example, the Auction entity presents a @NamedQuery according to the code below:


@NamedQuery(
name = "openAuctionsByItem",
query = "select a from Auction a where a.status = :status and a.deadline > :deadline and a.item = :item"
)
public class Auction implements IPojoExtension { ... }

Observe that named parameters on a named JPQL query are indicated in the annotation as if they were to be used by findByJPQL() method. And then, in the same manner, we need to create a map object in order to invoke the method, which this time is findByNamedQuery(). Take a look at the contents of AuctionDAO, particularly the listOpenAuctionsByItem() method:


public List>Auction< listOpenAuctionsByItem(Item item) {

Map<String, Object> params = new HashMap<String, Object<();
params.put("status", Status.OPEN);
params.put("deadline", new Date());
params.put("item", item);

List<Auction> result = findByNamedQuery("openAuctionsByItem", params);

return result;
}



6. Native SQL query


So, are we constrained to JPQL statements and object-oriented queries when adopting JPA persistence mechanism on a Java application? Not at all!

There are some cases where performance matters most of everything and the only possibility of achieving it is by executing native SQL instructions, usually made through JDBC in the Java world. This particular approach can boost up SQL execution time, especially for those who prefer to create their own database stored procedures or functions.

Despite this development behavior can lead to a given DBMS dependency and thus compromising application portability, the architect still might choose to create native SQL queries. JPA specification was developed with a rich set of components (i.e. interfaces and annotations) concerning the invocation of native SQL instructions.

In Demoiselle, once again, JPAGenericDAO can help the developer with a pre-built method: findByNativeQuery(). Take the example of the listMostOfferedAuctions() method inside AuctionDAO, which contains SQL directed to PostgreSQL DBMS:


public List<Auction> listMostOfferedAuctions(int quantity) {

String sql =
"select d.* from ( " +
" select b.auction_id, count(b.id) " +
" from bids b " +
" inner join auctions a on (a.id = b.auction_id) " +
" where a.status = ?1 and a.mode <> ?2 " +
" group by b.auction_id " +
" order by count(b.id) desc " +
" limit ?3) c " +
"join auctions d on (d.id = c.auction_id) " +
"where d.bestbid_id is not null " +
"order by c.count desc";

List<Auction> result = findByNativeQuery(sql,
Status.OPEN.ordinal(), Mode.SELLING.ordinal(), quantity);

return result;
}

Another method of AuctionDAO, listEndingSoonAuctions(), makes use of native SQL query and positional parameters:


public List<Auction> listEndingSoonAuctions(int seconds, int quantity) {

String sql =
"select * " +
"from auctions " +
"where status = ?1 " +
" and deadline - current_timestamp between '0 s' and cast(?2 as interval) " +
"order by deadline " +
"limit ?3";

List<Auction> result = findByNativeQuery(sql,
Status.OPEN.ordinal(), seconds + " s", quantity);

return result;
}

But now another issue is raised: what if we need native SQL query along with named parameters? The answer might be simple: just merge both techniques into one, right? Well, partially.

According to JPA 1.0 specification, a named parameter is an identifier that is prefixed by the ":" symbol, and its use applies just to JPQL, therefore not being defined for native queries. Thus, only positional parameter binding may be portably used for native queries.

Despite that lack of portability, JPA implementations usually offer named parameters on native queries. Unfortunately there is no common sense yet, so that implementations consider "?" or ":" symbols when defining named parameters in a native SQL string. This is better illustrated in the code below, taken from AuctionDAO's listCheapestPriceAuctions() method, which is prepared to run on Hibernate EntityManager JPA implementation:


public List<Auction> listCheapestPriceAuctions(int quantity) {

// WARNING: prepared for Hibernate - use "?" instead of ":" for TopLink or EclipseLink
String sql =
"select d.* from (" +
" select a.id, coalesce(b.amount, a.startingprice, a.sellingprice) as price " +
" from auctions a left join bids b on (b.id = a.bestbid_id) " +
" where a.status = :status " +
" order by 2 asc " +
" limit :quantity) c " +
"join auctions d on c.id = d.id " +
"order by c.price";

Map<String, Object> params = new HashMap<String, Object<();
params.put("status", Status.OPEN.ordinal());
params.put("quantity", quantity);

List<Auction> result = findByNativeQuery(sql, params);

return result;
}

Thus, portability is compromised in the previous example, as we need to take special precautions when switching to another JPA implementation, say EclipseLink or Apache OpenJPA.


7. Named native SQL query


As with JPQL, it is possible to store the entire native query statement onto an entity class and then reference it as needed. In order to do that, JPA defines another annotation, @NamedNativeQuery, which has the same purpose of @NamedQuery. After setting the named native query, one could invoke findByNamedQuery() method to execute it and possibly bring back the results. Note that this very same method fits for both JPQL and native SQL queries.

So, consider the named native query "newestAuctions", which is defined on Auction entity file. There, in the @NamedNativeQuery annotation three parameters must be passed: the query unique identifier, the statement itself, and a class indicating the result type. The referenced code is shown below:


@NamedNativeQuery(
name = "newestAuctions",
query = "select * from auctions where status = ?1 order by creation desc limit ?2",
resultClass = Auction.class
)
public class Auction implements IPojoExtension { ... }

At DAO implementation class AuctionDAO this named native query is called inside listNewestAuctions() method, as illustrated in the following code:


public List<Auction> listNewestAuctions(int quantity) {
List<Auction> result = findByNamedQuery("newestAuctions",
Status.OPEN.ordinal(), quantity);
}



8. LIQUidFORM tool


JPA 1.0 specification does not provide a Criteria API [7], a feature introduced by Hibernate designed to help developers on building HQL queries. With that we avoid string concatenation and rather we declaratively define the statement, thus reducing the risk of errors in its synthax.

Fortunately JPA 2.0 was conceived with a Criteria API, thus soon JPA implementations will run after that. Meanwhile, a lot of alternatives were developed to fill in such requirement in JPA. Amongst them, one particular tool was employed in the sample code: LIQUidFORM [8].

LIQUidFORM is actually a set of classes and interfaces designed to compose JPQL statements in a type-safe and refactoring proof style. It might be strange at first sight, as it contains methods called select(), from(), and where(), but it is indeed a very interesting idea.

The key to LIQUidFORM is to make use of static imports in the class header, namely the lines below:


import static com.google.code.liquidform.LiquidForm.*;
import static com.google.code.liquidform.Parameters.*;

OrderDAO implementation class exhibits the usage of LIQUidFORM tool for creating a JPQL query inside listOrdersByLogin() method, as shown below:


public List<Order> listOrdersByLogin(String login) {

Order o = alias(Order.class, "o");

String jpql =
select(o).
from(Order.class).as(o).
where(eq(o.getLogin(), param(1, String.class))
).toString();

return findByJPQL(jpql, login);
}



vi. Conclusion


In this article we have explored some benefits of building the persistence layer of a Java application by adopting Java Persistence API (JPA 1.0) inside an architecture composed by DAO-based interfaces and implementation classes.

In addition, we have seen the usage of Demoiselle Framework structural IDAO interface and JPAGenericDAO support class when building methods designed to manipulate data through JPA. Those methods also served to exemplify the existing JPA approaches by introducing JPQL and native SQL queries, positional and named parameters, JPQL and SQL named queries, and LIQUidFORM tool.


vii. References


[1] Core J2EE Patterns - Data Access Object
[2] JPA 1.0 (JSR 220)
[3] Demoiselle Framework
[4] Auction5 Application Sample
[5] IDAO interface
[6] JPAGenericDAO class
[7] Criteria API
[8] LIQUidFORM

segunda-feira, 16 de novembro de 2009

Unicode to Java string literal converter



I was having some headaches writing properties files valid across several platforms due to encoding issues. However, by googling around today I found this very interesting JavaScript-based conversion tool:

http://www.snible.org/java2/uni2java.html

Thanks to Ed Snible!

terça-feira, 10 de novembro de 2009

Resumos das palestras do TDC 2009



Seguem resumos das palestras dos keynotes no The Developer's Conference 2009 - Floripa [1] ministradas no dia 9 de novembro.

Tendências em Java EE: Como serão os próximos 5 anos - Rod Johnson

O criador do Spring [2], um dos mais populares frameworks em Java, apresentou nesta palestra suas previsões para o futuro da tecnologia da informação como um todo. Nessa linha, afirmou que "estamos a ponto de presenciar a maior ruptura em TI desde o declínio do mainframe", impulsionada por forças como Cloud Computing, Lei de Moore e o fato de os custos estarem se voltando para as pessoas em contraposição ao hardware.

Disse também que sistemas gerenciadores de bancos de dados relacionais (SGBDRs) não irão embora, e ao invés disso haverá grande ascensão da chamada "persistência poliglota", onde haverão diferentes meios de se armazenar os dados. (Aqui entra a JPA!)

Sobre a computação em nuvem, afirmou que ela está mais próxima que SOA e que possui as seguintes vantagens características: ser abstrata, integrada e opinativa, dinamicamente escalável e consumida como um serviço, além de inevitável devido a questões econômico-ambientais. E com isso, disse que nós desenvolvedores nos tornaremos mais poderosos! :) Precisaremos fornecer APIs direcionadas para a nuvem...

Com relação aos frameworks, iniciou com a frase "Frameworks can't be too disruptive!", alegando que o código aberto possibilitou a criação de excelentes soluções de software, mas por outro lado fez do Java uma plataforma confusa para os iniciantes.

Finalizou dizendo que nessa era de desordem a abstração será mais importante do que nunca, citando como possíveis gerenciadoras para esse caos no software as seguintes soluções: Grails, Spring, Roo e Rails, além de especificações como a JPA.


Java EE 6 - Uma grande evolução - Mike Keith

O Mike é o líder de importantes especificações do Java, tal como EJB 3 e JPA, e iniciou esta palestra apresentando os objetivos e destaques para a esperada versão 6 do Java EE [3], especialmente JSF 2.0, Servlet 3.0, EJB 3.1 e JPA 2.0.

Em seguida resumiu cada uma das novas funcionalidades: Java Profiles, JNDI Namespaces, Data Source Definition, novas anotações para Web (ex: @WebServlet, @ServletFilter, @WebServletContextListener), Web Fragments, Async Processing (mesmo para session beans!). Além disso, para o EJB 3.1 teremos: interfaces opcionais, singleton, concorrência, timer estilo cron, EJB lite, contêiner embutido e novas regras de empacotamento.

A JPA 2.0 terá cache compartilhado, melhorias na JPQL e a inclusão de uma API de Criteria. Os Managed Beans substituirão os MBs atuais do JSF e haverão anotações para injeção comuns a JSE e JEE, além do início de sua padronização. Os WebBeans serão contemplados pela CDI 1.0, a qual basicamente visará gerenciar objetos injetáveis ligados a contextos do ciclo de vida, possuindo anotações como @Inject, @Qualifier, @Scope, @Named e @Singleton.

Outra questão importante refere-se ao processo de limpeza, em que determinadas tecnologias tornaram-se obsoletas e entrarão no estágio de poda (pruning stage). Entre elas citou JAX-RPC, EJB Entity Beans, JAXR e JSR-88 Deployment. Os fornecedores não serão obrigados a implementá-las em releases futuras.

Por fim, a notícia que mais aguardávamos, a data do lançamento. Mike disse que os grupos de experts das JSR praticamente já concluíram seus trabalhos e que a Java EE 6 será liberada no dia 10 de dezembro!


JSF 2.0: Uma Abordagem Completa - Ed Burns



Ed iniciou a palestra fazendo uma pequena introdução sobre a tecnologia JavaServer Faces a fim de balizar o conhecimento do público. Apresentou o ciclo de vida no processamento de requisições do JSF e as funcionalidades desejadas (conversão e validação de dados, fluxo de páginas, integração com banco de dados, desempenho, segurança, etc).

Enfatizando os 4 pilares do JSF 2.0 (visão, interação com o modelo, navegação e ciclo de vida), destacou que as visões passarão a ser páginas Facelets, haverá a anotação @ManagedBean, e que o JSP será depreciado.

A Expression Language será mais poderosa, e na navegação de páginas o uso de XMLs será opcional, devido à nova navegação implícita, altamente dinâmica e especialmente útil com os novos objetos do tipo "flash". Com relação ao ciclo de vida, haverá um excelente suporte para o uso de Ajax, além do advento dos System Events, finos ouvintes dos eventos do ciclo.


Spring Roo - Rod Johnson

Em substituição ao Chris Schalk da Google, Rod fez uma palestra prática totalmente sem slides apresentando uma nova solução da SpringSource: o Spring Roo [5].

Trata-se de um gerador de código em Java que como qualquer do gênero visa impulsionar a produtividade do desenvolvimento. Achei muito semelhante ao Ruby on Rails: um prompt de comando (shell) que executa diversas tarefas, especialmente a criação automática de inúmeros arquivos e o fornecimento de um servidor Web simples para os testes. Funciona até auto-completion no estilo bash!

A instalação do Roo é extremamente simples, mas ele exige que tenhamos o seguinte ambiente: Eclipse IDE com plugins WTP e AJDT. Isso mesmo, o Spring Roo usa extensivamente o AspectJ! São criados arquivos ".aj" a fim de incluir funcionalidade as novas classes.

Outras características importantes foram a criação automática de testes unitários, o fato de o projeto ser gerenciado pelo Maven (são criados diversos arquivos "pom.xml") e a persistência ser manipulada através de JPA.

A mágica do Roo acontece durante o gerenciamento das entidades. Por exemplo, os métodos persist() e findAll() são inseridos em cada entidade através de injeção de aspectos! Achei um tanto bizarro, mas não deixa de ser uma possibilidade interessante. As entidades não precisam declarar métodos getters e setters e nem toString() customizado, pois, novamente, aspectos fazem isso..!

Eis um exemplo de classe criada no Roo:


@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Restaurant {

private String name;

}

Tal como no Rails, podemos criar com uma agilidade e rapidez descomunal uma aplicação completa incluindo scaffold (i.e. o CRUD), testes unitários, páginas web para apresentação (em JSF!) e toda a configuração da persistência.

Atualmente o Spring Roo está na release candidate 2 e tem previsão de ser liberado no final desse ano. Infelizmente ainda não tem suporte para o NetBeans, devido ao fato de depender do Eclipse AJDT.

Referências:
[1] TDC 2009 Floripa
[2] Spring Framework
[3] Java EE 6 Technologies
[4] JavaServer Faces Technology
[5] Spring Roo

sábado, 7 de novembro de 2009

Auction5 sample web application released



I'm very proud to announce that Auction5, a sample web application built under Demoiselle Framework [1] has just been released to the public. Its complete documentation including source codes retrieval and deployment instructions can be found here: [2].

Auction5 is a MVC-structured application that exemplifies an online auction system using Demoiselle Framework and related technologies. Its main goal is to provide a complete transactional application based on JavaServer Faces (JSF 1.2 [3]) and Java Persistence API (JPA 1.0 [4]) specifications.


Moreover, the sample assembles several top computing technologies such as Java EE, Maven, JBoss Application Server, Apache Tomcat, and PostgreSQL DBMS.

Please send me your comments and or suggestions.

References:
[1] Demoiselle Framework
[2] Auction5 Application Sample
[3] JavaServer Faces 1.2
[4] Java Persistence API 1.0

quinta-feira, 24 de setembro de 2009

Demoiselle JPA in a Nutshell



One of the greatest new features coming to 1.1 release of Demoiselle Framework [1] is the ability of using JPA [2] (Java Persistence API) in a straightforward manner. JPA specification [3] is a trend in Java development and is often recommended to be used in new applications.

In this article we'll explore the technical steps involved on developing the simplest DAO [4] (Data Access Object) implementation classes with the new JPA support components.

So, what's new in Demoiselle concerning JPA? First of all, there is a new injection aspect towards IDAO [5] interfaces. Basic classes implementing IDAO interface should now simply have an attribute of type EntityManager [6] annotated by @Injection and then with the aid of AspectJ engine and some proxy classes the magic happens!

For instance, consider the following IDepartmentDAO interface in order to manage a Department entity:

public interface IDepartmentDAO extends IDAO<Department> {

    public Department findById(Short id);

}

The respective implementation class DepartmentDAO should have the structure below:

public class DepartmentDAO implements IDepartmentDAO {

    @Injection
    private EntityManager em;

    public Department findById(Short id) {
        return em.find(Department.class, id);
    }

    public boolean exists(Department pojo) {
        return em.contains(pojo);
    }

    public Object insert(Department pojo) {
        em.persist(pojo);
        return pojo;
    }

    public void update(Department pojo) {
        em.merge(pojo);
    }

    public void remove(Department pojo) {
        em.remove(pojo);
    }

}

Simple, isn't it?

EntityManager is the interface used to interact with the persistence context in JPA. A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed. This interface defines the methods that are used to interact with the persistence context. The EntityManager API is used to create and remove persistent entity instances, to find entities by their primary key, and to query over entities.

Within a given unit of work, such as an HTTP request in web applications, a single instance of EntityManager is created and automatically injected on every DAO class that is referenced on that thread. That EntityManager is then closed as the transaction is finished.

Moreover, even outside a complete and robust Java EE application, i.e. without an EJB [3] container, the developer could make easy use of JPA in Demoiselle!

In order to turn it even simpler for the developer, the DAO implementation could still make use of the new JPAGenericDAO [7] support class. Thus, DepartmentDAO could have the following code:

public class DepartmentDAO extends JPAGenericDAO<Department>
    implements IDepartmentDAO {

    public Department findById(Short id) {
        return super.findById(id);
    }

}

In other words, there is no need anymore of storing an EntityManager field and to implement all IDAO methods. That is, there will be almost nothing left to do but customized business rules methods!

If you want to know more about the framework technical details, there will be another article explaining the whole architecture behind the scenes. :D

References:
[1] Demoiselle Framework
[2] The Java Persistence API
[3] EJB 3.0 JPA Specification
[4] Data Access Object
[5] IDAO interface
[6] EntityManager interface
[7] JPAGenericDAO class


terça-feira, 18 de agosto de 2009

Plugins essenciais para o Eclipse...



Eis alguns plugins interessantes e às vezes indispensáveis para a IDE Eclipse [1]:
  • m2eclipse [2]: integração do Maven 2 com o Eclipse
  • JBoss Tools [3]: utilitários para desenvolvimento de projetos Web
  • AJDT [4]: ferramentas de desenvolvimento com AspectJ (AJ)
  • Subclipse [5]: suporte ao controle de versionamento Subversion (SVN)



Lembre-se de que o ideal é sempre instalar os plugins via Update Site...

Referências:

[1] http://www.eclipse.org/
[2] http://m2eclipse.sonatype.org/
[3] http://www.jboss.org/tools/
[4] http://www.eclipse.org/ajdt/
[5] http://subclipse.tigris.org/

terça-feira, 4 de agosto de 2009

findjar.com: Encontre o jarro perdido!



O problema: java.lang.ClassNotFoundException


Como desenvolvedor Java, você com certeza passou por isso alguma vez...
Eis um exemplo de stack trace em que ocorre este tipo de exceção:


Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 21 more


Para resolver isso, é preciso encontrar o arquivo JAR em que a classe referenciada se encontra e colocá-lo no classpath da aplicação que está sendo executada. Mas e se não soubermos onde fica essa classe...?

A solução 1: grep


Se estiver com sorte, você já possui a biblioteca que contém essa classe. Neste caso, no Linux basta executar um rgrep ou um grep -r e copiar o arquivo da biblioteca.

A solução 2: findjar.com


Caso você ainda não possua a biblioteca localmente, é preciso saber o nome do arquivo JAR e onde encontrá-lo na Internet...

Para a primeira pergunta, o site findJAR.com[1] é um grande auxílio. Trata-se de um motor de busca que nos ajuda a encontrar arquivos do tipo JAR que contêm as classes Java requeridas. Basta digitar o nome (simples ou qualificado) da classe que você precisa e iniciar a busca!

findJAR.com é uma mão na roda para resolver com facilidade problemas do tipo NoClassDefFoundError e ClassNotFoundException.

Referências:


[1] http://www.findjar.com

domingo, 2 de agosto de 2009

Encoding UTF-8 no Tomcat 6.0



Ao utilizar o servidor Apache Tomcat 6.0 [1] para o deploy de aplicações Web em Java utilizando o encoding Unicode (UTF-8) [2] podemos enfrentar pequenos problemas quando existe entrada de dados pelo usuário.

Por exemplo, considere a aplicação a seguir que faz uso de um formulário para o cadastro de categorias de produtos:



A anomalia ocorre durante o processo de envio da requisição HTTP do tipo POST [3]. Veja na imagem a seguir o problema na acentuação resultante do formulário:



Uma possível solução para este problema consiste na criação de um filtro, ou Filter [4], um padrão de projeto da especificação Java EE. A função deste filtro é de interceptar as requisições HTTP e forçar a codificação de caracteres para a que for parametrizada (no exemplo, a UTF-8).

A primeira etapa envolve a criação de uma nova classe na aplicação, cujo nome será EncodingFilter e a qual deverá implementar a interface Filter [5] da especificação Java Servlet. Desta forma, o arquivo EncodingFilter.java terá o seguinte conteúdo:


package com.hsiconsultoria.zabumba.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class EncodingFilter implements Filter {

private String encoding;

public void init(FilterConfig config) throws ServletException {
this.encoding = config.getInitParameter("encoding");
}

public void destroy() {
}

public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {

request.setCharacterEncoding(encoding);

chain.doFilter(request, response);
}

}

Em seguida é preciso indicar a existência desse novo filtro no arquivo WEB-INF/web.xml, também conhecido como Deployment Descriptor [6]. É neste arquivo que o filtro será devidamente configurado para interceptar as requisições HTTP na aplicação Web.

Sendo assim, basta incluir as linhas seguintes no arquivo web.xml:



EncodingFilter
EncodingFilter

com.hsiconsultoria.zabumba.filter.EncodingFilter


filtro para codificação dos caracteres
encoding
UTF-8



EncodingFilter
/*


Para finalizar, é só salvar tudo e reinicializar o Tomcat. Veja na tela abaixo o resultado dessa alteração na aplicação:



Referências:
[1] Apache Tomcat 6.0
[2] Formato UTF-8
[3] HTTP/1.1 Method Definitions
[4] Intercepting Filter Pattern
[5] Interface Filter
[6] Java EE Deployment Descriptors

sábado, 21 de fevereiro de 2009

EJB 3 Developer Guide: Errata on Web Security Sample


Today I was just working on the lab 4 / chapter 12 of this book [1] and then I got stuck into an issue...

The environment used on the tests was GlassFish Application Server v2 and the sample code concerns Web Container Security. It just didn't work as expected...

"lab4:

to run program enter http://localhost:8080/BankService/index.html in browser. http://localhost:8080 is url for web applications as shown by Glassfish on startup. If Glassfish provides port other than 8080 then modify url accordingly.

then enter scott/xyz username/password in login form. servlet menu for adding customer to database is then displayed.

enter ramon/abc username/password in login form. error page is then displayed."

The problem is that the page was never displayed despite using the proper user (i.e. "scott") because security always failed.

After googling for a while, I could realize some missing points in the book packaged sample [2].

Yet I made some small modifications in order to test the application with both users: "scott" (belonging to "bankemployee" group) and "ramon" (belonging to "bankcustomer" group).

So, the idea behind it was to forbid access to a specific servlet ("/addCustomer" address) whenever the user did not belong to "bankemployee" role. Also, to restrict access to "/findCustomer" servlet to both roles.

First of all, I modified the WEB descriptor file, "WEB-INF/web.xml", by adding "/findCustomer" servlet mapping, specifying "/addCustomer" URL pattern in the security constraint and an optional welcome file. Here's the entire code:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>AddCustomerServlet</servlet-name>
<servlet-class>ch12lab4.AddCustomerServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>FindCustomerServlet</servlet-name>
<servlet-class>ch12lab4.FindCustomerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddCustomerServlet</servlet-name>
<url-pattern>/addCustomer</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>FindCustomerServlet</servlet-name>
<url-pattern>/findCustomer</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>bank service most restricted</web-resource-name>
<url-pattern>/addCustomer</url-pattern>
<url-pattern>/addCustomer.html</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>bankemployee</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>bank service restricted</web-resource-name>
<url-pattern>/findCustomer</url-pattern>
<url-pattern>/findCustomer.html</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>bankcustomer</role-name>
<role-name>bankemployee</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>bankemployee</role-name>
</security-role>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>file</realm-name>
</login-config>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
The second detail was the creation of a Sun's specific descriptor, "WEB-INF/sun-web.xml", which did not exist in the sample code. The key point here was to map existing groups already specified in the application server's realm file into roles used inside the WEB application. In the current case they share the same names, "bankcustomer" and "bankemployee", on both environments. Here's the new file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Application Server 8.1 Servlet 2.4//EN"
"http://www.sun.com/software/appserver/dtds/sun-web-app_2_4-1.dtd">
<sun-web-app>
<context-root>/BankService</context-root>
<security-role-mapping>
<role-name>bankcustomer</role-name>
<group-name>bankcustomer</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>bankemployee</role-name>
<group-name>bankemployee</group-name>
</security-role-mapping>
</sun-web-app>
That's it, it worked at last and I ran the whole book sample codes!

Now I'm gonna take a rest watching "The Hunt for the Red October", which had just finished on the eMule's queue...! :D

References:

[1] Michael Sikora, "EJB 3 Developer Guide", http://www.packtpub.com/developer-guide-for-ejb3/book