Pages

Showing posts with label jcr. Show all posts
Showing posts with label jcr. Show all posts

Saturday, December 19, 2015

Scribble 0.3.0

I am proud to announce a new version of the the Scribble testing library! The biggest changes are the new modularization and documentation. For every functional aspect there is now a separate module so that not a whole load of unused dependencies have to be included in your project if you only require just a single functional aspect. In addition to this, the entire project documentation is now kept in the source and be generated using maven's site support. This includes this wiki documentation as well, although the publishing process is not yet part of the release build jobs.
As new features for testing I introduce a http server as a TestRule that can be set up in various ways to server static content. It's still rather limited, but will be contiuously improved in future releases. Further features are the possibility to create temporary zip files, record system out and err via a TestRule and capture and restore System Properties - a simple rule that helps keeping the test environment clean, and finally a matcher for matching date strings against a data format.

For more information, have a look at the wiki or find the source code on GitHub

Task

Story

  • [SCRIB-35] - Embedd static HTTP content as a rule
  • [SCRIB-43] - Build documentation as part of the release
  • [SCRIB-49] - Create zipped temp file from resources
  • [SCRIB-50] - Date Format Matcher
  • [SCRIB-52] - Rule for capturing System.out and System.err
  • [SCRIB-53] - Rule for setting and restoring System Properties

Bug

  • [SCRIB-39] - ConfigPropertyInjection#isMatching sets default value
  • [SCRIB-51] - TemporaryFile not usable as ClassRule
  • [SCRIB-57] - ApacheDS all prevents exclusion of modules
  • [SCRIB-58] - Remove SLF4J Binding dependencies
  • [SCRIB-59] - DirectoryServer/DirectoryService not working as ClassRule

Wednesday, July 8, 2015

Scribble Release 0.2.0

I am proud to announce a new version of the the Scribble testing library! The new version has support for an embedded ldap server which allows to write tests against an ldap server without having to rely on existing infrastructure. Further, the JCR support has been improved, now it's possible to pre-initialize a JCR repository with content from a descriptor file and to create a security-enabled in-memory repository. Some additional improvements have been made in the CDI injection support and the matchers have been extended for availability checks for URLs.

For more information, have a look at the wiki or find the source code on GitHub.

Release Notes - Scribble - Version 0.2.0

Bug

  • [SCRIB-31] - Primitive types not support for ConfigProperty injection
  • [SCRIB-32] - String to Number conversion of default values in ConfigProperty injection fails
  • [SCRIB-41] - LDAP Rules are not properly applied
  • [SCRIB-42] - ResourceAvailabilityMatcher is not compatible with URL
  • [SCRIB-48] - Directory Rules can not be used as ClassRules

Story

  • [SCRIB-1] - Builder support for LDAP Server and Service
  • [SCRIB-2] - Make LDAP Port configurable
  • [SCRIB-5] - Matchers for availability of an URL
  • [SCRIB-10] - Support for prepared JCR Content
  • [SCRIB-12] - Support security enabled content repositories
  • [SCRIB-14] - Add Convenience method for admin login
  • [SCRIB-33] - Convenience Methods for Directory creation
  • [SCRIB-34] - Convenience Method for anonymous login
  • [SCRIB-38] - Supply package-info.java

Tuesday, May 19, 2015

Scribble 0.1.2

Today I released version 0.1.2 of the Scribble testing framwork. Beside some bugifxes, the main improvement was the added support for initializing the JCR ContentRepository test rules with nodetype definitions from a CND file.

 Bug

    [SCRIB-16] - @Inject Annotations is not considered
    [SCRIB-19] - InjectableHolders are not recognized properly when injecting
    [SCRIB-23] - Sonar Issue: The use of XPath.evaluate() is vulnerable to XPath injection
    [SCRIB-24] - Sonar Issue: The usage of /DocumentBuilder.parse(...) is vulnerable to XML External Entity attacks
    [SCRIB-25] - Sonar Issue: Use a cryptographically strong random number generator (RNG) like "java.security.SecureRandom" in place of this PRNG

Story

    [SCRIB-11] - Convenience Methods for InMemory and StandaloneRepository creation
    [SCRIB-20] - Initialize Repository with CND node types

Task

    [SCRIB-17] - Set up Build for master branch
    [SCRIB-18] - Set up Test Quality Assesment
    [SCRIB-21] - Update Apache DS Dependency to 2.0.0-M20
    [SCRIB-22] - Fix "Copyright and license headers should be defined" Rule configuration

Tuesday, April 29, 2014

JUnit Testing with Jackrabbit

