<commit_before>### Building

This project requires all of its dependencies be available from maven compatible repositories.
Some of these dependencies have not been pushed to the Maven Central Repository, however you
can obtain them from [lombok-dev-deps][1].

Follow the instructions found there to install the required dependencies into your local Maven
repository before attempting to build this plugin.

[1]: https://github.com/aalmiray/lombok-dev-deps<commit_msg>Added readme mirroring plugin documentation<commit_after>
JCR support
-----------

Plugin page: [http://artifacts.griffon-framework.org/plugin/jcr](http://artifacts.griffon-framework.org/plugin/jcr)


The JCR plugin enables the usage of the [JCR 2.0][1] specification via [Apache Jackrabbit][2].

Usage
-----
Upon installation the plugin will generate the following artifacts in
`$appdir/griffon-app/conf`:

 * JcrConfig.groovy - contains repository definitions.

A new dynamic method named `withJcr` will be injected into all controllers,
giving you access to a `javax.jcr.Session` object, with which you'll be able to
make calls to the repository. Remember to make all repository calls off the UI
thread otherwise your application may appear unresponsive when doing long
computations inside the UI thread.

This method is aware of multiple repositories. If no repositoryName is specified
when calling it then the default repository will be selected. Here are two
example usages, the first queries against the default repository while the
second queries a repository whose name has been configured as 'internal'

    package sample
    class SampleController {
        def queryAllRepositories = {
            withJcr { repositoryName, session -> ... }
            withJcr('internal') { repositoryName, session -> ... }
        }
    }

The following list enumerates all the variants of the injected method

 * `<R> R withJcr(Closure<R> stmts)`
 * `<R> R withJcr(CallableWithArgs<R> stmts)`
 * `<R> R withJcr(String sessionName, Closure<R> stmts)`
 * `<R> R withJcr(String sessionName, CallableWithArgs<R> stmts)`

These methods are also accessible to any component through the singleton
`griffon.plugins.jcr.JcrEnhancer`. You can inject these methods to
non-artifacts via metaclasses. Simply grab hold of a particular metaclass and
call `JcrEnhancer.enhance(metaClassInstance)`.

Configuration
-------------

### JcrAware AST Transformation

The preferred way to mark a class for method injection is by annotating it with
`@griffon.plugins.jcr.JcrAware`. This transformation injects the
`griffon.plugins.jcr.JcrContributionHandler` interface and default behavior
that fulfills the contract.

### Dynamic Method Injection

Dynamic methods will be added to controllers by default. You can
change this setting by adding a configuration flag in `griffon-app/conf/Config.groovy`

    griffon.jcr.injectInto = ['controller', 'service']

Dynamic method injection will be skipped for classes implementing
`griffon.plugins.jcr.JcrContributionHandler`.

### Events

The following events will be triggered by this addon

 * JcrConnectStart[config, repositoryName] - triggered before connecting to the repository
 * JcrConnectEnd[repositoryName, session] - triggered after connecting to the repository
 * JcrDisconnectStart[config, repositoryName, session] - triggered before disconnecting from the repository
 * JcrDisconnectEnd[config, repositoryName] - triggered after disconnecting from the repository

### Multiple Stores

The config file `JcrConfig.groovy` defines a default repository block. As the
name implies this is the repository used by default, however you can configure
named repositories by adding a new config block. For example connecting to a
repository whose name is 'internal' can be done in this way

    repositories {
        internal {
            configuration = '/var/jackrabbit/internal/repository.xml'
            home = '/var/jackrabbit/internal'
            credentials {
                username = 'wallace'
                password = '<PASSWORD>'
            }
        }
    }

This block can be used inside the `environments()` block in the same way as the
default repository block is used.

### Repository Types

Until further notice only local repositories (those that require a `repository.xml`
file and a home directoy) can be configured by this plugin.

### Configuration Storage

The plugin will load and store the contents of `JcrConfig.groovy` inside the
application's configuration, under the `pluginConfig` namespace. You may retrieve
and/or update values using

    app.config.pluginConfig.jcr

### Connect at Startup

The plugin will attempt a connection to the default database at startup. If this
behavior is not desired then specify the following configuration flag in
`Config.groovy`

    griffon.jcr.connect.onstartup = false

### Example

Inspired by [Jackrabbit's First Hops][3] here's how you can write a controller
that creates, queries and deletes some content

    package sample
    import javax.jcr.*
    @griffon.plugins.jcr.JcrAware
    class SampleController {
        def insert = {
            withJcr { repositoryName, session ->
                Node root = session.rootNode
                Node hello = root.addNode("hello")
                Node world = hello.addNode("world")
                world.setProperty("message", "Hello, World!")
                session.save()
            }
        }

        def queryAndDelete = {
            withJcr { repositoryName, session ->
                Node root = session.rootNode
                Node node = root.getNode("hello/world")
                println(node.path)
                println(node.getProperty("message").getString())
                root.getNode("hello").remove()
                session.save()
            }
        }
    }

The plugin exposes a Java friendly API to make the exact same calls from Java,
or any other JVM language for that matter. Here's for example the previous code
rewritten in Java. Note the usage of @JcrWare on a Java class

    package sample;
    import griffon.core.GriffonApplication;
    import griffon.util.CallableWithArgs;
    import java.awt.event.ActionEvent;
    import javax.jcr.*;
    import org.codehaus.griffon.runtime.core.AbstractGriffonController;
    @griffon.plugins.jcr.JcrAware
    public class SampleController extends AbstractGriffonController {
        private SampleModel model;

        public void setModel(SampleModel model) {
            this.model = model;
        }

        public void insert(ActionEvent event) {
            withJcr(new CallableWithArgs<Void>() {
                public Void call(Object[] args) {
                    Session session = (Session) args[1];
                    Node root = session.getRootNode();
                    Node hello = root.addNode("hello");
                    Node world = hello.addNode("world");
                    world.setProperty("message", "Hello, World!");
                    session.save();
                    return null;
                }
            });
        }
        
        public void queryAndDelete(ActionEvent event) {
            withJcr(new CallableWithArgs<Void>() {
                public Void call(Object[] args) {
                    Session session = (Session) args[1];
                    Node root = session.getRootNode();
                    Node node = root.getNode("hello/world");
                    System.out.println(node.getPath());
                    System.out.println(node.getProperty("message").getString());
                    root.getNode("hello").remove();
                    session.save();
                    return null;
                }
            });
        }
    }

Testing
-------

Dynamic methods will not be automatically injected during unit testing, because
addons are simply not initialized for this kind of tests. However you can use
`JcrEnhancer.enhance(metaClassInstance, jcrProviderInstance)` where
`jcrProviderInstance` is of type `griffon.plugins.jcr.JcrProvider`.
The contract for this interface looks like this

    public interface JcrProvider {
        <R> R withJcr(Closure<R> closure);
        <R> R withJcr(CallableWithArgs<R> callable);
        <R> R withJcr(String repositoryName, Closure<R> closure);
        <R> R withJcr(String repositoryName, CallableWithArgs<R> callable);
    }

It's up to you define how these methods need to be implemented for your tests.
For example, here's an implementation that never fails regardless of the
arguments it receives

    class MyJcrProvider implements JcrProvider {
        public <R> R withJcr(Closure<R> closure) { null }
        public <R> R withJcr(CallableWithArgs<R> callable) { null }
        public <R> R withJcr(String repositoryName, Closure<R> closure) { null }
        public <R> R withJcr(String repositoryName, CallableWithArgs<R> callable) { null }
    }

This implementation may be used in the following way

    class MyServiceTests extends GriffonUnitTestCase {
        void testSmokeAndMirrors() {
            MyService service = new MyService()
            JcrEnhancer.enhance(service.metaClass, new MyJcrProvider())
            // exercise service methods
        }
    }

On the other hand, if the service is annotated with `@JcrAware` then usage
of `JcrEnhancer` should be avoided at all costs. Simply set
`jcrProviderInstance` on the service instance directly, like so, first the
service definition

    @griffon.plugins.jcr.JcrAware
    class MyService {
        def serviceMethod() { ... }
    }

Next is the test

    class MyServiceTests extends GriffonUnitTestCase {
        void testSmokeAndMirrors() {
            MyService service = new MyService()
            service.jcrProvider = new MyJcrProvider()
            // exercise service methods
        }
    }

Tool Support
------------

### DSL Descriptors

This plugin provides DSL descriptors for Intellij IDEA and Eclipse (provided
you have the Groovy Eclipse plugin installed). These descriptors are found
inside the `griffon-jcr-compile-x.y.z.jar`, with locations

 * dsdl/jcr.dsld
 * gdsl/jcr.gdsl

### Lombok Support

Rewriting Java AST in a similar fashion to Groovy AST transformations is
possible thanks to the [lombok][4] plugin.

#### JavaC

Support for this compiler is provided out-of-the-box by the command line tools.
There's no additional configuration required.

#### Eclipse

Follow the steps found in the [Lombok][4] plugin for setting up Eclipse up to
number 5.

 6. Go to the path where the `lombok.jar` was copied. This path is either found
    inside the Eclipse installation directory or in your local settings. Copy
    the following file from the project's working directory

         $ cp $USER_HOME/.griffon/<version>/projects/<project>/plugins/jcr-<version>/dist/griffon-jcr-compile-<version>.jar .

 6. Edit the launch script for Eclipse and tweak the boothclasspath entry so
    that includes the file you just copied

        -Xbootclasspath/a:lombok.jar:lombok-pg-<version>.jar:        griffon-lombok-compile-<version>.jar:griffon-jcr-compile-<version>.jar

 7. Launch Eclipse once more. Eclipse should be able to provide content assist
    for Java classes annotated with `@JcrAware`.

#### NetBeans

Follow the instructions found in [Annotation Processors Support in the NetBeans
IDE, Part I: Using Project Lombok][5]. You may need to specify
`lombok.core.AnnotationProcessor` in the list of Annotation Processors.

NetBeans should be able to provide code suggestions on Java classes annotated
with `@JcrAware`.

#### Intellij IDEA

Follow the steps found in the [Lombok][4] plugin for setting up Intellij IDEA
up to number 5.

 6. Copy `griffon-jcr-compile-<version>.jar` to the `lib` directory

         $ pwd
           $USER_HOME/Library/Application Support/IntelliJIdea11/lombok-plugin
         $ cp $USER_HOME/.griffon/<version>/projects/<project>/plugins/jcr-<version>/dist/griffon-jcr-compile-<version>.jar lib

 7. Launch IntelliJ IDEA once more. Code completion should work now for Java
    classes annotated with `@JcrAware`.

[1]: http://jcp.org/en/jsr/detail?id=283
[2]: http://jackrabbit.apache.org
[3]: http://jackrabbit.apache.org/first-hops.html
[4]: /plugin/lombok
[5]: http://netbeans.org/kb/docs/java/annotations-lombok.html

### Building

This project requires all of its dependencies be available from maven compatible repositories.
Some of these dependencies have not been pushed to the Maven Central Repository, however you
can obtain them from [lombok-dev-deps][lombok-dev-deps].

Follow the instructions found there to install the required dependencies into your local Maven
repository before attempting to build this plugin.

[lombok-dev-deps]: https://github.com/aalmiray/lombok-dev-deps
{'fraction_non_alphanumeric': 0.08133144920709688, 'fraction_numerical': 0.0023551577955723034, 'mean_word_length': 3.0686681571382945, 'pattern_counts': {'":': 0, '<': 42, '<?xml version=': 0, '>': 46, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '391796', 'n_tokens_mistral': 3510, 'n_tokens_neox': 3337, 'n_words': 1268}