Posts Tagged Development

My Final Answer

I use the Java ‘final’ keyword quite a bit and I often get asked why. I guess the quick answer would be, “why not”?

I tend to use ‘final’ for instance variables, method parameters and local variables. The keyword can also be used to make classes and methods final but I leave that for the cases when I actually want that specific functionality.

Final instance variables (private fields) are useful for case when you want to specify the value once and then never again. This is helpful when configuring an object through Constructor dependency injection:

1
2
3
4
5
6
7
8
9
10
public class MyController implements Controller {

    private final MyService myService;

    public MyController(final MyService myService){
        this.myService = myService;
    }

    // other methods
}

This allows you to configure the service object in the controller and not have to worry about any of the other methods overwriting it.

Using final for method parameters is quite handy since it keeps you from unexpectedly overwriting one. Consider the following code:

1
2
3
4
5
6
7
8
9
public int count(List<String> list, String name){
    int count = 0;
    Iterator<String> names = list.iterator();
    while(names.hasNext()){
        name = names.next();
        if(name.equals(name)) count++;
    }
    return count;
}

which contains a possibly hard to find variable overwrite error, which becomes quite obvious when you use final

1
2
3
4
5
6
7
8
9
public int count(final List<String> list, final String name){
    int count = 0;
    Iterator<String> names = list.iterator();
    while(names.hasNext()){
        String nme = names.next();
        if(nme.equals(name)) count++;
    }
    return count;
}

Local variables are made final for basically the same reason.

Now, I know that simply because a variable is final does not mean it’s internal data cannot change, such as

1
2
3
final Map<String,Date> dates = new HashMap<String,Date>();
dates.get("Anniversary"); // will work fine
dates.put("Birthday");    // will work fine

The final keyword does not make them immutable, you just cannot overwrite them. Final makes you think a bit before overwriting a variable with a new object and it can sometimes point out, and save you from, potential bugs.

Eclipse Save Actions

Making your IDE do the work for you. If you use Eclipse (or one of its derivatives) you can have it automatically add ‘final’ to your code. Go into the Preferences and search for “Save Actions” and you should get something like what is show here. I generally auto-final method parameters and local variables since I have found that making private fields final can cause issues with some reflection and/or byte-code manipulation techniques.

Once you have the save actions setup, you will automatically have final added every time you save a file. I would imagine IntelliJ and NetBeans both have some similar functionality available.

Ultimately, the use of final comes down to personal preference. I find it useful and helpful to use the final keyword throughout my code. If you don’t then you really don’t have to use it unless you find a case where it’s absolutely necessary. To me, though, I figure why not use it?

And that’s my final answer.

Popularity: 4% [?]

  • Share/Bookmark

Tags: , , ,

Shortcutting Search Loops

A simple mistake I have seen a lot is not breaking out of a search loop once you have found what you are searching for. Take the code below, for example:

1
2
3
4
5
6
7
8
9
10
11
public User findUser(String name){
    User foundUser = null;
   
    for( User user : product.getUsers() ){
        if( name.equals(user.getName()) ){
            foundUser = user;
        }
    }
   
    return foundUser;
}

This code will loop through every user even after you have found the user you are looking for, which for large sets of data can get very inefficient. You can shortcut the loop with a break statement:

1
2
3
4
5
6
7
8
9
10
11
12
public User findUser(String name){
    User foundUser = null;
   
    for( User user : product.getUsers() ){
        if( name.equals(user.getName()) ){
            foundUser = user;
            break;
        }
    }
   
    return foundUser;
}

which will drop the control out of the loop once your user has been found. You could also simply return the user when found, such as

1
2
3
4
5
6
7
8
public User findUser(String name){
    for( User user : product.getUsers() ){
        if( name.equals(user.getName()) ){
            return user;
        }
    }
    return null;
}

This version causes there to be two exit points in the method, which is often frowned upon. Personally, I don’t see a problem with this in short simple methods.

Lastly, if you are a couple levels deep in a loop and cannot simply use a “return” for some reason, you can use a label to break out of the loop. Consider this example of a Map of Lists (something that you generally should not do):

