You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@jackrabbit.apache.org by Nandana Mihindukulasooriya <na...@gmail.com> on 2007/04/03 04:47:43 UTC

Re: How to start thinking in JCR

Hi,
     I changed the Node structure as following according to the discussion
and tried to register them in the repository. Here are some of the problems
I came across.

Grammer for CND notation defines a property of a Node type as

property_def ::= "-" property_name [property_type_decl]
                 [default_values] [attributes]
                 [value_constraints]
property_name ::= string

Does all properties have to have a namespace prefix ? When I tried
to register the node types using the CompactNodeTypeDefReader it
gave an error caused by "UnknownPrefixException".

It also said that if I define the autocreated attribute for a childnode
I should define the default type for them which is of cource reasonable.

Like Node class's hasNode() method is there convinient methods to check
whether a namespace prefix is already registered ? and to check whether
a Node Type is already registed ? I didn't find such methods in
NodeTypeManager or NamespaceRegistry Interfaces of JCR. I used the
NodeTypeManagerImpl which inplements the JackrabbitNodeTypeManager
Interface.

But in the mailing list
// only register the type if it does not yet exist
    if (!manager.hasNodeType("myfile"))
is used to check this. Is that method not available in Jackrabbit 1.0 ?

To create a the repository I used the TransientRepository class which
created
me a default repository configuration file. But if I am using JNDI binding
do I have to create a repository.xml by my self ? Is there a way to specify
to
create a default configuration ?

Where can I find contrib/compact-nt tools ?

I really appreciate your comments. Thank you in advance.

BR,
Nandana


----------------------------------------------------- Node Structure
-------------------------------------------------------

/blogRoot [nt:folder]
/blogRoot/user [blog:user]
/blogRoot/user/blogSpace [nt:folder]
/blogRoot/user/blogSpace/<yyyy> [nt:folder]
/blogRoot/user/blogSpace/<yyyy>/<mm> [nt:folder]
/blogRoot/user/blogSpace/<yyyy>/<mm>/blogEntry [blog:blogEntry]
/blogRoot/user/blogSpace/<yyyy>/<mm>/blogEntry/comment [blog:Comment]
/blogRoot/user/library [nt:folder]
/blogRoot/user/libray/avatar [nt:file]
/<libray> [nt:folder]

-------------------------------------------   CND Notation for custom
types   -------------------------------------


<blog = 'http://jackrabbit.apache.org/jackrabbit-jcr-demo/1.0'>
<nt =  ... >
<jcr =  ... >


[blog:user] > mix:referenceable
- blog:nickname  (string) mandatory
- blog:email  (string) mandatory
- blog:password (string) mandatory
+ blog:library (nt:folder) =nt:folder mandatory autocreated
+ blog:blogSpace (nt:folder) =nt:folder mandatory autocreated


[blog:blogEntry] > nt:hierarchyNode, mix:referenceable
- blog:title (string) mandarory primary
- blog:content (string) mandatory
- blog:rate (long)
+ blog:attachments (nt:folder) =nt:folder mandatory autocreated

[blog:comment]
- blog:content (string) mandatory primary
- blog:commenter (reference ) mandatory  < blog:user

-----------------------------------------------------------   Stack trace
-----------------------------------------------------

org.apache.jackrabbit.core.nodetype.compact.ParseException: Error while
parsing 'nickname'
    at org.apache.jackrabbit.core.nodetype.compact.Lexer.fail(Lexer.java
:146)
    at
org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.toQName
(CompactNodeTypeDefReader.java:635)
    at
org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.doPropertyDefinition
(CompactNodeTypeDefReader.java:373)
    at
org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.doItemDefs
(CompactNodeTypeDefReader.java:331)
    at
org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.parse(
CompactNodeTypeDefReader.java:208)
    at org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader
.<init>(CompactNodeTypeDefReader.java:165)
    at org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader
.<init>(CompactNodeTypeDefReader.java:150)
    at nandana.jackrabbit.example1.NodeStructure.RegisterCustomNodeTypes(
NodeStructure.java:94)
    at nandana.jackrabbit.example1.NodeStructure.main(NodeStructure.java:58)
