Monday, June 17, 2013

Jargon and RestEasy - some notes on what I've run into

I'm starting to work on a formal REST API for iRODS.  This is coming from multiple projects, but this first one gives me a chance to build the skeleton and set down some practices for later.  The project itself is here on the RENCI GForge.

For several reasons, I decided to roll with JBoss RestEasy.  Not least of which is their compliance with JAX-RS, which goes some way towards future-proofing any work I do.  There is also a need to do some S/MIME encryption of messages, and it looks like RestEasy handles this well enough.

RestEasy is not without its headaches and frustrations.  A good deal of this frustration has to do with integrating Spring beans into the mix, which I use a lot in Jargon.  The docs don't seem to reflect actual usage in this area, both for service development, and for testing.  In the Spring Integration section of the RestEasy docs, you get this example:


<web-app>
   <display-name>Archetype Created Web Application</display-name>

   <servlet>
      <servlet-name>Spring</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet;</servlet-class>
   </servlet>

   <servlet-mapping>
      <servlet-name>Spring</servlet-name>
      <url-pattern>/*</url-pattern>
   </servlet-mapping>


</web-app>

For your web.xml, and:


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">

    <!-- Import basic SpringMVC Resteasy integration -->
    <import resource="classpath:springmvc-resteasy.xml"/>

For the Spring configuration file.

It doesn't work, it doesn't load your beans...

I dug around a lot (and sorry, I cannot retrace my steps and refer you to some of the info I found!), and by combining several proposed solutions I found that this worked...

First, for the web.xml document:


 irods-rest

 
  contextConfigLocation
  /WEB-INF/rest-servlet.xml
 

   
        resteasy.media.type.param.mapping
        contentType
    
    
    
    
            resteasy.document.expand.entity.references
            false
         
 
 

 
 
  org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
 

 
 
  org.jboss.resteasy.plugins.spring.SpringContextLoaderListener
 

 
 
  resteasy.servlet.mapping.prefix
  /rest
 
 
  rest
  org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
 

 
 
  rest
  /rest/*
 

 
The things to highlight here include the fact that I had to comment out the RestEasy component scan context parameter, add the contextConfigLocation parameter, and wire in the Spring RestEasy integration components by hand.  In this configuration, it does load my custom beans, and then it loads my RestEasy services by the fact that I added direct Spring configuration for that component scan:



 
 
 
 
 
 

 
OK, so that seems to be OK now. I have a service running, it's doing content negotiation, it's wired in my Jargon components...now, how do I test it? Jargon has a lot of tests, I don't need to test Jargon or iRODS, so I decided to test at the http request level. Given this, I was not too excited at testing with mocks. Mocks seem like a lot of trouble, and might mask some of the subtleties involved, given that it's pretty easy (I thought) to test all of this with an embedded servlet container. This seems ideal...test end-to-end as the user sees things, create sample code at the same time. What could be better!

The JBoss docs don't go into great detail about best practices for testing RestEasy apps, but clearly the TJWS embedded container seemed obvious.  They provide a bit of pseudo-code in the docs, and, unfortunately, it does not work:



   public static void main(String[] args) throws Exception 
   {
      final TJWSEmbeddedJaxrsServer tjws = new TJWSEmbeddedJaxrsServer();
      tjws.setPort(8081);

      tjws.start();
      org.jboss.resteasy.plugins.server.servlet.SpringBeanProcessor processor = new SpringBeanProcessor(tjws.getDeployment().getRegistry(), tjws.getDeployment().getFactory();
      ConfigurableBeanFactory factory = new XmlBeanFactory(...);
      factory.addBeanPostProcessor(processor);
   }


At least we see a bit that looks like we can adapt to the setup() method of a JUnit test case.  Given that clue, I found some very helpful posts, such as this one from 'eugene' (thanks eugene!).  But even this did not work, as ApplicationContext was not @Autowired.  I kept getting NPEs.

This got me very close, and I've used SpringJUnit4ClassRunner extensively for Hibernate/JPA based applications in the iDrop suite, so I felt like I just needed to hack on that a bit and I could get there.  The missing piece came from 'Daff' (thanks Daff!) who pointed out the ability to have your JUnit test case extend ApplicationContextAware in his post.

I tried to wire this into the @BeforeClass annotated startup() method with a static ApplicationContext variable.  Needless to say, that did not work, and was always 'null'.  It ended up that I had to place that server startup code in the @Before annotated method, which runs on instance variables, and the context was then available.  That's a little bit hinky, but given that I have a very short window for this project, I rolled with a solution there that saves the ApplicationContext in a static variable, and checks to see (singleton-like) if an instance has been created yet for the JUnit class.  This is working fine so far, and only smells a tiny bit.  I may revisit it, but I'm happy enough to get a base testing strategy defined.

So, here's a JUnit test that works with Spring configured beans:


package org.irods.jargon.rest.commands;

import junit.framework.Assert;

import org.jboss.resteasy.client.ClientRequest;
import org.jboss.resteasy.client.ClientResponse;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.plugins.server.tjws.TJWSEmbeddedJaxrsServer;
import org.jboss.resteasy.plugins.spring.SpringBeanProcessor;
import org.jboss.resteasy.plugins.spring.SpringResourceFactory;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:jargon-beans.xml",
  "classpath:rest-servlet.xml" })
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
  DirtiesContextTestExecutionListener.class })
public class UserServiceTest implements ApplicationContextAware {

 private static TJWSEmbeddedJaxrsServer server;

 private static ApplicationContext applicationContext;

 @BeforeClass
 public static void setUpBeforeClass() throws Exception {

 }

 @AfterClass
 public static void tearDownAfterClass() throws Exception {
  if (server != null) {
   server.stop();
  }
 }

 @Before
 public void setUp() throws Exception {
  if (server != null) {
   return;
  }

  server = new TJWSEmbeddedJaxrsServer();
  server.setPort(8888);
  ResteasyDeployment deployment = server.getDeployment();
  server.start();
  Dispatcher dispatcher = deployment.getDispatcher();
  SpringBeanProcessor processor = new SpringBeanProcessor(dispatcher,
    deployment.getRegistry(), deployment.getProviderFactory());
  ((ConfigurableApplicationContext) applicationContext)
    .addBeanFactoryPostProcessor(processor);

  SpringResourceFactory noDefaults = new SpringResourceFactory(
    "userService", applicationContext, UserService.class);
  dispatcher.getRegistry().addResourceFactory(noDefaults);

 }

 @After
 public void tearDown() throws Exception {
 }

 @Test
 public void testGetUserJSON() throws Exception {

  final ClientRequest clientCreateRequest = new ClientRequest(
    "http://localhost:8888/user/mconway?contentType=application/json");

  final ClientResponse clientCreateResponse = clientCreateRequest
    .get(String.class);
  Assert.assertEquals(200, clientCreateResponse.getStatus());
  String entity = clientCreateResponse.getEntity();
  Assert.assertNotNull(entity);
 }

 

 @Override
 public void setApplicationContext(final ApplicationContext context)
   throws BeansException {
  applicationContext = context;
 }

}

























So it might not be 'ideal', but I can move on and get this thing done.  I'd appreciate any pointers or refinements, and hopefully this will at least get you running and save you similar headaches.

Thursday, April 25, 2013

Demo of SPARQL search in HIVE/iRODS Integration

This demo video was prepared for a presentation this week, and it shows a bit more of the integration between iRODS and https://www.nescent.org/sites/hive/Main_Page.

As mentioned previously, we're integrating controlled vocabularies via SKOS using the HIVE system. Dr. Jane Greenberg as SILS has prepared a short paper describing some of the concepts and motivations for this effort here.

Technically, we have three primary elements:


  1. Integration of the HIVE system into our iDrop web interface.  This includes a new set of Jargon libraries that support this integration, allowing easy wiring of HIVE functionality via Spring.
  2. A 'visitor' and 'iterator' library in Jargon for sweeping through data objects and collections marked up with HIVE RDF terms.
  3. An OWL vocabulary (though it's a rough sketch right now) describing iRODS ICAT metadata and relationships, which also goes into the index with our vocabularies.
  4. A HIVE query REST interface that can issue SPARQL queries to our indexed triple store, and a start at some preset queries such as searching on a term, or searching for items related to a term.
These elements are demonstrated in the demo video below...



Saturday, March 23, 2013

SPARQL queries for iRODS Data

This is cool:

PREFIX irods:       <http://www.irods.org/ontologies/2013/2/iRODS.owl#> 
PREFIX skos:    <http://www.w3.org/2004/02/skos/core#>
SELECT ?x ?y
WHERE { 

?x  irods:correspondingConcept ?y .
?y skos:related <http://www.fao.org/aos/agrovoc#c_28638>
}



That's a SPARQL query running on Jena Fuseki...and this is related to the work we're doing with HIVE integration, as discussed in this previous blog entry...SPARQL is a query langage that can be used to search semantic metadata, in our case, metadata that describes the iRODS catalog, SKOS controlled vocabularies, and 'serialized' RDF statements saved as iRODS AVUs that apply controlled vocabulary terms to iRODS files and collections.  This improves the normal iRODS AVUs by giving them structure and meaning, via SKOS.

In the case above, we have a term defined in the Agrovoc vocabulary which looks something like this snippet, as 'turtle'.

<http://www.fao.org/aos/agrovoc#c_1669>
      a       skos:Concept ;
      skos:narrower <http://www.fao.org/aos/agrovoc#c_7979> , <http://www.fao.org/aos/agrovoc#c_1745> , <http://www.fao.org/aos/agrovoc#c_7656> , <http://www.fao.org/aos/agrovoc#c_3688> , <http://www.fao.org/aos/agrovoc#c_6963> , <http://www.fao.org/aos/agrovoc#c_14658> , <http://www.fao.org/aos/agrovoc#c_16099> , <http://www.fao.org/aos/agrovoc#c_29563> , <http://www.fao.org/aos/agrovoc#c_613> ;
      skos:prefLabel "Climatic zones"@en ;
      skos:related <http://www.fao.org/aos/agrovoc#c_7213> , <http://www.fao.org/aos/agrovoc#c_28638> , <http://www.fao.org/aos/agrovoc#c_29554> ;
      skos:scopeNote "Use for areas having identical climates; for the physical phenomenon use Climate (1665)"@en .


Note that SKOS will define broader, narrower, and related terms, along with other data.  This means that a user may tag an iRODS file or collection with a term like c_1669, and search for it on the related term c:6963.  

That's what the SPARQL query above shows, you are looking for any iRODS files or collections that have an AVU with a SKOS vocabulary term from Agrovoc that is related to a given concept.  The result of this query, in JSON, looks like so:

{
        "x": { "type": "uri" , "value": "irods://localhost:1247/test1/trash/home/test1/jargon-scratch.1256888938/JenaHiveIndexerServiceImplWithDerbyTest/testExecuteOnt/subdirectory2/hivefile7" } ,
        "y": { "type": "uri" , "value": "http://www.fao.org/aos/agrovoc#c_1669" }
      } ,
      {
        "x": { "type": "uri" , "value": "irods://localhost:1247/test1/trash/home/test1/jargon-scratch.1256888938/JenaHiveIndexerServiceImplWithDerbyTest/testExecuteOnt/subdirectory1/hivefile7" } ,
        "y": { "type": "uri" , "value": "http://www.fao.org/aos/agrovoc#c_1669" }
      } ,
      {
        "x": { "type": "uri" , "value": "irods://localhost:1247/test1/trash/home/test1/jargon-scratch.705362199/JenaHiveIndexerServiceImplWithOntTest/testExecuteOnt/subdirectory1/hivefile6" } ,
        "y": { "type": "uri" , "value": "http://www.fao.org/aos/agrovoc#c_1669" }
      } ,


As you can see (or at least trust me on this), you are finding iRODS data based on a related concept.  With Fuseki, we could add such SPARQL queries in short order to the iDrop apps, or even to iCommands.  Note that we've done this by marking up iRODS data with SKOS terms, storing these as special AVUs, indexing them with a spider, and then putting them into a Jena triple store for SPARQL queries.  The same sorts of things can also be pretty easily done using Lucene for text search, and adding these new methods of finding data is going to be an interesting area for Jargon and iRODS development.  You can see some of the HIVE work in the GForge project at DICE and RENCI here!

Tuesday, March 12, 2013

Some work in progress integrating HIVE with iRODS

iRODS has a powerful facility, through the iCAT master catalog, to manage user-supplied metadata on different parts of the catalog domain, such as files and collections.  These are 'AVU' triples, which are just attribute-value-unit slots that can hold free-format data.

We're using AVUs by adding conventions and metaphors on top of them, such as free tags, starred folders, and shares, such as in this previous video demo.  One weakness of AVUs is that they are totally unstructured.  This does not mean that we cannot apply structure at a higher level, and that's exactly what the interest in HIVE integration is about.

HIVE is an acronym for Helping Interdisciplinary Vocabulary Engineering, and HIVE is a project from the Metadata Research Center at the School of Information and Library Science at UNC Chapel Hill,.  (Did I mention we were just voted the #2 best program in the country by US News and World Report?)

HIVE is a tool that allows browsing and searching across controlled vocabularies defined in SKOS, a simple RDF schema for defining dictionaries, thesauri, and other structured metadata.  A key aspect is the integration of RDF with Lucene to allow searching across selected vocabularies, a helpful approach since much of the focus of iRODS and DICE is in multi-disciplinary research collaboration, as in the Datanet Federation Consortium.  HIVE solves a lot of problems we were facing, so it is a happy circumstance that the MRC is just around the corner from us, and we're busy looking at integration.

In a nutshell, HIVE allows us to:


  • Keep multiple controlled vocabularies
  • Allow users to easily search and navigate across vocabularies to find appropriate terms
  • Make AVU metadata meaningful by providing structure and consistency
  • Power rich metadata queries using tools such as SPARQL to find iRODS files and collections

A short video demo follows that shows the first level of integration between iDrop (the iRODS cloud browser) and HIVE.  We've added a HIVE tab to contain a concept browser, allowing markup of iRODS files and collections with controlled vocabulary terms.




Note that we've yet to add search across vocabularies and automatic keyword extraction with MAUI and KEA.  These are available in HIVE, and we intend on adding them in this project.  

The next step is to build the capability to extract iRODS data and vocabulary terms and populate a triple store (Sesame or Jena), allowing queries on the triple-store, and allowing processing of results such that users can access the referenced data in iRODS.  We're seeking a generalized approach so that we can have a standard practice to store RDF statements about iRODS data, and we can index and manage real-time updates.  This aspect is next for the project, and should have a wide application for iRODS users!



Thursday, February 7, 2013

Packing Instructions

Folks often ask what Jargon actually does, and I usually say it's like a JDBC driver underneath a high-level object library. The JDBC driver part refers to the fact that iRODS has a wire-level protocol that communicates commands and data between client and server (and this same protocol works server-to-server, it's a grid!). Anyhow, inside Jargon, there is an org.irods.jargon.core.packinstr package that models iRODS packing instructions.

This low-level protocol handling is meant to be 'under the covers' so you never need to worry about it.  This is especially important because the protocols can change, and we might see future upgrade to use something like protobuf added.

At any rate, when developing Jargon implementations of the iRODS protocol, the actual procedure is to mine the C code, and hack at it until it works, via the creation of lots of unit tests.  Fancy...

In this endeavor, it's often helpful to see the actual protocol interactions of various icommands, and here's how you can do this too...

Simple open a shell, and export these variables:

export irodsProt=1; export irodsLogLevel=9;

Now as you execute your icommands, you'll be able to peek at the protocol operations going back and forth.  Be glad you don't really have to look at that, maybe you'll like Jargon now!

Thursday, January 31, 2013

A video demo of shares in iDrop web2

In a previous post, we talked about the addition of sharing to the Jargon libraries, and how that worked in code.

Here's a demo of sharing at work in the iDrop Web2 interface, and it shows how the AVU metadata and iRODS access control list (ACL) data works together to make shares.  It shows how a 'share' becomes a first-class object on the home page, and sort of like a 'drive' that can be mounted in a browse view.



Friday, January 25, 2013

Sharing

As we try and present this underlying complexity in interfaces and high-level API, certain themes  keeps repeating.  iRODS provides a fairly comprehensive set of tools to manage complex collections, including metadata management, automatic system metadata maintenance,  a global logical namespace, policy enforcement, ACL management, audit, and the like.

iRODS has a full ACL management system.  This includes the ability to federate iRODS zones and allow cross-zone access right management.  It's very powerful when combined with the audit log capability of the iRODS server.  iRODS has the mechanics covered...how can we take these elements and combine them in ways that create new services?  That's part of the fun of working on Jargon and iRODS user interfaces.  One thing that intrigues me is to look at iRODS as an application development platform from which new kinds of policy aware, distributed data applications are formed.  Sometimes the point is to look at merging iRODS middleware with other services (like Sesame and Lucene, as we're working on with the HIVE integration project, something we'll look at later).

A basic goal of Jargon and the iDrop projects is to create API and interfaces that can present iRODS as a familiar cloud-based data store, enhancing find-ability via social, metdata, and search capabilities.  We've leveraged iRODS AVU metadata to implement familiar 'tagging' as well as 'starred' folders and collections.  Those are reflected in the iDrop2 web interface, and we've done a few demos of this in prior posts.


Discovering and depicting shares has been a common use case.  In a way, you could do this by just querying collections or files in relation to their ACL settings, developing a list of shared files and collections.  The issue some had encountered is that iRODS collections can be BIG...lots of files, lots of deeply nested collections.  The queries get big, and the results that a user would get become incomprehensible in an interface.  The answer that we've cooked up with a few community folks is to develop a 'share' as a first class object.  That sounds fancy, but all it means is that, instead of treating a deeply nested collection of 10,000 files as 10,000 shared objects, why not just treat the parent collection as the share, and portray the contents of this share as a 'mount' you can make in an interface.  This way, you see 10 shares with meaningful names, instead of a pagable list of 5,000 files.  We just mixed the existing ACL system with  a new AVU entry very much like the starred folder.

So now, shares work like this:


  • Find a file or collection
  • Mark as 'shared'
    • set inheritance on collections
    • select users and the access rights you want
    • use the sharing service to mark as a share
  • Display the shares
    • We're using specific (SQL) query to develop more complex SQL queries to discover things like:
      • collections I'm sharing with others
      • collections shared with me
      • we'll add things like specifying users involved in the shares as we go
  • In iDrop web, you can now get a quick view (below) that can show things like 'files shared with me'.



As you can see, the 'home' landing page is presenting some of these 'overlays' on the AVU system.  In this case, the user has selected 'Folders shared with me", and the sharing service in Jargon we'll talk about runs the queries to inspect AVU metadata and ACL information to derive the list of shares.  Note that these are at the top level, and are portrayed using an alias, which is a share name that you give when the share is created.   

Note that clicking on the folder icon then sets that share as the top of the tree in the browse view, so a share becomes a 'mount' or a 'drive'.  The nice thing is that shares then get out of the way, and you are back to normal iRODS ACLs and collections, without a lot of cruft.  Here's what you see when you click on a folder in the share view...





What's cool is how unremarkable all of that is!  Note that the browse tree is looking into the share.  I've revealed the metadata tab so you can see the special iRODSUserTagging:Share tag that denotes the share.  Once a share is established, you can alter the rights to the share by just editing ACLs, so if the ACL is removed for a user, their share disappears.  I need to add a share button to this interface, and then we can show how to establish the share.  The code is done though...we'll look at that.

Code for sharing


Sharing is part of jargon-core, in the jargon-user-tagging subproject.  This is in the development branch of git, and as 3.3.0-SNAPSHOT, as of this writing.

Inside the org.irods.jargon.usertagging.sharing package, the key interface is the IRODSSharingService, which has the following interface:


package org.irods.jargon.usertagging.sharing;

import java.util.List;

import org.irods.jargon.core.exception.DataNotFoundException;
import org.irods.jargon.core.exception.FileNotFoundException;
import org.irods.jargon.core.exception.JargonException;
import org.irods.jargon.usertagging.domain.IRODSSharedFileOrCollection;
import org.irods.jargon.usertagging.domain.ShareUser;

/**
 * Service interface to create and manage shares. Like the star and tagging
 * facility, a share is a special metadata tag on a Collection or Data Object,
 * naming that item as shared. In the process of declaring the share, the proper
 * ACL settings are done.
 * * Sharing using a special tag at the 'root' of the share avoids representing
 * every file or collection in a deeply nested shared collection as 'shared', as
 * it would be based purely on the ACL settings. As a first class object, a
 * share can have an alias name, and is considered one unit.
 * 
 * @author Mike Conway - DICE (www.irods.org)
 * 
 */