1
2
3
4
5
6
7
8
9
10
User foundUser = null;
Iterator<List<User>> lists = users.values();
finder: for( List<User> list : users.values() ){
    for( User user : list){
        if( name.equals(user.getName()){
            foundUser = user;
            break finder;
        }
    }
}

which will break out of the loop labeled “finder” (the outer loop).

There are, of course, other ways of breaking out of loops, but these are the most general and cleanest, in my opinion.

Popularity: 2% [?]

  • Share/Bookmark

Tags: , ,

Blissful Development

I have been working on a personal project to create a movie catalog database for our collection of movies. Yes, there are a handful of both desktop and web-based applications available to do this, though, they all seem to be lacking in one feature or another… and I needed an interesting useful project. This project has been one of my most enjoyable personal development projects; if you have ever seen my project graveyard, you know there have been quite a few that didn’t get very far for one reason or anther, usually time and/or complexity.

I owe my development-bliss on this project to something not usually found on modern software projects (at least in my experience)… simplicity. No, not simplicity of the project goals, but of the tools and the environment. For development I am using my favorite text editor, Notepad++, Cygwin (yes, I am doing this on Windows… I have not yet found a good solid text editor for Linux, perhaps I will have to make peace with vi)… and that’s it unless you want to count Firefox. But, wait… where is the IDE, the pile of plug-ins, and tools? Well, okay… I am also using Grails along with JQuery and JQueryUI. That’s the key… Grails.

With this environment I have been able to focus on building my application, not plumbing and other mundane details. I have an almost instant turn-around on changes to domain and controller classes, as well as a simple yet flexible syntax that allows for less IDE overhead, hence the text editor. Heck, most of my actual coding has been the HTML and JavaScript for the front-end; I have modified a few scaffold controller actions, and added a few of my own, which have all been simple and straightforward. It truly is a pleasant environment to work with.

Groovy itself is quite nice, providing a much more rich syntax while still maintaining total support of Java and Java syntax. One of the features of Grails I have always appreciated is that you always have Java to fall back on if you need a bit more performance, or access to something that cannot be accessed through Groovy itself. The two languages really coexist quite nicely.

I have the core JavaDocs pretty much burned into my memory (:-)), but I wonder how less-seasoned developers would find this setup. Without an IDE you don’t get code completion or the other helpful features that help to boost the productivity of more junior developers. It’s not meant to be a hard-core developer test; I am not into that kind of stuff. I am all about making development easier… and this just feels right for Grails (and Groovy).

Java web application development has gotten too hard somewhere along the way.

Popularity: 1% [?]

  • Share/Bookmark

Tags: , , , ,

…Try Try Again

After yesterday’s post, If At First You Don’t Succeed…, I thought of a slightly different approach to the retry execution method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public <T> T execute( final Retriable<T> op ) throws Exception {
    for(int r=0; r<maxRetries; r++){
        try {
            return op.execute();

        } catch(final Exception e) {
            if( ArrayUtils.contains(catchAndThrow, e.getClass()) ) throw e;

            if(log.isWarnEnabled()){
                log.warn("RetryCaughtException[" + r + "]: " + e.getMessage());
            }
        }  
    }

    if(log.isWarnEnabled()) {
        log.warn("RetriesFailed: " + op.getClass().getName());
    }    

    throw new MaxRetriesExceededException(maxRetries,op.getClass().getName());
}

This approach does not rely on the boolean to end the loop. If an exception is thrown it will try again until a return value is returned or the retry count has been exceeded. With this approach you only fall out of the loop on a retry exceeded condition, hence the logging and the thrown exception. This feels a little cleaner than the other approach.

I have not run it through the unit testing so, you might do that first if you intend to use this code.

Popularity: 2% [?]

  • Share/Bookmark

Tags: , , ,

If At First You Don’t Succeed…

Sometimes you run into an operation that is a little on the flaky side and might sometimes fail on one execution but then work fine on the next; apart from fixing the underlying issue which may not be within your control, you can implement an operation retry strategy. With an operation retry strategy you attempt to execute an operation and then retry it if it fails. After a specified number of attempts (or under certain exceptions) you can allow the operation to fail gracefully. This gives you greater isolation of the questionable service while also allowing you more control what happens on a failure.

I have run into this issue a few years back with a twitchy SMTP server, and now again with a slightly-flaky database connection pool. Last time I wrote a very code-specific retry strategy (and like a doof, never blogged about it), but this time something more reusable is in order since this component would be used extensively throughout the code.

Basically the requirement is the ability to run a repeatable operation some number of times until it either succeeds or exceeds a specified number of attempts, at which point it will stop trying and fail. Since there are no closures in Java yet, I came up with a Retriable interface with a single method which will execute the operation and return it’s return value if there is one.

