You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@jackrabbit.apache.org by loproman <lo...@gmail.com> on 2007/11/07 11:38:44 UTC

Re: Jackrabbit Best Practices/Design Patterns (an attempt)

I thought I would augment my question with a (simplified) example of what I'm
trying to do. Starting with a very basic approach:

Node root = session.getRootNode();
Node album = root.getNode("MyAlbum");
Node songs = album.getNode("songs");
Node song = songs.addNode("MySong");
song.setProperty("genre", "funky");
session.save();

I've expanded this into a richer object model:

public class Album{

	public static void main(){
	
		Session session = null;
	
		try{
			session = Repository.login();
			Node root = session.getRootNode();
			
			Album myAlbum = new Album(root.getNode("MyAlbum"));
			Song mySong = album.addSong("MySong");
			mySong.setGenre("funky");
			myAlbum.save();
		}
		catch(Exception e){
			System.err.println(e.getMessage());
		}
		finally{
			if(session != null)
				session.logout();
		}
	}
}


class Album{

	private Node _album;
	
	public Album(Node album){
		_album = album;
	}
	
	public String getName(){
		Property name = _album.getProperty("name");
		return name.getString();
	}
	
	public Song addSong(String name){
		Node songs = _album.getNode("songs");
		Node song = songs.addNode(name);
		return new Song(song);
	}
	
	public void save(){
		_album.save();
	}
}

class Song{
	private Node _song;
	
	public Song(Node song){
		_song = song;
	}
	
	public void setGenre(String genre){
		_song.setProperty("genre", genre);
	}
}

What are your thoughts? Am I doing anything that might cause issues as
things get more complex? I'm new to the concept of JCR, so I'm very
interested in learning how I can use it in my code as naturally as possible. 

Personally, I feel the "addSong" method is a little awkward, but I don't
really see another way to do it. Also, how long should sessions live? With
relational databases, best practice is to open and close the connection as
quickly as possible. However, it seems like JCR sessions can/should stay
open much longer. Any thoughts/tips/examples are very much appreciated!



loproman wrote:
> 
> Hi,
> 
> Does anyone know if there has been any research or documentation developed
> on best practices working with repositories?  Specifically, I'm wondering
> if there are any ideal patterns out there for working with the JCR API.
> I've seen lots of primitive examples where nodes are accessed directly via
> "getNode" and "addNode" etc. However, I think a more scalable solution
> would need to wrap nodes with domain logic (similar to how DTO objects are
> used with a relational database I suspect). Thanks!
> 

-- 
View this message in context: http://www.nabble.com/Jackrabbit-Best-Practices-Design-Patterns-tf4762615.html#a13624765
Sent from the Jackrabbit - Users mailing list archive at Nabble.com.


Re: Jackrabbit Best Practices/Design Patterns (an attempt)

Posted by Sandro Böhme <sa...@gmx.de>.
Hi,

thats a good point. An other one is, that one might like to add 
properties / methods that don't need to be persisted. I personally
would put those things in domain classes like Song and Album.


