Community:NCore/Java client api example

From NSDLWiki

Jump to: navigation, search

This page contains some annotated examples on how to perform operations on NCore objects through the Java API. To begin, we will start off with an example that covers many of the basic functionality. This particular example is analogous to adding a resource (+ associated metadata) to a collection. We go through the process of finding (or creating) the resource, building Metadata for it (with some of our own custom properties, relationships, and datastreams in it) and commit our changes to the repository.

       /*
        * This is an instance of the core NDRAccess interface. Everything,
        * ultimately, goes through this.
        */
       NDRAccess ndr = NDRAccessFactory.getAccess();
       /* The namespace of our custom properties and relationships, with our preferred namespace prefix */
       String my_ns = "http://my/namespace#";
       String my_ns_prefix = "mine";
       /* The identities of a MetadataProvider and Aggregator */
       NDRObjectInfo MY_MD_PROVIDER = ...;
       NDRObjectInfo MY_AGGREGATOR = ...;
       /*
        * Here we name some of our own datastreams, properties, and
        * relationships that don't exist in the NCore model. We're going
        * create instances of these to add them to an object later.
        */
       Property.Name myProperty =
               new Property.Name(my_ns, "MySpecialProperty", my_ns_prefix);
       Relationship.Name myRelationship =
               new Relationship.Name(my_ns, "mySpecialRelatiomnship",
                       my_ns_prefix);
       /* 
        * We are experimenting with exposing datastream names as being qualified
        * by a namespace, just like properties and relationships.  This may 
        * not stay this way, depending on how the pain/benefit ration works out..
        */
       Datastream.Name myDatastream =
               new Datastream.Name(my_ns, "mySpecialDatastream", my_ns_prefix);
       /*
        * Here, we fetch the MetadataProvider and Aggregator instances from the
        * repository. Dynamically typed languages probably won't need the
        * second argument (desired object type).
        */
       MetadataProvider provider =
               ndr.getObject(MY_MD_PROVIDER, Type.MetadataProvider);
       Aggregator agg = ndr.getObject(MY_AGGREGATOR, Type.Aggregator);
       /* This is the url of a resource we wish to add to our "collection" */
       String url = "http://url_to_find.org";
       /*
        * NDRAccess provides a Finder for finding objects based on various
        * criteria. Here, we use a convenience method to find a resource by
        * it's URL.
        */
       Resource r =
               ndr.getObjectFinder()
                       .findResource(Resource.IDENTIFIER_URL, url);
       /* If the resource is not found, we'll create one */
       if (r == null) {
           /*
            * New objects are created by invoking newObject on NDRAccess. It
            * creates an essentially empty object with the given type.
            */
           r = ndr.newObject(Type.Resource);
           /*
            * Resources that are URLs have a special method 'setContentURL' to
            * set this identity. It is the equivalent of doing the following,
            * using only feneral NDRObject methods
            * r.addProperty(Resource.IDENTIFIER_URL, url);
            * 
            * Likewise, there is also a method setContent(InputStream, String),
            * which specifies the binary content that lives in the resource
            * (instead of in an external URL). This method is equivalent to :
            * Datastream d = r.addDatastream(Resource.CONTENT);
            * d.setContent(contentStream); d.setMimeType(mime);
            */
           r.setContentURL(url);
           /*
            * Commits the object to the repository. Changes are not permanent
            * until committed
            */
           r.commit();
       }
       /*
        * Add the resource as a member of our aggregator. This is an
        * aggregator- specific method. In terms of generic NDRObject methods,
        * this is equivalent to:
        * 
        * r.addRelationship(Resource.MEMBER_OF, agg); r.commit();
        * 
        * The motivation for this method is that it is perhaps a more natural
        * way to think of adding members to an aggregation...
        */
       agg.addMember(r);
       agg.commit();
       /*
        * This is an exercise finding objects in the repository that match a
        * given set of required properties or relationships.
        */
       NDRObject matchTemplate = new NDRObjectTemplate(Type.Metadata);
       matchTemplate.addProperty(Metadata.UNIQUE_ID, "12345");
       matchTemplate.addRelationship(Metadata.METADATA_PROVIDED_BY, provider);
       Results<NDRObjectInfo> results =
               ndr.getObjectFinder().matchObject(matchTemplate);
       NDRObjectInfo myFoundObject;
       /*
        * Since a result set is an Iterable instead of a java.util.Collection,
        * getting exactly one object may seem a bit awkward...
        */
       try {
           /*
            * Doing it this way is just to show how Results are suitable for
            * use in the enhanced for loop..
            */
           for (NDRObjectInfo found : results) {
               if (myFoundObject != null) {
                   throw new IllegalStateException(
                           "Not expecting to find more than one...");
               }
               myFoundObject = found;
           }
       } finally {
           /*
            * The we did this in a try/catch block instead of the "easy" way
            * was that Results from a finder are a closeable resource, and
            * should be closed when done (The implementation underneath may use
            * jdbc, for example). Although probably not required for clients
            * using a web API implementation underneath, it is probably best
            * practice to be careful and close results explicitly.
            */
           results.close();
           if (myFoundObject == null) {
               throw new IllegalStateException(
                       "Was expecting to find *something*!");
           }
       }
       /*
        * Getting back to the task at hand, we created (or found) a resource of
        * interest, and added it to our aggregator. Now we create metadata for
        * it.
        *
        *
        *
        * This is a MetadataProvider-specific method that creates a new
        * Metadata object that describes a given resource. It is equivalent to
        * the following generic NDRObject operations:
        * 
        * NDRObject metadata = ndr.newObject(Type.Metadata);
        * metadata.addRelationship(Metadata.METADATA_FOR, r);
        * metadata.addRelationship(Metadata.METADATA_PROVIDED_BY, provider);
        * 
        */
       Metadata metadata = provider.newMetadata(r);
       /* Here, assemble the metadata so that it is accesable as an InputStream */
       InputStream mdContent = (... various processing);
       /* Set the content for for format nsdl_dc metadata */
       metadata.setMetadata("nsdl_dc", mdContent);
       /* Add our non-NCore properties and relationships */
       metadata.addProperty(myProperty, "my special value");
       metadata.addRelationship(myRelationship, myFoundObject);
       /* Now, we'll add a datastream that isn't in the NCore model */
       InputStream otherDSContent =  ( ... Other processing);
       /*
        * New datastreams in an object are created using the addDatastream
        * method. This creates an empty datastream in the object which can then
        * be populated.
        */
       Datastream myDS = metadata.addDatastream(myDatastream);
       myDS.setMimeType("text/xml");
       myDS.setContent(otherDSContent);
       /* Now, commit our Metadata to the repository! */
       metadata.commit();

