Sunday, December 14, 2008

Glassfish + Jackrabbit + PostgreSQL (2)

RMI Access

It is now time to create our first Netbeans 6.5 project to access our repository. First of all, in Netbeans create a New Project->Java Web->Web Application project named testrepositoryweb.


Choose Glassfish V2 (or V3) and JavaEE 5 as illustrated below:


Leave the defaults for the remaining settings. After your project has been created, we can now add a JUnit test to check a RMI connection with the testrepository.

Before doing it, add all the libraries discussed in the Glassfish set up tutorial to the project Library folder, additionally you have to add the jackrabbit-jcr-rmi-1.5.0.jar library.

Go to Test Packages right click and select JUnit test (if it is not in the list choose New->Other...->JUnit) and create a RMIConnectionTest unit case:


You can choose the default package or better create your own package (for this tutorial it does not matter which way you choose).

The source code for the class is:
import java.net.MalformedURLException;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

import javax.jcr.Repository;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import org.apache.jackrabbit.rmi.repository.URLRemoteRepository;

public class RMIConnectionTest
{

public RMIConnectionTest(){}

@BeforeClass
public static void setUpClass() throws Exception {}

@AfterClass
public static void tearDownClass() throws Exception {}

@Before
public void setUp() {}

@After
public void tearDown() {}

@Test
public void testConnection()
{
Session session = null;
try
{
Repository repository = new URLRemoteRepository(
"http://localhost:8080/jackrabbit/rmi");

session = repository.login(new SimpleCredentials(
"anonymous", "anonymous".toCharArray()));

System.out.println("Successfully logged in as user: "
+ session.getUserID());
}
catch(java.net.MalformedURLException ex)
{
fail(ex.getMessage());
}
catch(javax.jcr.LoginException ex)
{
fail(ex.getMessage());
}
catch(javax.jcr.RepositoryException ex)
{
fail(ex.getMessage());
}
finally
{
if(null != session)
session.logout();
}
}
}
At this stage since we didn't set up a username and password for our repository, it will accept any username and password.

If you right click on the RMIConnectionTest.java and choose Run File you should get the test passing with a final message:
Successfully logged in as user: anonymous

JNDI Access


RMI is of course slow. Depending on your repository deployment model you can choose a JNDI connection.

Note that a Jackrabbit repository directory contains a lock file that prevents it from being accessed simultaneously by multiple processes. You will see repository startup exceptions caused by the lock file if you fail to properly close all sessions or otherwise shut down the repository before leaving the process that accesses a repository. This behaviour may create some issues with Glassfish if you choose Shared J2EE Resource deployment model. My advice is to simply have one distinct in-process repository for each web application. In other words, remove (undeploy in Glassfish admin) any application, including the jackrabbit web app previously installed and only leave the testrepositoryweb application.
More specifically, if you get the following exception:

javax.naming.CommunicationException: serial context communication ex
[Root exception is javax.jcr.RepositoryException: The repository home
/home/emanuele/testrepository appears to be in use since the file named .lock is
already locked by the current process.]

while trying to access a repository, you have almost certainly been hit by the problem described above.

Ok, time to set up a JNDI resource for our testrepositoryweb app, we can do this easily from the Glassfish (V2) admin console, go to Resources->JNDI->Custom Resources and create a new Custom Resource as follows:

In other words, create a Custom Resource with:
  • JNDI Name = jcr/testrepository
  • Resource Type = javax.jcr.Repository
  • Factory Class = org.apache.jackrabbit.core.jndi.BindableRepositoryFactory
  • repHomeDir =
  • configFilePath = /repository.xml
The JNDI name is pretty much free form.

We can now create a Servlet that will login to our
testrepository (in a real application we will use instead a Listener implementing ServletContextListener as I will show in a later tutorial). In Netbeans right click on the testrepositoryweb and choose New->Servlet and enter the following:


The source code for the newly created JNDIRepositoryLogin servlet is (we are only implementing the doGet method for the sake of this example):
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import javax.naming.*;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;

public class JNDIRepositoryLogin extends HttpServlet
{

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try
{}
finally { out.close(); }
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter writer = response.getWriter();

try
{
InitialContext ctx = new InitialContext();
Repository repository = (Repository) ctx.lookup ("jcr/testrepository");

Session session = repository.login(new SimpleCredentials(
"anonymous", "anonymous".toCharArray()));

writer.println("<h1>Success</h1>");
writer.println("logged in as user: " + session.getUserID());
session.logout();
ctx.close();

}
catch(javax.naming.NamingException ex)
{
ex.printStackTrace(writer);
}
catch(javax.jcr.LoginException ex)
{
ex.printStackTrace(writer);
}
catch(javax.jcr.RepositoryException ex)
{
ex.printStackTrace(writer);
}
finally
{
}
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
public String getServletInfo() {
return "Short description";
}
}


you can now launch your application and enter the URL:
http://localhost:8080/testrepositoryweb/JNDIRepositoryLogin
You should see a web page displaying:




Datastore

The last feature we will set in this tutorial is the Datastore Setting up a Datastore is trivial, simply add the following entry after the Repository tag in you repository xml configuration file:
<DataStore class="org.apache.jackrabbit.core.data.db.DbDataStore">
<param name="url" value="jdbc:postgresql:testrepository"/>
<param name="user" value="postgres"/>
<param name="password" value=""/>
<param name="databaseType" value="postgresql"/>
<param name="driver" value="org.postgresql.Driver"/>
<param name="minRecordLength" value="1024"/>
<param name="maxConnections" value="3"/>
<param name="copyWhenReading" value="true"/>
<param name="tablePrefix" value=""/>
</DataStore>

Don't forget to set the username and password matching your DB configuration.

1 comments:

Dom said...

Thanks for the blog about Jackrabbit and PostGresql, I found it very useful!

One note is that on my firefox browser, I cannot see the proper values for repoHomeDir and configFilePath, I simply assumed that you were using the jndi resource factory would look in the same place as the web app from the first part does. It was only after I accidentally enlarged the screen shot that it was clear that this was my problem. Perhaps you could fix this for others.

Thanks,
Domenic Figliomeni