Caused by: org.apache.jackrabbit.name.UnknownPrefixException:
    at org.apache.jackrabbit.name.QName.fromJCRName(QName.java:596)
    at
org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.toQName
(CompactNodeTypeDefReader.java:630)
    ... 7 more
Exception in thread "main" java.lang.NullPointerException
    at nandana.jackrabbit.example1.NodeStructure.RegisterCustomNodeTypes(
NodeStructure.java:101)
    at nandana.jackrabbit.example1.NodeStructure.main(NodeStructure.java:58)

















On 3/28/07, Jukka Zitting <ju...@gmail.com> wrote:
>
> Hi,
>
> On 3/29/07, Nandana Mihindukulasooriya <na...@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
>

Re: How to start thinking in JCR

Posted by Nandana Mihindukulasooriya <na...@gmail.com>.
HI,


> The URLs are "clean" and map one-to-one to nodes in the repository.
> And the best part is that you don't need to put any custom "a URL like
> this should be processed like this" rules in your application, as you
> can just use the primary node type of the target node to select the
> appropriate view component for displaying the node contents.


Thanks, now I understand the advantage of having a simplified title as the
node name
of blogEntry node type as we discussed in an earlier mail. so the
requirements of the simplification algorithm would be,

1. It should remove white spaces and other special characters which doesn't
fit in to urls
2. It should make the nodename unique with respect to it's siblings so the
one-to-one mapped urls are unique too.
(Blog entries can have same title and it's algorithms responsibility to make
them unique )
3. It should preserve the readability of the title as much as possible.


