X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=server%2Fruntime%2Forg.argeo.server.jcr%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fjcr%2FJcrUtils.java;h=f530f170b7fe4a0637d2eb72b75b4470dba07199;hb=2e256eea0004998d101b2df989cae4f419b74032;hp=72b94fbcd0590baef6c4b30049282dd61cdc0ae0;hpb=03db65bd74ce09b696a4c5af15a58df988e5368d;p=lgpl%2Fargeo-commons.git diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java index 72b94fbcd..f530f170b 100644 --- a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java +++ b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java @@ -41,7 +41,9 @@ import javax.jcr.RepositoryException; import javax.jcr.RepositoryFactory; import javax.jcr.Session; import javax.jcr.Value; +import javax.jcr.Workspace; import javax.jcr.nodetype.NodeType; +import javax.jcr.observation.EventListener; import javax.jcr.query.Query; import javax.jcr.query.QueryResult; import javax.jcr.query.qom.Constraint; @@ -365,11 +367,16 @@ public class JcrUtils implements ArgeoJcrConstants { } /** - * Copies recursively the content of a node to another one. Mixin are NOT - * copied. + * Copies recursively the content of a node to another one. Do NOT copy the + * property values of {@link NodeType#MIX_CREATED} and + * {@link NodeType#MIX_LAST_MODIFIED}, but update the + * {@link Property#JCR_LAST_MODIFIED} and + * {@link Property#JCR_LAST_MODIFIED_BY} properties if the target node has + * the {@link NodeType#MIX_LAST_MODIFIED} mixin. */ public static void copy(Node fromNode, Node toNode) { try { + // process properties PropertyIterator pit = fromNode.getProperties(); properties: while (pit.hasNext()) { Property fromProperty = pit.nextProperty(); @@ -379,10 +386,32 @@ public class JcrUtils implements ArgeoJcrConstants { .isProtected()) continue properties; - toNode.setProperty(fromProperty.getName(), - fromProperty.getValue()); + if (fromProperty.getDefinition().isProtected()) + continue properties; + + if (propertyName.equals("jcr:created") + || propertyName.equals("jcr:createdBy") + || propertyName.equals("jcr:lastModified") + || propertyName.equals("jcr:lastModifiedBy")) + continue properties; + + if (fromProperty.isMultiple()) { + toNode.setProperty(propertyName, fromProperty.getValues()); + } else { + toNode.setProperty(propertyName, fromProperty.getValue()); + } + } + + // update jcr:lastModified and jcr:lastModifiedBy in toNode in case + // they existed, before adding the mixins + updateLastModified(toNode); + + // add mixins + for (NodeType mixinType : fromNode.getMixinNodeTypes()) { + toNode.addMixin(mixinType.getName()); } + // process children nodes NodeIterator nit = fromNode.getNodes(); while (nit.hasNext()) { Node fromChild = nit.nextNode(); @@ -623,6 +652,21 @@ public class JcrUtils implements ArgeoJcrConstants { } } + /** + * Discards the current changes in the session attached to this node. To be + * used typically in a catch block. + * + * @see #discardQuietly(Session) + */ + public static void discardUnderlyingSessionQuietly(Node node) { + try { + discardQuietly(node.getSession()); + } catch (RepositoryException e) { + log.warn("Cannot quietly discard session of node " + node + ": " + + e.getMessage()); + } + } + /** * Discards the current changes in a session by calling * {@link Session#refresh(boolean)} with false, only logging @@ -650,6 +694,30 @@ public class JcrUtils implements ArgeoJcrConstants { return getUserHome(session, userID); } + /** + * Returns user home has path, embedding exceptions. Contrary to + * {@link #getUserHome(Session)}, it never returns null but throws and + * exception if not found. + */ + public static String getUserHomePath(Session session) { + String userID = session.getUserID(); + try { + Node userHome = getUserHome(session, userID); + if (userHome != null) + return userHome.getPath(); + else + throw new ArgeoException("No home registered for " + userID); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot find user home path", e); + } + } + + /** 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. * @@ -657,10 +725,10 @@ public class JcrUtils implements ArgeoJcrConstants { * 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 + * @param username + * the username of the user */ - public static Node getUserHome(Session session, String userID) { + public static Node getUserHome(Session session, String username) { try { QueryObjectModelFactory qomf = session.getWorkspace() .getQueryManager().getQOMFactory(); @@ -671,14 +739,119 @@ public class JcrUtils implements ArgeoJcrConstants { DynamicOperand userIdDop = qomf.propertyValue("userHome", ArgeoNames.ARGEO_USER_ID); StaticOperand userIdSop = qomf.literal(session.getValueFactory() - .createValue(userID)); + .createValue(username)); 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); + throw new ArgeoException("Cannot find home for user " + username, e); + } + } + + public static Node getUserProfile(Session session, String username) { + 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(username)); + 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 " + username, e); + } + } + + /** Creates an Argeo user home. */ + public static Node createUserHome(Session session, String homeBasePath, + String username) { + try { + if (session == null) + throw new ArgeoException("Session is null"); + 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 + 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); + } + } + + /** + * Quietly unregisters an {@link EventListener} from the udnerlying + * workspace of this node. + */ + public static void unregisterQuietly(Node node, EventListener eventListener) { + try { + unregisterQuietly(node.getSession().getWorkspace(), eventListener); + } catch (RepositoryException e) { + // silent + if (log.isTraceEnabled()) + log.trace("Could not unregister event listener " + + eventListener); + } + } + + /** Quietly unregisters an {@link EventListener} from this workspace */ + public static void unregisterQuietly(Workspace workspace, + EventListener eventListener) { + if (eventListener == null) + return; + try { + workspace.getObservationManager() + .removeEventListener(eventListener); + } catch (RepositoryException e) { + // silent + if (log.isTraceEnabled()) + log.trace("Could not unregister event listener " + + eventListener); + } + } + + /** + * If this node is has the {@link NodeType#MIX_LAST_MODIFIED} mixin, it + * updates the {@link Property#JCR_LAST_MODIFIED} property with the current + * time and the {@link Property#JCR_LAST_MODIFIED_BY} property with the + * underlying session user id. In Jackrabbit 2.x, these properties + * are not automatically updated, hence the need for manual update. The + * session is not saved. + */ + public static void updateLastModified(Node node) { + try { + if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) { + node.setProperty(Property.JCR_LAST_MODIFIED, + new GregorianCalendar()); + node.setProperty(Property.JCR_LAST_MODIFIED_BY, node + .getSession().getUserID()); + } + } catch (RepositoryException e) { + throw new ArgeoException("Cannot update last modified", e); } } }