jackrabbit-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Jukka Zitting" <jukka.zitt...@gmail.com>
Subject Re: How to start thinking in JCR
Date Thu, 29 Mar 2007 06:29:35 GMT
Hi,

On 3/29/07, Nandana Mihindukulasooriya <nandana.cse@gmail.com> wrote:
> I should thank Brian, Xin and Jukka a lot for their valuable feedbacks
> and I was able to understand lot of things when I went through the
> JSR-170 again after reading the feedbacks.

Excellent, thanks again for your efforts. Comments below...

> Now we will create users using their unique user IDs like
>
> Node root = session.getRootNode();
> Node user = root.addNode("uniqueUserID","blog:user");
>
> To avoid uniqueUserID conflit with other root's child nodes ( which are not
> user nodes), can we use a namespace prefix as "id:uniqueID" with <id =
> http://... >.

Namespaces are a good way to avoid naming conflicts, but in this case
an even better way would be to create your own "application root node"
like /blog:root like the one you propose below. Such a root node
nicely separates the blog content from any other application you may
want to store in the same workspace. It also makes it easy to limit
searches to just that subtree instead of including for example the
whole /jcr:system tree.

Also, I would encourage you to use a username or even the real name of
the user as the node name instead of a numeric or some other abstract
identifier.

Thus the code would be:

    Node root = session.getRootNode().getNode("blog:root");
    Node user = root.addNode(username,"blog:user");

> can we add something like this to root node to avoid non-unique userIDs ?
>
>     ChildNodeDefinition
>     Name *
>     RequiredPrimaryTypes UNDEFINED
>     ...
>     SameNameSiblings false

The root node definition in Jackrabbit is essentially equivalent to
nt:unstructured, so you can already add whatever child nodes you want
to it without modifying the type definition.

> If we can, how can we define it ? and is ItemExistsException throwed
> immediately or on save in jackrabbit implementation of jcr ?
> If we can't is there any other way to prevent it ?

Node types can be registered using the CND or XML node type definition
formats and the JackrabbitNodeTypeManager extension interface found in
the jackrabbit-api component.

Generally you shouldn't rely on an JCR implementation to perform
consistency checks before the save() call. There are even many full
consistency checks that can logically not be performed before the
save() call.

> or can I make a one root note child like this and define it not to have
> sameNameSiblings.
>
> [blog:blog]
>     - * [blog:user]
>     - library [nt:folder] mandatory autocreated

Such an approach would be my preferred alternative. You can't easily
constraint the repository root node not to have children with same
names, but you can quite easily enforce that in your application.

As to the blog:blog node type, again I would prefer the standard
nt:folder type. I'd also place the image library under a separate
"content root" since it could very well be used by other applications
as well. The content tree would look something like this:

    /blog:blog [nt:folder]
    /blog:blog/...
    /blog:library [nt:folder]
    /blog:library/...

You can initialize your application like this (assuming you've already
set up the namespaces):

    Session session = ...;
    Node root = session.getRootNode();
    if (!root.hasNode("blog:blog")) {
        root.addNode("blog:blog", "nt:folder");
    }
    if (!root.hasNode("blog:library")) {
        root.addNode("blog:library", "nt:folder");
    }
    session.save();

> As blogspace is of type nt:folder which has a child node definition,
>
>     ChildNodeDefinition
>     Name *
>     RequiredPrimaryType[nt:hierarchyNode]
>
> it can in turn have children of type nt:folder as nt:folder is a  subtype of
> nt:hierarchyNode. So both <yyyy> and <mm> would be nodes of type nt:folder.

Exactly.

> So we can use,
>
> Node blogSpace = user.getNode("blogSpace");
> Node year = blogspace.addNode("2007","nt:folder");
> Node month = year.addNode("03","nt:folder");
>
> Is there a way to create a node with intermediate created automatically ?
> If there is a way, how can we declare the types of intermediate nodes ?
> Javadoc says it throws a PathNotFoundException if we try to create a
> node without creating intermediate nodes.

There's no such way, you need to handle that in your application.

    Node blogSpace = ...;
    if (!blogSpace.hasNode("year")) {
        blogSpace.addNode("year", "nt:folder");
        blogSpace.save();
    }
    Node year = blogSpace.getNode("year");
    if (!year.hasNode("month")) {
        year.addNode("month", "nt:folder");
        year.save();
    }
    Node month = year.getNode("month");