Below is a quick code fragment that illustrates some 'read' capabilities. In this example, our task is to display all OAI identifiers of our metadata that describes resources that are grouped in a particular aggregation.

       /*
        * getMembers is a aggregator-specific method that returns members of an
        * Aggregation, with an optional 'type' parameter to only return
        * aggregator members that are of the given type. Here, we're interested
        * only in resources.
        * 
        * This method as used here (with the Type.Resource argument) is
        * equivalent to the following, in generic NDRObject operations:
        * 
        * NDRObject template = new NDRObjectTemplate(Type.Resource);
        * template.addRelationship(Resource.MEMBER_OF, aggregator);
        * ndr.getObjectFinder().matchObject(template);
        */
       for (Resource memberResource : aggregator.getMembers(Type.Resource)) {
           /* Get all Metadata from my provider describing this resource */
           for (Metadata myMD : memberResource.getMetadata(provider)) {
               Property oaiID =
                       myMD.getProperty(OAIModel.Item.Components.ITEM_ID);
               if (oaiID != null) {
                   System.out.println("OAI id: " + oaiID.value());
               }
           }
       }

By default, all NDRAccess operations assume they're working with 'Active' objects, in the sense that getRelationships() will only return relationships to 'Active' objects, matchObject will implicitly assume you wish to match 'Active' objects, etc. If one did wish to view or find objects that aren't active, that needs to be made explicit:

       NDRObject template  new NDRObjectTemplate(Type.Metadata);
       /* We want to match *deleted* objects */
       template.setState(State.Deleted);
       template.addRelationship(Metadata.METADATA_PROVIDED_BY, provider);
       ndr.getObjectFinder().matchObject(template);

While we're on the topic of object state, here is how to delete an object:

       /* deleteObject is a method provided by NDRAccess.  It is equivalent to
        * 
        * metadata.setState(State.Deleted);
        * metadata.commit();
        * 
        * It may make sense to add 'cascade' parameter to NDRAccess to
        * aid in deletion of Metadataproviders + Metadata underneath, for example
        */
       ndr.deleteObject(metadata);
Personal tools