1
2
3
public interface Retriable<T> {
    public T execute() throws Exception;
}

I had originally considered using java.lang.Runnable; however, a return value simplifies cases where you are trying to extract some value from the operation. Similarly I considered the org.apache.commons.lang.Closure class and discarded if for the same reason.

The retry logic itself is pretty straight-forward. The failure condition is based on exceptions thrown by the operation execution. If an execption is thrown that is not contained in the “catchAndThrow” list, the counter will be incremented and the operation will be tried again if the max number of tries has not been exceeded. The “catchAndThrow” list is an array of Exception classes which if caught are to be thrown out of the handler rather than initiating a retry. This allows some desired exceptions to be handled by the calling method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public <T> T execute( final Retriable<T> op ) throws Exception {
    boolean retry = true;
    int count = 0;
    do {
        try {
            return op.execute();

        } catch(final Exception e) {
            if( ArrayUtils.contains(catchAndThrow, e.getClass()) ) throw e;

            retry = ++count < maxRetries;
            if(log.isWarnEnabled() && !retry) {
                log.warn("RetriesFailed[" + op.getClass().getName() + "]: " + e.getMessage(), e);
            }
        }
    } while( retry );

    throw new MaxRetriesExceededException(maxRetries,op.getClass().getName());
}

You will notice that if all the retries fail, a MaxRetriesExceededException is thrown. This allows calling methods to catch and handle the case when total failure occurs in a method-specific manner.

A more advanced retry strategy could also be derived from this where the failure condition is based on an injected object (ala strategy pattern) so that other criteria could be used to determine success and failure. The exception catch and throw filter could also be enhanced in this manner.

The retry object itself, which I have called the Retrier is a reusable thread-safe POJO so it can be configured via dependency injection (Spring) and used for any number of Retriable operations.

1
2
3
4
5
6
7
8
<bean id="retrier" class="retry.Retrier">
    <property name="maxRetries" value="3" />
    <property name="catchAndThrow">
        <list>
            <value>java.lang.NullPointerException</value>
        </list>
    </property>
</bean>

What you end up with is a very clean way to perform retiable logic:

1
2
3
4
5
6
7
8
final long id = someBeanId;
final String criteria = searchCriteria;

SomeBean resultBean = retrier.execute( new Retriable<SomeBean>(){
    public SomeBean execute() throws Exception {
        return searchDao.findBean(id,criteria);
    }
} );

In this sample code a searchDao is being called in a retriable manner using parameters from the calling method. As long as parameters are final they can be passed into the anonymous inner class craeted by the inline creation of the Retriable. You will also see that with the use of generics you get a seamless integration of the retiable operation into your code.

Currently, I do not have this code in any of my projects, though it would be a good addition to my CodePerks-Lang project (which is as of now, still unreleased). I have included a downloadable zip file containing the source code (including unit test) for this example.

Popularity: 3% [?]

  • Share/Bookmark

Tags: , , ,

Abominable Nested Collections

The dreaded nested collection, one of the most vile creatures in the lazy-development arsenal. What’s wrong with nested collections? Nothing, if they are required to solve a problem… everything if they are used as the lazy-man’s domain object or data transfer object. Consider this case where you have AddressBeans being mapped to their zip code and then again to their state:

1
Map<String,Map<String,AddressBean>> map = getAddresses();

or worse yet, back in the pre-Java 5 days:

1
Map map = getAddresses();

How meaningless is that? In the second case you will probably stumble over that Map a few times before you even figure out what’s in it, or you will have to trace down into the code and see what’s being put into it.

Code should be straight-forward and expressive. When you find yourself writing a Collection of Collections (or arrays or maps), step back and look at the larger picture. Do you really need such a complex construct? If so, does it really need to be so general that the caller must figure out it’s meaning?

Generally the answer is no. This example could easily be replaced with a wrapper object, say AddressGroup that may internally still use the nested collection, or some other storage mechanism that the caller really does not care about as long as the data is accessible. This wrapper object would also provided a richer less error-prone means of accessing the data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class AddressGroup {

    private Map<String,Map<String,AddressBean>> addresses;
   
    public AddressBean[] getAddressesForState(String state){
        // return the addresses for the selected state
    }
   
    public AddressBean[] getAddressesForZip(String zip){
        // return the addresses for the selected zip
    }
   
    Iterator<AddressBean> addresses(){
        // return an iterator over all addresses
    }
   
    // other accessors as needed
}

With this wrapper object, we now have a much more meaningful statement:

1
AddressGroup addressGroup = getAddresses();

The point here is that there is nothing stopping you from writing your own “collection objects” that can be used to abstract away some of the nastiness of complex raw data structures. Doing this reduces the complexity of your code while at the same time making it easier to read and understand.

Popularity: 1% [?]

  • Share/Bookmark

Tags: , , ,

Looking into PHP

I have started to look into PHP a bit. I have a general working-knowledge of it’s language, which seems to be the point at which most “developers” start to call themselves PHP developers or programmers. I have nothing against PHP; however, I have seen a lot of good money spent on bad PHP code and coders that have no idea how to write good software. It’s not the language, it’s the coders. But, I digress.

So, trying not to join that group, I decided to find out what “best practices”, unit testing functionality and other standards exist around PHP. I came across some helpful postings.

I am happy to see that there is unit testing support, and that is seems to be very standard xUnit-style; there even seems to be some support for mock objects.

There are quite a few web development frameworks for PHP, which is good; I will have to dig up a handful of them and see what they provide.

There is also decent IDE support, including an Eclipse plug-in (PDT).

Not sure where I will go from here, maybe put together a working project to try things out in a few real-world situations.

Popularity: 2% [?]

  • Share/Bookmark

Tags: , ,

Development Environment

One of the tasks I have been given at work is to help modernize the “old school” organization and implement more standardized Java development practices; it has gotten me thinking a lot about what makes a good development procedure/environment.

The list I came up with, should be very familiar to most developers, and though they are most often considered part of “agile” development, they are in my opinion, just good development practices that go beyond any specific development methodology:

  • Coding to interfaces
  • Unit testing
  • Decoupled tools
  • Automated build (with command line ability)
  • Good source control
  • Continuous integration
  • Good bug tracking

Coding to Interfaces

Write your services and DAOs based on interfaces rather than concrete classes. This allows you to test code in a more decoupled manner and allows you greater implementation flexability. It also makes team development more manageable, since you can agree on some interfaces early on in the project and then code to them separately withough the need to wait for the missing pieces.

Unit Testing

Again… how do you know that your code works without proof. Unit testing in some automated, reproducable format is crucial to providing confidence that your code does what it is supposed to do. Into this category I also throw code coverage, which gives you some clues as to how thoroughly you have tested your code.

Decoupled Tools

Your tools should work for you, not against you. Your IDEs, servers, build tools, etc should not be tied together in any way. Developers should be able to use whatever IDE/tools (other than servers) that they are familiar with.

Automated Build

Your project(s) should be buildable from the command line in a repeatable manner with little effort. Tools like Ant and Maven provide rich environments for build production, while still allowing good integration with most IDEs. This goes along somewhat with the previous point about decoupled tools. There is no reason why a developer should not be able to easity edit a file with a plain text editor, run the tests and produce build from the command line with no less confidence than working in a full-featured IDE.

Source Control

Whether it’s a personal project, a prototype, or a project for work, it should have some sort of good source control setup for it. There are tons of free options (git, svn, bazar, mercurial, cvs) and even some good commercial options (perforce), each with varrying levels of IDE integration and ease of use. A good source control system should provide good IDE integration as well as good command line usability.

Continuous Integration

If you have more than one person committing code to a project you will need a continuous integration server (CruiseControl, Hudson, Continuum) setup to do automated builds at least once a day. Having a build kicked off after a checkin is made is also very helpful.

CI builds provide an addition level of assurance that the project compiles and is testable on something other than the developers machine. It also provides help with larger teams in keeping the repository clean from bad code commits. A failing build becomes a red flag that should be fixed right away.

Bug Tracking

A good means of logging and managing bug tickets is very important to keeping track of what has been done as well as what needs to be done. Don’t use an in-house spreadsheed, access database or some other bogus bug tracker. Intall one of the many free options (bugzilla, trac, redmine, mantis) or one of the commercial options (fogbugz). A good tracker should provide a number of access points so that developer tools can also integrate with the tracker and provide a more rich interaction experience.

I am sure that most of this is nothing new, but I wanted to get it all down in one place for future reference. Let me know if I have missed or over/under-stated anything.

Popularity: 2% [?]

  • Share/Bookmark

Tags: , ,

Finding Duplicate Ints: Clojure

Finding Duplicate Ints: Clojure

I return to my common interview question (see Interview Question: Find 2 Matching Ints) with a solution based on Clojure.