loproman schrieb:
> Hi Jukka,
> 
> I appreciate the input. You make a good point about the session lifespan in
> contrast with desktop and web application paradigms. On my class example, if
> I might defend my thinking a bit it seems like in larger systems, the
> loosely typed nature of JCR objects (in particular Node and Property) could
> become a problem. Unless these are wrapped with objects, I could imagine
> there being many references to the same node in many places. 
> 
> For example, I need to change the underlying type of a property "myprop"
>>from Long to Double. If not wrapped, I'd have to hunt down cases of the
> following throughout my application. 
> 
> Property p = node.getProperty("myprop");
> Long l = p.getLong();
> 
> Unfortunately, this may not be written the same way every time, so it's
> likely I miss a few instances and introduce bugs. With a wrapper around a
> node, I'd change it in once place and the compiler would enforce the change
> throughout the application for me based upon strongly typed references.
> 
> Aside from being strongly typed, I feel like it presents opportunities to
> build in domain logic on top of my nodes. My example probably did a crummy
> job of illustrating additional benefits, but one idea might be to have Album
> implement a Comparable interface. Or maybe I might want to pass an album
> into a RecordPlayer object. It just seems more OO friendly to me, although I
> will concede it's going to be more work.
> 
> 
> 
> Jukka Zitting wrote:
>> Hi,
>>
>> On Nov 7, 2007 12:38 PM, loproman <lo...@gmail.com> wrote:
>>> What are your thoughts? Am I doing anything that might cause issues as
>>> things get more complex? I'm new to the concept of JCR, so I'm very
>>> interested in learning how I can use it in my code as naturally as
>>> possible.
>> I wouldn't use your Album and Song classes as they are now, as their
>> methods are essentially just wrappers around equivalent JCR methods.
>> Such a data access layer is more useful for JDBC, where a method like
>> Album.getName() could become:
>>
>>        public String getName() throws SQLException {
>>            PreparedStatement ps = connection.prepareStatement(
>>                "SELECT name FROM albums WHERE albumid=?");
>>            try {
>>                ps.setString(1, albumid);
>>                ResultSet rs = ps.executeQuery();
>>                try {
>>                    if (rs.next()) {
>>                        return rs.getString(1);
>>                    } else {
>>                        ... // handle error
>>                    }
>>                } finally {
>>                    rs.close();
>>                }
>>            } finally {
>>                ps.close();
>>            }
>>        }
>>
>> No wonder why frameworks like Hibernate are popular...
>>
>>> Also, how long should sessions live? With relational databases, best
>>> practice
>>> is to open and close the connection as quickly as possible. However, it
>>> seems
>>> like JCR sessions can/should stay open much longer.
>> It depends on your application. A standalone client is probably best
>> served with a single JCR session (just like a single JDBC connection
>> would be a good idea), but a webapp serving multiple independent and
>> concurrent requests should probably (unless it wants to leverage the
>> transient space for handling unsaved changes) use a session pool or
>> start separate sessions for each request.
>>
>> BR,
>>
>> Jukka Zitting
>>
>>
> 


Re: Jackrabbit Best Practices/Design Patterns (an attempt)

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On Nov 7, 2007 11:23 PM, loproman <lo...@gmail.com> wrote:
> On my class example, if I might defend my thinking a bit it seems like in
> larger systems, the loosely typed nature of JCR objects (in particular
> Node and Property) could become a problem. Unless these are wrapped
> with objects, I could imagine there being many references to the same
> node in many places.