Note that the above code has a slight chance of race conditions if two
sessions attempt to create the same intermediate nodes at the same
time. You can either proactively prevent it by making the blogSpace
node lockable and using JCR locks, or you can catch the concurrent
modification exception and recover by retrying the operation. Since
the chance of collisions is so small, I would go with the latter
option.

> So we have to come with a way to name the blog entries. Title may not be a
> good candidate because they may white spacses and '.', '/', ':', '[', ']', '*',
> ''', '"' charactors. Would a simple sequential numbering work ? Is there a better
> way to handle this ?

Generally a somewhat meaningful name is preferred over a sequence
number. It makes administration much easier and also gives a nice
URL-to-path mapping for web applications. I would go for a solution
that either allows the user to specify the node name or uses a
"simplifies" title as the node name. Many existing blog applications
(for example WordPress) already use a "simplification" algorithm that
turns title strings into valid URL path components. A similar solution
would be perfect here as well.

You would still store the full title as a normal string property to
avoid losing data.

> Node blogEntry = month.addNode("01","blog:blogEntry");
> blogEntry.setProperty("content","my first blog entry");
>
> As blogEntry is a subtype of nt:hierarchyNode, I would be able to use
> jcr:created property to get the created date of the blog.

Exactly.

> To add an image attachment,
>
> Node attachments = blogEntry.getNode("attachments");
> Node linkedFile = attachments.addNode("attachment01","nt:linkedFile");
> linkedFile.setProperty("jcr:content",root.getNode("library/xxx/yyy"));
>
> jcr:content property of the  nt:linkedFile  type  is of type reference.

Perfect. Note that the jcr:content reference of an nt:linkedFile node
should probably point to the jcr:content resource node of another file
instead of the file node itself.

> To add a Comment
>
> Node comment = blogEntry.addNode("01","blog:comment");
> comment.setProperty("content","my first comment");
> comment.setProperty("commenter", root.getNode("commenterID"));

Exactly.

> To add a avator to a user,
>
> Node library = root.getNode("library");
> Node manAvatar = library.addNode("manAvator","blog:avatar");
> Node content = manAvatar.getNode("jcr:content");
> content.setProperty("image",imageInputStream);
>
> user.setProperty("avatar",manAvator);

I think you'd be better of again using the standard nt:hierarchyNode
model here instead of custom types. I would define a "personal
library" folder for each user and place the avatar image there as
either a local nt:file or as a nt:linkedFile that refers to some image
in the global image library. A reserved name would be used for the
avatar image, but other files could also be stored in this personal
library folder. Using the standard node types allows you for example
to use the existing WebDAV servlet classes from jackrabbit-jcr-server
to serve the content in a web application. As an extra bonus you'd
even get PUT support for free for remote clients to update the
content!

    [blog:user]
    ....
    + library (nt:folder) mandatory autocreated

Adding a custom avatar would be:

    Node user = ...;
    Node library = user.getNode("library");
    Node avatar = library.addNode("avatar", "nt:file");
    avatar.addNode("jcr:content", "nt:resource");
    avatar.setProperty("jcr:content/jcr:mimeType", "image/gif");
    avatar.setProperty("jcr:content/jcr:lastModified", Calendar.getInstance());
    avatar.setProperty("jcr:content/jcr:data", ...);
    library.save();

Adding a standard avatar from the global image library would be:

    Node standardAvatar = ...;
    Node user = ...;
    Node library = user.getNode("library");
    Node avatar = library.addNode("avatar", "nt:linkedFile");
    avatar.setProperty("jcr:content", standardAvatar.getNode("jcr:content"));
    library.save();

> jcr:content is of type nt:base and how can we define that it has a property
> "image" which contains Binary data. Or shall I directly add "image" property
> to blog:avator type like
>
> [blog:avatar] > nt:file,mix:referenceable
> - image (binary) mandatory primary

No. You should instead use the standard nt:resource type as the
jcr:content node of a file and store the image data as
jcr:content/jcr:data as shown above.

If you want to, you can extend the nt:resource type to contain
image-specific information like size and possible thumbnails:

    [blog:image] > nt:resource
    - width (long)
    - height (long)
    + thumbnails (nt:folder)

BR,

Jukka Zitting

Mime
View raw message