I worked on the solution to this problem in a few steps, which I will share. First, I assumed that the list of numbers has been sorted:

1
2
3
4
5
6
7
8
(defn find-dupint [intseq]
    (loop [result nil its intseq]
        (if (= result (peek its))
            result
            (recur (peek its) (pop its))
        )
    )
)

This function simmply iterates over the list of numbers and returns the first number whose value matches the next number in the sequence. This code will fail if the numers are not in order and the problem definition says that they can be in any order, so I worked up the following code to sort the numbers before looping through them:

1
2
3
4
5
6
7
8
9
10
(defn find-dupint [intseq]
    (let [sorint (sort intseq)]
        (loop [result nil its sorint]
            (if (= result (first its))
                result
                (recur (first its) (rest its))
            )
        )
    )
)

Notice that I changed (peek coll) and (pop coll) to (first coll) and (rest coll), which work on sequences rather than stacks. I kept getting a ClassCastException the other way.

Now all that’s left is to have an exception thrown when no duplicated number is found. The function as written returns a nil, which is ok for Clojure I guess but not really the same behavior as the other versions of this code as it is defined, so I dipped into the Java interoperability features to add some exception throwing for the finished product:

1
2
3
4
5
6
7
8
9
10
11
12
13
(defn find-dupint [intseq]
    (let [sorint (sort intseq)]
        (loop [result nil its sorint]
            (if (= result (first its))
                (if (nil? result)
                    (throw (new IllegalArgumentException "No duplicate found!"))
                    result
                )
                (recur (first its) (rest its))
            )
        )
  )
)

Note: the IllegalArgumentException does not need to be fully qualified because the java.lang package is imported by default, just as it is in Java.

The output from using this function is shown below:

1:1 user=> (find-dupint [1 2 3])
java.lang.IllegalArgumentException: No duplicate found! (repl-1:1)

1:2 user=> (find-dupint [1 2 3 2])
2

An intesting side-effect of this version is that you are not constrained to use ints, or even numbers for that matter. You could actually use any Comparable object. (It would not be that difficult to add the ability to really generalize the function by passing in a comparator)

A few other comparable objects are tested below:

1:1 user=> (find-dupint ["a" "c" "b" "a"])
"a"

1:2 user=> (find-dupint [1.1 1.4 1.3 1.22 1.1])
1.1

I guess I should rename the function “find-dup” to reflect the broader scope than just integers.

At this point, reviewing the code, it’s not all that less verbose than the Java version, but it gets the job done. I will have to come back to this problem again as I get more experience with Clojure… I am willing to bet there is a more concise way to achieve the same results.

Popularity: 2% [?]

  • Share/Bookmark

Tags: , , ,

Herding Jackrabbits

I have been playing with Jackrabbit, Apache’s Java Content Repository (JCR 1.0) (see also JSR-170) implementation. So far, the JCR is a very interesting and seemingly under-utilized API, but I for the purpose of this discussion I am going to assume that you already have a bare minimum of understanding of Jackrabbit and the JCR. The documentation is a little thin in some parts so I decided to share some of my experimentation with NodeType registry and SQL searching of Nodes.

The registration of custom NodeTypes is apparently not part of the 1.0 spec, since the only implementation for registering custom node types seems to be in a Jackrabbit-specific class, though it does have a note about changes in JCR 2.0 moving the functionality into the core interface, which is nice. The basic code needed to load and register node types is as follows:

1
2
final NodeTypeManagerImpl manager = (NodeTypeManagerImpl)session.getWorkspace().getNodeTypeManager();
manager.registerNodeTypes(new FileInputStream(cndFile), NodeTypeManagerImpl.TEXT_X_JCR_CND, true);

The registerNodeTypes(...) boolean parameter being true forces a reload of the node types even if they have already been installed. You will notice that the node types are contained in a separate “cnd” file (see Node Type Definition). The file I used for my test was as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
<baggage = 'http://baggage.sourceforge.net/baggage'>

[baggage:Note]

> nt:base

- baggage:text (string) = ''

primary

mandatory autocreated

version

I am not sure at this point whether or not this is a grammar that they created themselves or if it is some external standard. It seems a bit clunky, but it gets the job done. Basically I am creating a namespace called “baggage” and adding a new node type to it called “baggage:Note” which will have one auto-created required property called “baggage:text”.

It seems from some other tests I have done that you really need your own node types if you are going to do any querying of your content. You can use the default types but it could get really cluttered and cause your searches to slow down (Note: it uses Lucene internally for indexing and searching).