Writing unit tests for your code is not only best practices, it's essential for writing quality code. In order to write good unit tests, you should use mocking of code not under test. But what if you're using a technology or an API that would require quite a lot of complicated mocks?
In this article I'd like to describe how you write unit tests for code that accesses a JCR repository.

At first I really tried to mock the JCR API using Mockito, but stopped my attempt at the point where I had to mock the behavior of the Query Object Model. It became apparent, that writing mocks would outweigh the effort to write the actual production code by far. So I had to search for an alternative and found one.

The reference implementation of JCR is the Apache Jackrabbit project. This implementation comes with a set of JCR Repository implementations, one of these is the TransientRepository. The TransientRepository starts the repository on first login and shuts it down on the last session being closed. The repository is created in memory which works pretty fast and makes it the best solution for unit testing. But nevertheless, a directory structure is created for the repository and unless not specified a config file is created as well.

For writing unit tests against this repository, we need the following:
  • a temporary directory to locate the directory structure of the repository
  • a configuration file (unless you want one created on every startup)
  • the repository instance
  • a CND content model description to initialize the repository data model (optional) 
  • an admin session to perform administrator operations
  • a cleanup operation to remove the directory structure
  • the maven dependencies to satisfy all dependencies
Let's start with the Maven dependencies. You need the JCR Spec, the Jackrabbit core implementation and the Jackrabbit commons for setting up the repository.

<properties>
  <!-- JCR Spec -->
  <javax.jcr.version>2.0</javax.jcr.version>
  <!-- JCR Impl -->
  <apache.jackrabbit.version>2.6.5</apache.jackrabbit.version>
</properties>
...
<dependencies>
<!-- The JCR API -->
  <dependency>
    <groupId>javax.jcr</groupId>
    <artifactId>jcr</artifactId>
    <version>${javax.jcr.version}</version>
  </dependency>
  <!-- Jackrabbit content repository -->
  <dependency>
    <groupId>org.apache.jackrabbit</groupId>
    <artifactId>jackrabbit-core</artifactId>
    <version>${apache.jackrabbit.version}</version>
    <scope>test</scope>
  </dependency>
  <!-- Jackrabbit Tools like the CND importer -->
  <dependency>
    <groupId>org.apache.jackrabbit</groupId>
    <artifactId>jackrabbit-jcr-commons</artifactId>
    <version>${apache.jackrabbit.version}</version>
    <scope>test</scope>
  </dependency>
</dependencies> 

Now let's create the directory for the repository. I recommend to locate it in a temporary folder so multiple test runs don't affect each other if cleanup failed. We use the Java TempDirectory facility for that:
//prefix for the repository folder
import java.nio.file.Files;
import java.nio.file.Path;
...
private static final String TEST_REPOSITORY_LOCATION = "test-jcr_";
...
final Path repositoryPath = 
      Files.createTempDirectory(TEST_REPOSITORY_LOCATION);

Next, you require a configuration file. If you already have a configuration file available in the classpath, i.e. in src/test/resource, you should load it first:
final InputStream configStream = 
  YourTestCase.class.getResourceAsStream("/repository.xml");

Knowing the location and the configuration, we can create the repository:
import org.apache.jackrabbit.core.config.RepositoryConfig;
import org.apache.jackrabbit.core.TransientRepository;
...
final Path repositoryLocation = 
      repositoryPath.toAbsolutePath();
final RepositoryConfig config = 
      RepositoryConfig.create(configStream, repositoryLocation.toString());
final TransientRepository repository = 
  new TransientRepository(config);

If you ommit the config parameter, the repository is created in the working directory including the repository.xml file, which is good for a start, if you have no such file.

Now that we have the repository, we want to login to create a session (admin) in order to populate the repository. Therefore we create the credentials (default admin user is admin/admin) and perform a login:
final Credentials creds = 
  new SimpleCredentials("admin", "admin".toCharArray());
final Session session = repository.login(creds);

With the repository running and an open session we can initialize the repository with our content model if require some extensions beyond the standard JCR/Jackrabbit content model. In the next step I import a model defined in the Compact Node Definition (CND) Format, described in JCR 2.0
import org.apache.jackrabbit.commons.cnd.CndImporter;
...
private static final String JCR_MODEL_CND = "/jcr_model.cnd.txt";
...
final URL cndFile = YourTestCase.class.getResource(JCR_MODEL_CND);
final Reader cndReader = new InputStreamReader(cndFile.openStream());
CndImporter.registerNodeTypes(cndReader, session, true);

All the code examples above should be performed in the @BeforeClass annotated method so that the repository is only created once for the entire test class. Otherwise a lot of overhead will be generated. Nevertheless, in the @Before and @After annotated methods, you should create your node structures and erase them again (addNode() etc).