public interface IRODSSharingService {

 /**
  * Create a new share. This will tag the top level as a shared collection
  * and data object, and set requisite AVUs for the provided set of users.
  * Note that collections will have recursive set of permissions, as well as
  * inheritance.
  * 
* Note that the share is only settable as originating from the file or * collection owner * * @param irodsSharedFileOrCollection * {@link IRODSSharedFileOrCollection} representing the share * @throws ShareAlreadyExistsException * if a share has already been defined * @throws FileNotFoundException * if the absolute path does not exist in iRODS * @throws JargonException */ void createShare(IRODSSharedFileOrCollection irodsSharedFileOrCollection) throws ShareAlreadyExistsException, FileNotFoundException, JargonException; /** * Given an absolute path to an iRODS file or collection, return the * IRODSSahredFileOrCollection that may exist. Note that * null is returned if no such share exists, and a * FileNotFoundException is returned if the absolute path does * not exist. * * @param irodsAbsolutePath * String with a valid iRODS absolute path to a file * or collection * @return {@link IRODSSharedFileOrCollection} or null * @throws FileNotFoundException * if the target absolute path does not exist in iRODS * @throws JargonException */ IRODSSharedFileOrCollection findShareByAbsolutePath(String irodsAbsolutePath) throws FileNotFoundException, JargonException; /** * Remove the share indicated at the given absolute path. Note that this method will silently ignore an occasion where a share does not * exist for the given path. *
* NOTE: an outstanding issue remains, which is how to handle the ACLs associated with the given file or collection. Right now the share goes away, * but the ACLs remain. It is under consideration to remove all ACLs, or add a flag or method variant that will either preserve or delete the associated * ACLs. * * @param irodsAbsolutePath String with a valid iRODS absolute path to a file * or collection * @throws FileNotFoundException if the iRODS absolute path does not point to a file or collection * @throws JargonException */ void removeShare(String irodsAbsolutePath) throws FileNotFoundException, JargonException; /** * Retrieve a list of collections shared by the given user and zone. No shares will return an empty set. *
* Note here that, for efficiency, the list of users (via theACLs) is not returned in this variant. It is intended that obtaining * the listing would be done as a separate request. A variant may be added later that does do this extra processing * @param userName String with the name of the user who is doing the sharing, based on the owner of the collection. * @param userZone String with the zone for the user. This may be set to blank, in which case the zone of the * logged in user will be used *
* Note that this method uses Specific Query, and the listSharedCollectionsOwnedByUser query alias must be provided. This can * be initialized by running a script in the jargon-user-tagging project to set up all required specific queries. See project documentation. * This method requires and iRODS server that supports Specific Query (iRODS 3.1+) * @return List of {@link IRODSSharedFileOrCollection} that is shared by the user * @throws JargonException */ List listSharedCollectionsOwnedByAUser( String userName, String userZone) throws JargonException; /** * Retrieve a list of collections shared with a given user by another user, as determined by the owner of that collection. *
* Note here that, for efficiency, the list of users (via theACLs) is not returned in this variant. It is intended that obtaining * the listing would be done as a separate request. A variant may be added later that does do this extra processing * @param userName String with the name of the user who is doing the sharing, based on the owner of the collection. * @param userZone String with the zone for the user. This may be set to blank, in which case the zone of the * logged in user will be used *
* Note that this method uses Specific Query, and the listSharedCollectionsSharedWithUser query alias must be provided. This can * be initialized by running a script in the jargon-user-tagging project to set up all required specific queries. See project documentation. * This method requires and iRODS server that supports Specific Query (iRODS 3.1+) * @return List of {@link IRODSSharedFileOrCollection} that is shared by a party with the user * @throws JargonException */ List listSharedCollectionsSharedWithUser( String userName, String userZone) throws JargonException; /** * Handy method to retrieve ACL share details for a share at the given absolute path. Note that if * there is no share, an empty list is returned. This seems to convey the message with the least amount of surprise. * * @param String with a valid iRODS absolute path to a file * or collection * @return List of {@link ShareUser} * @throws FileNotFoundException if the path cannot be found * @throws JargonException */ List listUsersForShare(String irodsAbsolutePath) throws FileNotFoundException, JargonException; /** * Update the name of the share at the given path * @param irodsAbsolutePath String with a valid iRODS absolute path to a file * or collection * @param newShareName String with the desired name of the share * @throws FileNotFoundException if the iRODS file or collection is missing * @throws DataNotFoundException if a current share is not found * @throws JargonException */ void updateShareName(String irodsAbsolutePath, String newShareName) throws FileNotFoundException, DataNotFoundException, JargonException; }