Once the new node types have been registered we can use them simply by creating nodes with them:

1
2
3
4
5
6
7
8
final Node root = session.getRootNode();
final Node notesRoot = root.addNode("notes-root");

final Node node1 = notesRoot.addNode("Something Interesting","baggage:Note");
node1.setProperty("baggage:text","This is some note that I would write.");

final Node node2 = notesRoot.addNode("Another Note","baggage:Note");
node2.setProperty("baggage:text","More really cool text content.");

which creates a “notes-root” node off of the main root node and then adds some “baggage:Note” nodes to it.

Once you have a few nodes with content it might be nice to search through them. The JCR gives you a fairly straight-forward query functionality using either XPath or SQL. I chose SQL for my test. Let’s find all the “baggage:Note” nodes that contain the text ‘cool’. The following code shows how this is done:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
final QueryManager queryManager = session.getWorkspace().getQueryManager();

final String sql = "select * from baggage:Note where contains(baggage:text,'cool')";
final Query xq = queryManager.createQuery(sql, Query.SQL);
final QueryResult result = xq.execute();

final RowIterator rows = result.getRows();
log.info("Found: " + rows.getSize());
while(rows.hasNext()){
    final Row row = rows.nextRow();
    for(final String col : result.getColumnNames()){
        log.info(col + " = " + row.getValue(col).getString());
    }
    log.info("---");
}

Using my sample nodes, it will find one matching node and send the following output to the log appender:

556  [main] INFO  net.sourceforge.baggage.jcrex.JcrExp2  - Found: 1
560  [main] INFO  net.sourceforge.baggage.jcrex.JcrExp2  - baggage:text = More really cool text content.
560  [main] INFO  net.sourceforge.baggage.jcrex.JcrExp2  - jcr:primaryType = baggage:Note
563  [main] INFO  net.sourceforge.baggage.jcrex.JcrExp2  - jcr:path = /notes-root/Another Note
564  [main] INFO  net.sourceforge.baggage.jcrex.JcrExp2  - jcr:score = 5035
564  [main] INFO  net.sourceforge.baggage.jcrex.JcrExp2  - ---

The code for the whole test class is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class JcrExp2 {

    private static final Logger log = LoggerFactory.getLogger(JcrExp2.class);

    public static void main(final String[] args) throws Exception {
        final Repository repository = new TransientRepository();
        final Session session = repository.login(new SimpleCredentials("username", "password".toCharArray()));

        final String cndFile = "baggage.cnd";

        final NodeTypeManagerImpl manager = (NodeTypeManagerImpl)session.getWorkspace().getNodeTypeManager();
        manager.registerNodeTypes(new FileInputStream(cndFile), NodeTypeManagerImpl.TEXT_X_JCR_CND, true);
        log.info("Registered Node types");

        try {
            final Node root = session.getRootNode();

            final Node notesRoot = root.addNode("notes-root");
            log.info("Added NotesRoot: " + notesRoot);

            final Node node1 = notesRoot.addNode("Something Interesting","baggage:Note");
            node1.setProperty("baggage:text","This is some note that I would write.");

            final Node node2 = notesRoot.addNode("Another Note","baggage:Note");
            node2.setProperty("baggage:text","More really cool text content.");

            session.save();

            // query

            final QueryManager queryManager = session.getWorkspace().getQueryManager();

            //          final String sql = "select * from baggage:Note";
            final String sql = "select * from baggage:Note where contains(baggage:text,'cool')";
            final Query xq = queryManager.createQuery(sql, Query.SQL);
            final QueryResult result = xq.execute();

            final RowIterator rows = result.getRows();
            log.info("Found: " + rows.getSize());
            while(rows.hasNext()){
                final Row row = rows.nextRow();
                for(final String col : result.getColumnNames()){
                    log.info(col + " = " + row.getValue(col).getString());
                }
                log.info("---");
            }

            // cleanup

            notesRoot.remove();
            session.save();

        } catch(final Exception e){
            log.error("Something bad has happened! " + e.getMessage(),e);

        } finally {
            session.logout();
        }
    }
}

You will need the JCR 1.0 jar as well as the jackrabbit 1.6 jar, both of which can be downloaded from the Jackrabbit downloads page.

The JCR seems pretty useful and flexible. I am surprised that it is not used more than it seems to be.

Popularity: 1% [?]

  • Share/Bookmark

Tags: , , ,

Switch to our mobile site