Sounds like another case of the classical strong/loose typing debate.
You make a good point and I agree that strong typing certainly has
lots of value (I'm still a Java weenie :-), but there's also a
downside of requiring extra code whenever you want to introduce a new
field or some new access pattern. A fixed data access layer can easily
become a bottleneck for many features like search, versioning,
unstructured content, etc. There's no single correct way to solve that
tradeoff for all people and projects.

> Aside from being strongly typed, I feel like it presents opportunities to
> build in domain logic on top of my nodes.

Agreed, but then you're already moving from a pure data layer closer
to the logic or control layer. In fact I believe that for many MVC
applications you can use JCR as-is as the model, i.e. have both the
view and controller parts use the JCR API directly without a separate
layer on top of it.

PS. There's been some related debate on the mailing list of the
incubating Sling project.

BR,

Jukka Zitting

Re: Jackrabbit Best Practices/Design Patterns (an attempt)

Posted by loproman <lo...@gmail.com>.
Hi Jukka,

I appreciate the input. You make a good point about the session lifespan in
contrast with desktop and web application paradigms. On my class example, if
I might defend my thinking a bit it seems like in larger systems, the
loosely typed nature of JCR objects (in particular Node and Property) could
become a problem. Unless these are wrapped with objects, I could imagine
there being many references to the same node in many places. 

For example, I need to change the underlying type of a property "myprop"
from Long to Double. If not wrapped, I'd have to hunt down cases of the
following throughout my application. 

Property p = node.getProperty("myprop");
Long l = p.getLong();

Unfortunately, this may not be written the same way every time, so it's
likely I miss a few instances and introduce bugs. With a wrapper around a
node, I'd change it in once place and the compiler would enforce the change
throughout the application for me based upon strongly typed references.

Aside from being strongly typed, I feel like it presents opportunities to
build in domain logic on top of my nodes. My example probably did a crummy
job of illustrating additional benefits, but one idea might be to have Album
implement a Comparable interface. Or maybe I might want to pass an album
into a RecordPlayer object. It just seems more OO friendly to me, although I
will concede it's going to be more work.



Jukka Zitting wrote:
> 
> Hi,
> 
> On Nov 7, 2007 12:38 PM, loproman <lo...@gmail.com> wrote:
>> What are your thoughts? Am I doing anything that might cause issues as
>> things get more complex? I'm new to the concept of JCR, so I'm very
>> interested in learning how I can use it in my code as naturally as
>> possible.
> 
> I wouldn't use your Album and Song classes as they are now, as their
> methods are essentially just wrappers around equivalent JCR methods.
> Such a data access layer is more useful for JDBC, where a method like
> Album.getName() could become:
> 
>        public String getName() throws SQLException {
>            PreparedStatement ps = connection.prepareStatement(
>                "SELECT name FROM albums WHERE albumid=?");
>            try {
>                ps.setString(1, albumid);
>                ResultSet rs = ps.executeQuery();
>                try {
>                    if (rs.next()) {
>                        return rs.getString(1);
>                    } else {
>                        ... // handle error
>                    }
>                } finally {
>                    rs.close();
>                }
>            } finally {
>                ps.close();
>            }
>        }
> 
> No wonder why frameworks like Hibernate are popular...
> 
>> Also, how long should sessions live? With relational databases, best
>> practice
>> is to open and close the connection as quickly as possible. However, it
>> seems
>> like JCR sessions can/should stay open much longer.
> 
> It depends on your application. A standalone client is probably best
> served with a single JCR session (just like a single JDBC connection
> would be a good idea), but a webapp serving multiple independent and
> concurrent requests should probably (unless it wants to leverage the
> transient space for handling unsaved changes) use a session pool or
> start separate sessions for each request.
> 
> BR,
> 
> Jukka Zitting
> 
> 

-- 
View this message in context: http://www.nabble.com/Jackrabbit-Best-Practices-Design-Patterns-tf4762615.html#a13636339
Sent from the Jackrabbit - Users mailing list archive at Nabble.com.


Re: Jackrabbit Best Practices/Design Patterns (an attempt)

Posted by Sandro Böhme <sa...@gmx.de>.
Jukka Zitting schrieb:
> Hi,
> 
> On Nov 9, 2007 10:57 PM, Sandro Böhme <sa...@gmx.de> wrote:
>> Especially in this case a simple
>> NodeIterator "node.getNodesByType(String nodeTypeName)"
>> would be very handy. But as it is not available I would personally
>> favor wrappers for that.
>>
>> Hoping that expert group folks are reading this:
>> I'm just curious, was this usecase already considered by the expert
>> groups and discarded for some reason?
> 
> I wasn't around for JSR 170, so I can't say.
> 
> Personally I tend to prefer using paths for node selection (for
> example have all "songs" as <album>/songs/<song> nodes) and node types
> only for deciding what to do with an already selected node. In my
> experience such late binding works wonders for modularity and
> flexibility.
> 
> Such a design would for example allow you to make no distinction
> between a custom mymusic:Song node and a standard nt:file MP3 node
> uploaded through the standard WebDAV servlet. As long as your
> application  has handlers for both mymusic:Song and nt:file, it can
> simply list everything within ./songs/*. In fact the application
> doesn't even need to have such handlers, but the code that lists the
> songs will still work fine.
> 
> BR,
> 
> Jukka Zitting
> 
Hi Jukka,

this solution is interesting. It clearly shows that one could also solve 
this problem using loose typing and the available API.
Additionally I think it would just be cool if strong typing would also 
be supported with a method along the lines of getNodesByType(nodeTypeName).

Bye,

Sandro

Re: Jackrabbit Best Practices/Design Patterns (an attempt)

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On Nov 9, 2007 10:57 PM, Sandro Böhme <sa...@gmx.de> wrote:
> Especially in this case a simple
> NodeIterator "node.getNodesByType(String nodeTypeName)"
> would be very handy. But as it is not available I would personally
> favor wrappers for that.
>
> Hoping that expert group folks are reading this:
> I'm just curious, was this usecase already considered by the expert
> groups and discarded for some reason?

I wasn't around for JSR 170, so I can't say.

Personally I tend to prefer using paths for node selection (for
example have all "songs" as <album>/songs/<song> nodes) and node types
only for deciding what to do with an already selected node. In my
experience such late binding works wonders for modularity and
flexibility.

Such a design would for example allow you to make no distinction
between a custom mymusic:Song node and a standard nt:file MP3 node
uploaded through the standard WebDAV servlet. As long as your
application  has handlers for both mymusic:Song and nt:file, it can
simply list everything within ./songs/*. In fact the application
doesn't even need to have such handlers, but the code that lists the
songs will still work fine.

BR,

Jukka Zitting

Re: Jackrabbit Best Practices/Design Patterns (an attempt)

Posted by Sandro Böhme <sa...@gmx.de>.
Hi Jukka,

this is an interesting discussion. In general I think you are right that 
JCR is much easier to use than JDBC. But e.g. if one has more than one 
list (like the album having a list of songs and a list of song texts) 
under a node than one has to distinguish the childnodes by
their type or an other criterion (like a node name pattern). In this
case the things are getting somewhat more verbose like this:

public NodeIterator getSongs() throws RepositoryException {
       QueryManager queryManager =
             node.getSession().getWorkspace().getQueryManager();
       String path = node.getPath();
       String searchString = path+"/element(*,"mymusic:Song" )";
       Query query = queryManager.createQuery(searchString, Query.XPATH);
       QueryResult result = query.execute();
       return result.getNodes();
}

public NodeIterator getSongTexts() throws RepositoryException {
       QueryManager queryManager =
             node.getSession().getWorkspace().getQueryManager();
       String path = node.getPath();
       String searchString = path+"/element(*,"mymusic:SongText" )";
       Query query = queryManager.createQuery(searchString, Query.XPATH);
       QueryResult result = query.execute();
       return result.getNodes();
}

Especially in this case a simple
NodeIterator "node.getNodesByType(String nodeTypeName)"
would be very handy. But as it is not available I would personally
favor wrappers for that.

Hoping that expert group folks are reading this:
I'm just curious, was this usecase already considered by the expert
groups and discarded for some reason?

Bye,

Sandro


Jukka Zitting schrieb:
> Hi,
> 
> On Nov 7, 2007 12:38 PM, loproman <lo...@gmail.com> wrote:
>> What are your thoughts? Am I doing anything that might cause issues as
>> things get more complex? I'm new to the concept of JCR, so I'm very
>> interested in learning how I can use it in my code as naturally as possible.
> 
> I wouldn't use your Album and Song classes as they are now, as their
> methods are essentially just wrappers around equivalent JCR methods.
> Such a data access layer is more useful for JDBC, where a method like
> Album.getName() could become:
> 
>        public String getName() throws SQLException {
>            PreparedStatement ps = connection.prepareStatement(
>                "SELECT name FROM albums WHERE albumid=?");
>            try {
>                ps.setString(1, albumid);
>                ResultSet rs = ps.executeQuery();
>                try {
>                    if (rs.next()) {
>                        return rs.getString(1);
>                    } else {
>                        ... // handle error
>                    }
>                } finally {
>                    rs.close();
>                }
>            } finally {
>                ps.close();
>            }
>        }
> 
> No wonder why frameworks like Hibernate are popular...
> 
>> Also, how long should sessions live? With relational databases, best practice
>> is to open and close the connection as quickly as possible. However, it seems
>> like JCR sessions can/should stay open much longer.
> 
> It depends on your application. A standalone client is probably best
> served with a single JCR session (just like a single JDBC connection
> would be a good idea), but a webapp serving multiple independent and
> concurrent requests should probably (unless it wants to leverage the
> transient space for handling unsaved changes) use a session pool or
> start separate sessions for each request.
> 
> BR,
> 
> Jukka Zitting
> 


Re: Jackrabbit Best Practices/Design Patterns (an attempt)

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On Nov 7, 2007 12:38 PM, loproman <lo...@gmail.com> wrote:
> What are your thoughts? Am I doing anything that might cause issues as
> things get more complex? I'm new to the concept of JCR, so I'm very
> interested in learning how I can use it in my code as naturally as possible.

I wouldn't use your Album and Song classes as they are now, as their
methods are essentially just wrappers around equivalent JCR methods.
Such a data access layer is more useful for JDBC, where a method like
Album.getName() could become:

       public String getName() throws SQLException {
           PreparedStatement ps = connection.prepareStatement(
               "SELECT name FROM albums WHERE albumid=?");
           try {
               ps.setString(1, albumid);
               ResultSet rs = ps.executeQuery();
               try {
                   if (rs.next()) {
                       return rs.getString(1);
                   } else {
                       ... // handle error
                   }
               } finally {
                   rs.close();
               }
           } finally {
               ps.close();
           }
       }

No wonder why frameworks like Hibernate are popular...

> Also, how long should sessions live? With relational databases, best practice
> is to open and close the connection as quickly as possible. However, it seems
> like JCR sessions can/should stay open much longer.

It depends on your application. A standalone client is probably best
served with a single JCR session (just like a single JDBC connection
would be a good idea), but a webapp serving multiple independent and
concurrent requests should probably (unless it wants to leverage the
transient space for handling unsaved changes) use a session pool or
start separate sessions for each request.

BR,

Jukka Zitting

RE: Jackrabbit Best Practices/Design Patterns (an attempt)

Posted by loproman <lo...@gmail.com>.
Thanks Ard. Yes, I had seen it, and it was helpful. I very much appreciate
David's attempts to get the ball rolling on it. Unfortunately, it's probably
only the tip of an iceberg and I didn't see anything that helped me with my
particular interests. 


Ard Schrijvers wrote:
> 
> Not sure wether this is too abstract for you, but you might want to take a
> look over here:
> 
> http://wiki.apache.org/jackrabbit/DavidsModel
> 
> Regards Ard
> 
> 
> 
> I thought I would augment my question with a (simplified) example of what
> I'm
> trying to do. Starting with a very basic approach:
> 
> Node root = session.getRootNode();
> Node album = root.getNode("MyAlbum");
> Node songs = album.getNode("songs");
> Node song = songs.addNode("MySong");
> song.setProperty("genre", "funky");
> session.save();
> 
> I've expanded this into a richer object model:
> 
> public class Album{
> 
> 	public static void main(){
> 	
> 		Session session = null;
> 	
> 		try{
> 			session = Repository.login();
> 			Node root = session.getRootNode();
> 			
> 			Album myAlbum = new Album(root.getNode("MyAlbum"));
> 			Song mySong = album.addSong("MySong");
> 			mySong.setGenre("funky");
> 			myAlbum.save();
> 		}
> 		catch(Exception e){
> 			System.err.println(e.getMessage());
> 		}
> 		finally{
> 			if(session != null)
> 				session.logout();
> 		}
> 	}
> }
> 
> 
> class Album{
> 
> 	private Node _album;
> 	
> 	public Album(Node album){
> 		_album = album;
> 	}
> 	
> 	public String getName(){
> 		Property name = _album.getProperty("name");
> 		return name.getString();
> 	}
> 	
> 	public Song addSong(String name){
> 		Node songs = _album.getNode("songs");
> 		Node song = songs.addNode(name);
> 		return new Song(song);
> 	}
> 	
> 	public void save(){
> 		_album.save();
> 	}
> }
> 
> class Song{
> 	private Node _song;
> 	
> 	public Song(Node song){
> 		_song = song;
> 	}
> 	
> 	public void setGenre(String genre){
> 		_song.setProperty("genre", genre);
> 	}
> }
> 
> What are your thoughts? Am I doing anything that might cause issues as
> things get more complex? I'm new to the concept of JCR, so I'm very
> interested in learning how I can use it in my code as naturally as
> possible. 
> 
> Personally, I feel the "addSong" method is a little awkward, but I don't
> really see another way to do it. Also, how long should sessions live? With
> relational databases, best practice is to open and close the connection as
> quickly as possible. However, it seems like JCR sessions can/should stay
> open much longer. Any thoughts/tips/examples are very much appreciated!
> 
> 
> 
> loproman wrote:
>> 
>> Hi,
>> 
>> Does anyone know if there has been any research or documentation
>> developed
>> on best practices working with repositories?  Specifically, I'm wondering
>> if there are any ideal patterns out there for working with the JCR API.
>> I've seen lots of primitive examples where nodes are accessed directly
>> via
>> "getNode" and "addNode" etc. However, I think a more scalable solution
>> would need to wrap nodes with domain logic (similar to how DTO objects
>> are
>> used with a relational database I suspect). Thanks!
>> 
> 
> -- 
> View this message in context:
> http://www.nabble.com/Jackrabbit-Best-Practices-Design-Patterns-tf4762615.html#a13624765
> Sent from the Jackrabbit - Users mailing list archive at Nabble.com.
> 
> 
> 
> 
> 

-- 
View this message in context: http://www.nabble.com/Jackrabbit-Best-Practices-Design-Patterns-tf4762615.html#a13636511
Sent from the Jackrabbit - Users mailing list archive at Nabble.com.


RE: Jackrabbit Best Practices/Design Patterns (an attempt)

Posted by Ard Schrijvers <a....@hippo.nl>.
Not sure wether this is too abstract for you, but you might want to take a look over here:

http://wiki.apache.org/jackrabbit/DavidsModel

Regards Ard



I thought I would augment my question with a (simplified) example of what I'm
trying to do. Starting with a very basic approach:

Node root = session.getRootNode();
Node album = root.getNode("MyAlbum");
Node songs = album.getNode("songs");
Node song = songs.addNode("MySong");
song.setProperty("genre", "funky");
session.save();

I've expanded this into a richer object model:

public class Album{

	public static void main(){
	
		Session session = null;
	
		try{
			session = Repository.login();
			Node root = session.getRootNode();
			
			Album myAlbum = new Album(root.getNode("MyAlbum"));
			Song mySong = album.addSong("MySong");
			mySong.setGenre("funky");
			myAlbum.save();
		}
		catch(Exception e){
			System.err.println(e.getMessage());
		}
		finally{
			if(session != null)
				session.logout();
		}
	}
}


class Album{

	private Node _album;
	
	public Album(Node album){
		_album = album;
	}
	
	public String getName(){
		Property name = _album.getProperty("name");
		return name.getString();
	}
	
	public Song addSong(String name){
		Node songs = _album.getNode("songs");
		Node song = songs.addNode(name);
		return new Song(song);
	}
	
	public void save(){
		_album.save();
	}
}

class Song{
	private Node _song;
	
	public Song(Node song){
		_song = song;
	}
	
	public void setGenre(String genre){
		_song.setProperty("genre", genre);
	}
}

What are your thoughts? Am I doing anything that might cause issues as
things get more complex? I'm new to the concept of JCR, so I'm very
interested in learning how I can use it in my code as naturally as possible. 

Personally, I feel the "addSong" method is a little awkward, but I don't
really see another way to do it. Also, how long should sessions live? With
relational databases, best practice is to open and close the connection as
quickly as possible. However, it seems like JCR sessions can/should stay
open much longer. Any thoughts/tips/examples are very much appreciated!



loproman wrote:
> 
> Hi,
> 
> Does anyone know if there has been any research or documentation developed
> on best practices working with repositories?  Specifically, I'm wondering
> if there are any ideal patterns out there for working with the JCR API.
> I've seen lots of primitive examples where nodes are accessed directly via
> "getNode" and "addNode" etc. However, I think a more scalable solution
> would need to wrap nodes with domain logic (similar to how DTO objects are
> used with a relational database I suspect). Thanks!
> 

-- 
View this message in context: http://www.nabble.com/Jackrabbit-Best-Practices-Design-Patterns-tf4762615.html#a13624765
Sent from the Jackrabbit - Users mailing list archive at Nabble.com.