Finally, after you have performed you test, you should cleanup the test environment again. Because a directory was created for the transient repository, we have to remove the directory again, otherwise the temp folder will grow over time.
There are three options for cleaning it up.
  1. Cleaning up in @AfterClass annotated method
  2. Cleaning up using File::deleteOnExit()
  3. Cleaning up using shutdown hook
I prefer combining 1 and 3 for fail-safe deletion. For option 1 we require a method to destroy the repository and cleaning up the directory. For deleting the directory I use Apache Commons FileUtil as it allows deletion of directory structures containing files and subdirectories. The method could look like this:

import org.apache.commons.io.FileUtils;
...
@AfterClass
public static void destroyRepository(){
  repository.shutdown();
  String repositoryLocation = repository.getHomeDir();
  try {
    FileUtils.deleteDirectory(new File(repositoryLocation));
  } catch (final IOException e) {
   ...
  }
  repository = null;
}

As fail-safe operation I prefer to add an additional shutdown hook that is executed when the JVM shuts down. This will delete the repository even when the @AfterClass method is not invoked by JUnit. I do not use the deleteOnExit() method of File as it requires the directory to be empty while I could call any code in the shutdown hook using my own cleanup implementation.


A shutdown hook can easily be added to the runtime by specifying a Thread to be executed on VM shutdown. We simply add a call to the destroy methode to the run() method.
Runtime.getRuntime().addShutdownHook(new Thread("Repository Cleanup") {
  @Override
  public void run() {
    destroyRepository();
  }
});

Now you should have everything to set-up you Test JCR Repositoy and tear-down the test environment. Happy Testing!

Monday, March 24, 2014

Some interesting points about JCR

While I was doing research about Java Content Repository (JCR, specified in JSR 283) for our current project I stumbled upon various interesting fact worth mentioning besides the actual specification details, which I like to share.
  • JCR was specified by Day Software, a company based in Bale, Switzerland which is especially interesting to me, as I live in Switzerland. Day Software has been bought by Adobe
  • In comparison with CMIS, JCR seems to be a functional superset of CMIS, as various sources indicate (see here, here,here or here), which means, JCR offers more functionality than CMIS does. Nevertheless, both standards are hardly directly comparable, as they focus on disjunct fields of use. While CMIS is service oriented and aims at repository interoperability, JCR is a standard and model for accessing hierarchical content. JCR seems to be easier to implement that CMIS.
  • JCR and CMIS are compatible to each other.
    • It's possible to access JCR repositories via CMIS. The JCR reference implementation (Jackrabbit) offers a service provider interface (SPI) implementation towards CMIS as well as there is a CMIS-JCR Bridge as part of the OpenCMIS implementation Apache Chemistry (for an overview, see here). The only restriction ist, that some of the JCR mixin types are not visible via CMIS.
    • An access via JCR to CMIS repositories should be a programmer's exercise as they are directly mappable.
  • JBoss ModeShape is an alternative open source implementation of the JCR that is not based on Jackrabbit. Although it does not implement the standard as complete as Jackrabbit does regarding optional elements (the mandatory elements are fully implemented), it offers various functionalities beyond Jackrabbit, such as Administration, configuration and enterprise readiness. The documentation is much more useful.
  • Both, Apache Jackrabbit and JBoss ModeShape are implementations of the JCR standard and form only the data management component of a content management system. Typically, CMS define their own data model and define workflows for dealing with the data.
  • Content Management Systems that are built upon a JCR Repository are
    • Magnolia CMS (support both ModeShape and Jackrabbit), it's a swiss product, by the way!
    • Jahia (Jackrabbit)
    • Hippo-CMS (Jackrabbit)
    • exo Platform (is more of a Social/Collaboration Portal product, but offers it's own JCR implementation)
    • Adobe Exprience Manager (former CQ5, uses ContentRepositoryExtreme (CRX) which is  a commercial, enterprise-ready JCR implementation, based on Jackrabbit),
  • Other implementations that supportJCR
  • Day Software (now part of Adobe) offers various JCR connectors for other commercial repositories:
  • Apache Sling is a framework allowing to access JCR repositories via REST and to create repository-oriented web applications.
  • Apache Stanbol is a framework combining the elements of semantic web with structured repository data of content management systems (JCR, CMIS) 
  • A good overview of available CM Repositories is shown here http://stackoverflow.com/questions/1174131/looking-for-a-good-programmable-java-cms-content-management-system
  • Looking at the Jackrabbit team you might stumble upon the name 'Roy Fielding'. Doesn't ring a bell? Maybe have a look at his must-read thesis (tl;dr: its about defining REST architectural style)