You might also want to consider custom created/published/updated
> timestamp properties, as the standard jcr:created is somewhat
> inflexible (once created it can't be modified).


Does the property type DATE behaves as the same way most of the
RDBMS behave. If we make it autocreated and don't give it a default
value, does it take the current timestamp.


Add "> nt:hierarchyNode" to allow comments to be stored as normal
> child elements of the nt:folder blog entry node.



That will also give us the jcr:created property which will be useful in
date-based listing of comments.


-----------------------------------------------     Node Structure
--------------------------------------

/blogRoot [nt:folder]
/blogRoot/user [blog:user]
/blogRoot/user/<yyyy> [nt:folder]
/blogRoot/user/<yyyy>/<mm> [nt:folder]
/blogRoot/user/<yyyy>/<mm>/blogEntry [blog:blogEntry]
/blogRoot/user/<yyyy>/<mm>/blogEntry/comment [blog:Comment]
/blogRoot/user/avatar [nt:file]
/<libray> [nt:folder]


---------------------------------   CND Notation for custom types
------------------------------

<blog = 'http://jackrabbit.apache.org/jackrabbit-jcr-demo/1.0'>
<mix = 'http://www.jcp.org/jcr/mix/1.0'>
<nt = 'http://www.jcp.org/jcr/nt/1.0'>

[blog:user] >  nt:folder, mix:referenceable
- blog:nickname  (string) mandatory
- blog:email  (string) mandatory
- blog:password (string) mandatory


[blog:blogEntry] > nt:folder, mix:referenceable
- blog:title (string) mandarory primary
- blog:content (string) mandatory
- blog:rate (long)
- blog:created (date) autocreated
- blog:published (date)
- blog:updated (date)


[blog:comment] > nt:hierarchyNode
- blog:content (string) mandatory primary
- blog:commenter (reference ) mandatory  < blog:user


BR,
Nandana

Re: How to start thinking in JCR

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

On 4/9/07, Nandana Mihindukulasooriya <na...@gmail.com> wrote:
> > Also, there's a chance to drop the explicit blog:library and
> > blog:blogSpace child node definitions if you make blog:user extend
> > nt:folder. Then you could simply have "library" and "blog" subfolders
> > within the user node, or even avoid those subfolders entirely and rely
> > on node types to detect which child nodes are blog entries and which
> > are other resources.
>
> Does it mean a structure like,
>
>  /blogRoot/user/ <yyyy>/<mm> /blogEntry [blog:blogEntry]
>  /blogRoot/user/<avatar.gif> [nt:file]
>
>  Or a structure like,
>
>  /blogRoot/user/blogEntry [blog:blogEntry]
>  /blogRoot/user/<avatar.gif> [nt:file]
>
> In the latter one, are we using the jcr:created property of  nt:hierarchyNode
> if we want to list the blog entries according to date or month.

I would use the former structure (with yyyy/mm) as discussed in
previous messages. In any case, for date-based listings it makes sense
to use a subtree query like
"/jcr:root/blogRoot/user//element(*,blog:blogEntry) order by
@jcr:created" to avoid hardcoding the folder structure in your
application. The yyyy/mm structure is more designed to avoid too many
child nodes in one place and to make it easier to administer the
content, not that much as a way to organize normal read access.

> > It's a tradeoff between flexibility and more
> > completely specifying the content structure.
> >
> > I would actually suggest we take this opportunity for extra
> > flexibility since it gives some very nice late binding benefits when
> > we later start defining the URL mapping for the application.
> >
> By flexibility, does it mean the flexibility we have to change the structure
> as we want in the future ? How does this help to get the late binding
> benefits?

The benefit is that without the explicit blog:blogSpace and
blog:library subfolders, we can do a clean and direct URL to path
mapping without any hardcoded information about the application
structure. Consider the URL space for such a blog application:

    .../jukka/ -> /blogRoot/jukka [blog:user]
    .../jukka/2007/04/hello/ -> /blogRoot/jukka/2007/04/hello [blog:blogEntry]
    .../jukka/2007/04/hello/world.pdf ->
/blogRoot/jukka/2007/04/hello/world.pdf [nt:file]
    .../jukka/avatar.png -> /blogRoot/jukka/avatar.png [nt:file]

The URLs are "clean" and map one-to-one to nodes in the repository.
And the best part is that you don't need to put any custom "a URL like
this should be processed like this" rules in your application, as you
can just use the primary node type of the target node to select the
appropriate view component for displaying the node contents.

> [blog:user] >  nt:hierarchyNode, mix:referenceable

Use nt:folder instead of nt:hierarchyNode as the parent type to allow
the custom substructure described above.

> [blog:blogEntry] > nt:hierarchyNode, mix:referenceable
> - blog:title (string) mandarory primary
> - blog:content (string) mandatory
> - blog:rate (long)
> + blog:attachments (nt:folder) =nt:folder mandatory autocreated

Here we could do the same thing, allow attachments directly below the
blog entries without the intermediate blog:attachments folder. Just
change the supertype from nt:hierarchyNode to nt:folder. This also
allows us to place the comments as nt:hierarchyNode subnodes within
the same subtree.

You might also want to consider custom created/published/updated
timestamp properties, as the standard jcr:created is somewhat
inflexible (once created it can't be modified).

>  [blog:comment]
> - blog:content (string) mandatory primary
> - blog:commenter (reference ) mandatory  < blog:user

Add "> nt:hierarchyNode" to allow comments to be stored as normal
child elements of the nt:folder blog entry node.

BR,

Jukka Zitting

Re: How to start thinking in JCR

Posted by Nandana Mihindukulasooriya <na...@gmail.com>.
Hi,


> Also, there's a chance to drop the explicit blog:library and
> blog:blogSpace child node definitions if you make blog:user extend
> nt:folder. Then you could simply have "library" and "blog" subfolders
> within the user node, or even avoid those subfolders entirely and rely
> on node types to detect which child nodes are blog entries and which
> are other resources.


Does it mean a structure like,

 /blogRoot/user/ <yyyy>/<mm> /blogEntry [blog:blogEntry]
 /blogRoot/user/<avatar.gif> [nt:file]

 Or a structure like,

 /blogRoot/user/blogEntry [blog:blogEntry]
 /blogRoot/user/<avatar.gif> [nt:file]

 In the latter one, are we using the jcr:created property of  nt:hierarchyNode
if we want to list the blog entries according to date or month.



> It's a tradeoff between flexibility and more
> completely specifying the content structure.
>
> I would actually suggest we take this opportunity for extra
> flexibility since it gives some very nice late binding benefits when
> we later start defining the URL mapping for the application.
>
By flexibility, does it mean the flexibility we have to change the structure
as we want in the future ? How does this help to get the late binding
benefits ?



----------------------------------   Node Structure
---------------------------------

 /blogRoot [nt:folder]
/blogRoot/user [blog:user]
/blogRoot/user/ blogEntry [blog:blogEntry]
/blogRoot/user/ blogEntry /comment [blog:Comment]
/blogRoot/user/avatar [nt:file]
/<libray> [nt:folder]


------------------------------   CND Notation for custom types
------------------------

<blog = 'http://jackrabbit.apache.org/jackrabbit-jcr-demo/1.0'>
<mix = 'http://www.jcp.org/jcr/mix/1.0'>
<nt = 'http://www.jcp.org/jcr/nt/1.0'>

[blog:user] >  nt:hierarchyNode, mix:referenceable
- blog:nickname  (string) mandatory
- blog:email  (string) mandatory
- blog:password (string) mandatory

[blog:blogEntry] > nt:hierarchyNode, mix:referenceable
- blog:title (string) mandarory primary
- blog:content (string) mandatory
- blog:rate (long)
+ blog:attachments (nt:folder) =nt:folder mandatory autocreated

 [blog:comment]
- blog:content (string) mandatory primary
- blog:commenter (reference ) mandatory  < blog:user

BR,

Nandana

Re: How to start thinking in JCR

Posted by Nandana Mihindukulasooriya <na...@gmail.com>.
Hi,
   Thank you very much Brian and Jukka for the comments. This is the code I
used earlier (without error handling),

Workspace workSpace = session.getWorkspace();
String CNDFile = "…";
FileReader fileReader = *new* FileReader(cndFileName);
CompactNodeTypeDefReader cndReader =
*new*CompactNodeTypeDefReader(fileReader, cndFileName);
List ntdList = cndReader.getNodeTypeDefs();
NodeTypeManagerImpl  ntmgr = (NodeTypeManagerImpl)ws.getNodeTypeManager()
NodeTypeRegistry ntreg = ntmgr.getNodeTypeRegistry();
NodeTypeDef ntd = (NodeTypeDef)ntdList.next()
ntreg.registerNodeType(ntd);

But now I'm using Jackrabbit 1.2.3 , with JSR - 349, I can simply use

file = *new* File("CNDFile");
FileInputStream inputStream = *new* FileInputStream(file);
Workspace workSpace = session.getWorkspace();
NodeTypeManagerImpl ntTypeMgr = (NodeTypeManagerImpl) workSpace
.getNodeTypeManager();
ntTypeMgr.registerNodeTypes(inputStream,JackrabbitNodeTypeManager.*
TEXT_X_JCR_CND*,*false*);


But when I tried to use it with the registerExisting flag true, it gave me
an exception at line,

*return* (NodeType[]) nodeTypes.toArray(*new* NodeTypeDef[nodeTypes.size
()]);

Exception in thread "main" *java.lang.ArrayStoreException*
    at java.lang.System.arraycopy(*Native Method*)
    at java.util.ArrayList.toArray(*ArrayList.java:304*)
    at
org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl.registerNodeTypes(*
NodeTypeManagerImpl.java:295*)
    at nandana.jackrabbit.example1.NodeStructuteNew.main(*
NodeStructuteNew.java:34*)


I was able to fix type conflict by changing that line to

*return* (NodeType[]) nodeTypes.toArray(*new* NodeType[nodeTypes.size()])

I will go through content structure again and do the modifications. Thanks.

BR,
Nandana

Re: How to start thinking in JCR

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

On 4/3/07, Nandana Mihindukulasooriya <na...@gmail.com> wrote:
> Does all properties have to have a namespace prefix ? When I tried
> to register the node types using the CompactNodeTypeDefReader it
> gave an error caused by "UnknownPrefixException".

You can have properties in the default namespace with no namespace
prefix. Since Jackrabbit 1.0.1 there hasn't been a need to include the
default namespace prefixes in the CND file (see JCR-383), so I don't
think that's an issue with your CND file.

Check that the "blog" prefix definition is included in the CND file
you feed to Jackrabbit, and that there's no typo in the property
definition. I'm not sure if the CND you included in your message is
the one you're using since you had replaced parts with "...".

> Like Node class's hasNode() method is there convinient methods to check
> whether a namespace prefix is already registered ?

Not really. You can use the Session.getNamespacePrefix() method and
catch the exception if needed. However, since the 1.1 release
Jackrabbit has automatically registered the namespaces included in the
CND files given for registration, so you shouldn't need to worry about
that. See JCR-349 for the details.

> and to check whether a Node Type is already registed ? I didn't find such
> methods in NodeTypeManager or NamespaceRegistry Interfaces of JCR.
> I used the NodeTypeManagerImpl which inplements the
> JackrabbitNodeTypeManager Interface.

In the 1.1 release we added the
JackrabbitNodeTypeManager.hasNodeType() utility method designed for
just this purpose. See JCR-521 for the details.

> But in the mailing list
> // only register the type if it does not yet exist
>     if (!manager.hasNodeType("myfile"))
> is used to check this. Is that method not available in Jackrabbit 1.0 ?

No, only in Jackrabbit 1.1 and higher. I suggest that you use the
latest Jackrabbit release in your work.

> To create a the repository I used the TransientRepository class which
> created me a default repository configuration file. But if I am using JNDI binding
> do I have to create a repository.xml by my self ? Is there a way to specify
> to create a default configuration ?

It depends on how you do the JNDI binding. For example if you follow
the deployment model 2 instructions for Tomcat
(http://jackrabbit.apache.org/doc/deploy/howto-model2.html), you need
to specify the path to the repository.xml configuration file. You can
use the default one created by TransientRepository.

> Where can I find contrib/compact-nt tools ?

The code in the compact-nt contrib project was moved to
org.apache.jackrabbit.core.nodetype.compact within the jackrabbit-core
component. Nowadays you shouldn't need to access the functionality
directly, instead use the JackrabbitNodeTypeManager extension
interface.

> [blog:user] > mix:referenceable
> - blog:nickname  (string) mandatory
> - blog:email  (string) mandatory
> - blog:password (string) mandatory
> + blog:library (nt:folder) =nt:folder mandatory autocreated
> + blog:blogSpace (nt:folder) =nt:folder mandatory autocreated

You'll want to make blog:user extend nt:hierarchyNode to make it
possible to create blog:user nodes within nt:folder.

Also, there's a chance to drop the explicit blog:library and
blog:blogSpace child node definitions if you make blog:user extend
nt:folder. Then you could simply have "library" and "blog" subfolders
within the user node, or even avoid those subfolders entirely and rely
on node types to detect which child nodes are blog entries and which
are other resources. It's a tradeoff between flexibility and more
completely specifying the content structure.

I would actually suggest we take this opportunity for extra
flexibility since it gives some very nice late binding benefits when
we later start defining the URL mapping for the application.

BR,

Jukka Zitting

Re: How to start thinking in JCR

Posted by Brian Thompson <el...@gmail.com>.
In your CND-file, you need to declare all the namespaces you'll use, even
the ones that come with Jackrabbit:

==============================

/* orig.namespaces */
<mix = 'http://www.jcp.org/jcr/mix/1.0'>
<nt = 'http://www.jcp.org/jcr/nt/1.0'>

/* my stuff */
<my = 'http://www.example.com/jcr-ns/my'>

<page = 'http://www.example.com/jcr-ns/page'>

[my:page] > nt:unstructured, mix:referenceable
        orderable
        - my:active (boolean) = 'true' mandatory autocreated

=======================

etc...


On 4/2/07, Nandana Mihindukulasooriya <na...@gmail.com> wrote:
>
> Hi,
>      I changed the Node structure as following according to the discussion
> and tried to register them in the repository. Here are some of the
> problems
> I came across.
>
> Grammer for CND notation defines a property of a Node type as
>
> property_def ::= "-" property_name [property_type_decl]
>                  [default_values] [attributes]
>                  [value_constraints]
> property_name ::= string
>
> Does all properties have to have a namespace prefix ? When I tried
> to register the node types using the CompactNodeTypeDefReader it
> gave an error caused by "UnknownPrefixException".
>
> It also said that if I define the autocreated attribute for a childnode
> I should define the default type for them which is of cource reasonable.
>
> Like Node class's hasNode() method is there convinient methods to check
> whether a namespace prefix is already registered ? and to check whether
> a Node Type is already registed ? I didn't find such methods in
> NodeTypeManager or NamespaceRegistry Interfaces of JCR. I used the
> NodeTypeManagerImpl which inplements the JackrabbitNodeTypeManager
> Interface.
>
> But in the mailing list
> // only register the type if it does not yet exist
>     if (!manager.hasNodeType("myfile"))
> is used to check this. Is that method not available in Jackrabbit 1.0 ?
>
> To create a the repository I used the TransientRepository class which
> created
> me a default repository configuration file. But if I am using JNDI binding
> do I have to create a repository.xml by my self ? Is there a way to
> specify
> to
> create a default configuration ?
>
> Where can I find contrib/compact-nt tools ?
>
> I really appreciate your comments. Thank you in advance.
>
> BR,
> Nandana
>
>
> ----------------------------------------------------- Node Structure
> -------------------------------------------------------
>
> /blogRoot [nt:folder]
> /blogRoot/user [blog:user]
> /blogRoot/user/blogSpace [nt:folder]
> /blogRoot/user/blogSpace/<yyyy> [nt:folder]
> /blogRoot/user/blogSpace/<yyyy>/<mm> [nt:folder]
> /blogRoot/user/blogSpace/<yyyy>/<mm>/blogEntry [blog:blogEntry]
> /blogRoot/user/blogSpace/<yyyy>/<mm>/blogEntry/comment [blog:Comment]
> /blogRoot/user/library [nt:folder]
> /blogRoot/user/libray/avatar [nt:file]
> /<libray> [nt:folder]
>
> -------------------------------------------   CND Notation for custom
> types   -------------------------------------
>
>
> <blog = 'http://jackrabbit.apache.org/jackrabbit-jcr-demo/1.0'>
> <nt =  ... >
> <jcr =  ... >
>
>
> [blog:user] > mix:referenceable
> - blog:nickname  (string) mandatory
> - blog:email  (string) mandatory
> - blog:password (string) mandatory
> + blog:library (nt:folder) =nt:folder mandatory autocreated
> + blog:blogSpace (nt:folder) =nt:folder mandatory autocreated
>
>
> [blog:blogEntry] > nt:hierarchyNode, mix:referenceable
> - blog:title (string) mandarory primary
> - blog:content (string) mandatory
> - blog:rate (long)
> + blog:attachments (nt:folder) =nt:folder mandatory autocreated
>
> [blog:comment]
> - blog:content (string) mandatory primary
> - blog:commenter (reference ) mandatory  < blog:user
>
> -----------------------------------------------------------   Stack trace
> -----------------------------------------------------
>
> org.apache.jackrabbit.core.nodetype.compact.ParseException: Error while
> parsing 'nickname'
>     at org.apache.jackrabbit.core.nodetype.compact.Lexer.fail(Lexer.java
> :146)
>     at
>
> org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.toQName
> (CompactNodeTypeDefReader.java:635)
>     at
>
> org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.doPropertyDefinition
> (CompactNodeTypeDefReader.java:373)
>     at
>
> org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.doItemDefs
> (CompactNodeTypeDefReader.java:331)
>     at
> org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.parse
> (
> CompactNodeTypeDefReader.java:208)
>     at
> org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader
> .<init>(CompactNodeTypeDefReader.java:165)
>     at
> org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader
> .<init>(CompactNodeTypeDefReader.java:150)
>     at nandana.jackrabbit.example1.NodeStructure.RegisterCustomNodeTypes(
> NodeStructure.java:94)
>     at nandana.jackrabbit.example1.NodeStructure.main(NodeStructure.java
> :58)
> Caused by: org.apache.jackrabbit.name.UnknownPrefixException:
>     at org.apache.jackrabbit.name.QName.fromJCRName(QName.java:596)
>     at
>
> org.apache.jackrabbit.core.nodetype.compact.CompactNodeTypeDefReader.toQName
> (CompactNodeTypeDefReader.java:630)
>     ... 7 more
> Exception in thread "main" java.lang.NullPointerException
>     at nandana.jackrabbit.example1.NodeStructure.RegisterCustomNodeTypes(
> NodeStructure.java:101)
>     at nandana.jackrabbit.example1.NodeStructure.main(NodeStructure.java
> :58)
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> On 3/28/07, Jukka Zitting <ju...@gmail.com> wrote:
> >
> > Hi,
> >
> > On 3/29/07, Nandana Mihindukulasooriya <na...@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
> >
>