Note that there is a domain object that represents the share, the IRODSSharedFileOrCollection.  This object holds a path, an alias, and a list of users and their access rights desired for the share.  It's really that easy, you just call createShare() with the right parameters, then you call list* methods to get the shares back.  It handles ACLs, metadata, and inheritance of permissions.

The twist is that the queries are too complex for GenQuery, the built-in SQL-like syntax for querying the iRODS metadata catalog.  It uses Specific or SQL query, which is essentially like iRODS stored procedures, where you define a query with parameters via Jargon or the iadmin icommand.  We need to run a couple of shell scripts with iRODS rodsadmin privileges to enable specific query support.

First, Jargon itself has a few simple Specific query statements it relies on to support specific query, namely, it has methods to list and find specific queries stored in iRODS based on alias.  These can be set up by running the jargon-specquery.sh commands in the jargon-core library.  I think that these will soon be baked into iRODS, but as of 3.2 they are not yet there.

Next, we're in the process of developing the complex SQL required to list different sorts of shares, and these are being developed in the jargon-user-tagging subproject of jargon, and are provisioned by running the usertagging_specquery.sh.

Run those two scripts as rodsadmin and you should be good to go!  We're testing and integrating with iDrop web2, so it'll be a few weeks before the final API and queries are settled, but it's working now, and worth a try...see if it satisfies your use case!