]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java
Use bind authentication in LDAP
[lgpl/argeo-commons.git] / server / runtime / org.argeo.server.jcr / src / main / java / org / argeo / jcr / JcrUtils.java
index 6871e60c76a57a1f1479aff03003f246596315b9..77309feea0ad8e88c507b81f279184ac3b750373 100644 (file)
@@ -23,6 +23,7 @@ import java.text.ParseException;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -35,21 +36,32 @@ import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
+import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
 import javax.jcr.Session;
 import javax.jcr.Value;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryResult;
+import javax.jcr.query.qom.Constraint;
+import javax.jcr.query.qom.DynamicOperand;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+import javax.jcr.query.qom.Selector;
+import javax.jcr.query.qom.StaticOperand;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.ArgeoException;
 
 /** Utility methods to simplify common JCR operations. */
-public class JcrUtils {
+public class JcrUtils implements ArgeoJcrConstants {
        private final static Log log = LogFactory.getLog(JcrUtils.class);
 
+       /** Prevents instantiation */
+       private JcrUtils() {
+       }
+
        /**
         * Queries one single node.
         * 
@@ -112,9 +124,7 @@ public class JcrUtils {
                        URL u = new URL(url);
                        StringBuffer path = new StringBuffer(url.length());
                        // invert host
-                       String[] hostTokens = u.getHost().split("\\.");
-                       for (int i = hostTokens.length - 1; i >= 0; i--)
-                               path.append(hostTokens[i]).append('/');
+                       path.append(hostAsPath(u.getHost()));
                        // we don't put port since it may not always be there and may change
                        path.append(u.getPath());
                        return path.toString();
@@ -123,6 +133,21 @@ public class JcrUtils {
                }
        }
 
+       /**
+        * Creates a path from a FQDN, inverting the order of the component:
+        * www.argeo.org => org.argeo.www
+        */
+       public static String hostAsPath(String host) {
+               StringBuffer path = new StringBuffer(host.length());
+               String[] hostTokens = host.split("\\.");
+               for (int i = hostTokens.length - 1; i >= 0; i--) {
+                       path.append(hostTokens[i]);
+                       if (i != 0)
+                               path.append('/');
+               }
+               return path.toString();
+       }
+
        /**
         * The provided data as a path ('/' at the end, not the beginning)
         * 
@@ -171,13 +196,6 @@ public class JcrUtils {
 
        }
 
-       /** Converts the FQDN of an host into a path (converts '.' into '/'). */
-       public static String hostAsPath(String host) {
-               // TODO : inverse order of the elements (to have org/argeo/test IO
-               // test/argeo/org
-               return host.replace('.', '/');
-       }
-
        /** The last element of a path. */
        public static String lastPathElement(String path) {
                if (path.charAt(path.length() - 1) == '/')
@@ -212,7 +230,10 @@ public class JcrUtils {
                return mkdirs(session, path, type, null, false);
        }
 
-       /** Creates the nodes making path, if they don't exist. */
+       /**
+        * Creates the nodes making path, if they don't exist. This is up to the
+        * caller to save the session.
+        */
        public static Node mkdirs(Session session, String path, String type,
                        String intermediaryNodeType, Boolean versioning) {
                try {
@@ -254,7 +275,7 @@ public class JcrUtils {
                                        currentNode = (Node) session.getItem(current.toString());
                                }
                        }
-                       session.save();
+                       // session.save();
                        return currentNode;
                } catch (RepositoryException e) {
                        throw new ArgeoException("Cannot mkdirs " + path, e);
@@ -540,9 +561,184 @@ public class JcrUtils {
                return name.replace(':', '_');
        }
 
+       /** Cleanly disposes a {@link Binary} even if it is null. */
        public static void closeQuietly(Binary binary) {
                if (binary == null)
                        return;
                binary.dispose();
        }
+
+       /**
+        * Creates depth from a string (typically a username) by adding levels based
+        * on its first characters: "aBcD",2 => a/aB
+        */
+       public static String firstCharsToPath(String str, Integer nbrOfChars) {
+               if (str.length() < nbrOfChars)
+                       throw new ArgeoException("String " + str
+                                       + " length must be greater or equal than " + nbrOfChars);
+               StringBuffer path = new StringBuffer("");
+               StringBuffer curr = new StringBuffer("");
+               for (int i = 0; i < nbrOfChars; i++) {
+                       curr.append(str.charAt(i));
+                       path.append(curr);
+                       if (i < nbrOfChars - 1)
+                               path.append('/');
+               }
+               return path.toString();
+       }
+
+       /**
+        * Wraps the call to the repository factory based on parameter
+        * {@link ArgeoJcrConstants#JCR_REPOSITORY_ALIAS} in order to simplify it
+        * and protect against future API changes.
+        */
+       public static Repository getRepositoryByAlias(
+                       RepositoryFactory repositoryFactory, String alias) {
+               try {
+                       Map<String, String> parameters = new HashMap<String, String>();
+                       parameters.put(JCR_REPOSITORY_ALIAS, alias);
+                       return repositoryFactory.getRepository(parameters);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException(
+                                       "Unexpected exception when trying to retrieve repository with alias "
+                                                       + alias, e);
+               }
+       }
+
+       /**
+        * Wraps the call to the repository factory based on parameter
+        * {@link ArgeoJcrConstants#JCR_REPOSITORY_URI} in order to simplify it and
+        * protect against future API changes.
+        */
+       public static Repository getRepositoryByUri(
+                       RepositoryFactory repositoryFactory, String uri) {
+               try {
+                       Map<String, String> parameters = new HashMap<String, String>();
+                       parameters.put(JCR_REPOSITORY_URI, uri);
+                       return repositoryFactory.getRepository(parameters);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException(
+                                       "Unexpected exception when trying to retrieve repository with uri "
+                                                       + uri, e);
+               }
+       }
+
+       /**
+        * Discards the current changes in a session by calling
+        * {@link Session#refresh(boolean)} with <code>false</code>, only logging
+        * potential errors when doing so. To be used typically in a catch block.
+        */
+       public static void discardQuietly(Session session) {
+               try {
+                       if (session != null)
+                               session.refresh(false);
+               } catch (RepositoryException e) {
+                       log.warn("Cannot quietly discard session " + session + ": "
+                                       + e.getMessage());
+               }
+       }
+
+       /** Logs out the session, not throwing any exception, even if it is null. */
+       public static void logoutQuietly(Session session) {
+               if (session != null)
+                       session.logout();
+       }
+
+       /** Returns the home node of the session user or null if none was found. */
+       public static Node getUserHome(Session session) {
+               String userID = session.getUserID();
+               return getUserHome(session, userID);
+       }
+
+       /** Get the profile of the user attached to this session. */
+       public static Node getUserProfile(Session session) {
+               String userID = session.getUserID();
+               return getUserProfile(session, userID);
+       }
+
+       /**
+        * Returns the home node of the session user or null if none was found.
+        * 
+        * @param session
+        *            the session to use in order to perform the search, this can be
+        *            a session with a different user ID than the one searched,
+        *            typically when a system or admin session is used.
+        * @param userID
+        *            the id of the user
+        */
+       public static Node getUserHome(Session session, String userID) {
+               try {
+                       QueryObjectModelFactory qomf = session.getWorkspace()
+                                       .getQueryManager().getQOMFactory();
+
+                       // query the user home for this user id
+                       Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_HOME,
+                                       "userHome");
+                       DynamicOperand userIdDop = qomf.propertyValue("userHome",
+                                       ArgeoNames.ARGEO_USER_ID);
+                       StaticOperand userIdSop = qomf.literal(session.getValueFactory()
+                                       .createValue(userID));
+                       Constraint constraint = qomf.comparison(userIdDop,
+                                       QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
+                       Query query = qomf.createQuery(userHomeSel, constraint, null, null);
+                       Node userHome = JcrUtils.querySingleNode(query);
+                       return userHome;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot find home for user " + userID, e);
+               }
+       }
+
+       public static Node getUserProfile(Session session, String userID) {
+               try {
+                       QueryObjectModelFactory qomf = session.getWorkspace()
+                                       .getQueryManager().getQOMFactory();
+                       Selector sel = qomf.selector(ArgeoTypes.ARGEO_USER_PROFILE,
+                                       "userProfile");
+                       DynamicOperand userIdDop = qomf.propertyValue("userProfile",
+                                       ArgeoNames.ARGEO_USER_ID);
+                       StaticOperand userIdSop = qomf.literal(session.getValueFactory()
+                                       .createValue(userID));
+                       Constraint constraint = qomf.comparison(userIdDop,
+                                       QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
+                       Query query = qomf.createQuery(sel, constraint, null, null);
+                       Node userHome = JcrUtils.querySingleNode(query);
+                       return userHome;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot find profile for user " + userID,
+                                       e);
+               }
+       }
+
+       public static Node createUserHome(Session session, String homeBasePath,
+                       String username) {
+               try {
+                       if (session.hasPendingChanges())
+                               throw new ArgeoException(
+                                               "Session has pending changes, save them first");
+                       String homePath = homeBasePath + '/'
+                                       + firstCharsToPath(username, 2) + '/' + username;
+                       Node userHome = JcrUtils.mkdirs(session, homePath);
+
+                       Node userProfile = userHome.addNode(ArgeoNames.ARGEO_PROFILE);
+                       userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
+                       userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                       session.save();
+                       // we need to save the profile before adding the user home type
+                       PropertyIterator pit = userHome.getProperties();
+                       while (pit.hasNext()) {
+                               Property p = pit.nextProperty();
+                               log.debug(p.getName() + "=" + p.getValue().getString());
+                       }
+                       userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
+                       // see
+                       // http://jackrabbit.510166.n4.nabble.com/Jackrabbit-2-0-beta-6-Problem-adding-a-Mixin-type-with-mandatory-properties-after-setting-propertiesn-td1290332.html
+                       userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                       session.save();
+                       return userHome;
+               } catch (RepositoryException e) {
+                       discardQuietly(session);
+                       throw new ArgeoException("Cannot create home node for user "
+                                       + username, e);
+               }
+       }
 }