X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=server%2Fruntime%2Forg.argeo.server.jackrabbit%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fjcr%2FBeanNodeMapper.java;h=a526bcf5ec0d06ed838392baa08aabfbccd38bb1;hb=946518ac41ee428e6cfd27b8d8b7e92b6d38470c;hp=d05af407f33e3996e696c871bf0d146746868588;hpb=1f8e265227896228ced3d39285abaee99bb97e5e;p=lgpl%2Fargeo-commons.git diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jcr/BeanNodeMapper.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jcr/BeanNodeMapper.java index d05af407f..a526bcf5e 100644 --- a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jcr/BeanNodeMapper.java +++ b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jcr/BeanNodeMapper.java @@ -22,14 +22,12 @@ import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.ValueFactory; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.argeo.ArgeoException; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; -public class BeanNodeMapper { - private final static Log log = LogFactory.getLog(BeanNodeMapper.class); +public class BeanNodeMapper implements NodeMapper { + // private final static Log log = LogFactory.getLog(BeanNodeMapper.class); private final static String NODE_VALUE = "value"; @@ -39,54 +37,93 @@ public class BeanNodeMapper { private Boolean versioning = false; private Boolean strictUuidReference = false; + + // TODO define a primaryNodeType Strategy private String primaryNodeType = null; private ClassLoader classLoader = getClass().getClassLoader(); - public String storagePath(Object obj) { - String clss = obj.getClass().getName(); - StringBuffer buf = new StringBuffer("/objects/"); - StringTokenizer st = new StringTokenizer(clss, "."); - while (st.hasMoreTokens()) { - buf.append(st.nextToken()).append('/'); - } - buf.append(obj.toString()); - return buf.toString(); - } + private NodeMapperProvider nodeMapperProvider; - public Node save(Session session, Object obj) { - return save(session, storagePath(obj), obj); + /** + * exposed method to retrieve a bean from a node + */ + public Object load(Node node) { + try { + if (nodeMapperProvider != null) { + NodeMapper nodeMapper = nodeMapperProvider.findNodeMapper(node); + if (nodeMapper != this) { + return nodeMapper.load(node); + } + } + return nodeToBean(node); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot load object from node " + node, e); + } } + /** Update an existing node with an object */ public void update(Node node, Object obj) { try { - beanToNode(createBeanWrapper(obj), node); + if (nodeMapperProvider != null) { + + NodeMapper nodeMapper = nodeMapperProvider.findNodeMapper(node); + if (nodeMapper != this) { + nodeMapper.update(node, obj); + } else + beanToNode(createBeanWrapper(obj), node); + } else + beanToNode(createBeanWrapper(obj), node); } catch (RepositoryException e) { throw new ArgeoException("Cannot update node " + node + " with " + obj, e); } } + /** + * if no storage path is given; we use canonical path + * + * @see this.storagePath() + */ + public Node save(Session session, Object obj) { + return save(session, storagePath(obj), obj); + } + + /** + * Create a new node to store an object. If the parentNode doesn't exist, it + * is created + * + * the primaryNodeType may be initialized before + */ public Node save(Session session, String path, Object obj) { try { - BeanWrapper beanWrapper = createBeanWrapper(obj); final Node node; String parentPath = JcrUtils.parentPath(path); // find or create parent node Node parentNode; if (session.itemExists(path)) parentNode = (Node) session.getItem(parentPath); - else + else { parentNode = JcrUtils.mkdirs(session, parentPath, null, versioning); + } // create node + if (primaryNodeType != null) node = parentNode.addNode(JcrUtils.lastPathElement(path), primaryNodeType); else node = parentNode.addNode(JcrUtils.lastPathElement(path)); - beanToNode(beanWrapper, node); + // Check specific cases + if (nodeMapperProvider != null) { + NodeMapper nodeMapper = nodeMapperProvider.findNodeMapper(node); + if (nodeMapper != this) { + nodeMapper.update(node, obj); + return node; + } + } + update(node, obj); return node; } catch (ArgeoException e) { throw e; @@ -96,136 +133,163 @@ public class BeanNodeMapper { } } - @SuppressWarnings("unchecked") - public Object nodeToBean(Node node) throws RepositoryException { - - String clssName = node.getProperty(classProperty).getValue() - .getString(); - - if (log.isTraceEnabled()) - log.debug("Map node " + node.getPath() + " to bean " + clssName); - - BeanWrapper beanWrapper = createBeanWrapper(loadClass(clssName)); - - // process properties - PropertyIterator propIt = node.getProperties(); - props: while (propIt.hasNext()) { - Property prop = propIt.nextProperty(); - if (!beanWrapper.isWritableProperty(prop.getName())) - continue props; - - PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(prop - .getName()); - Class propClass = pd.getPropertyType(); - - // list - if (propClass != null && List.class.isAssignableFrom(propClass)) { - List lst = new ArrayList(); - Class valuesClass = classFromProperty(prop); - if (valuesClass != null) - for (Value value : prop.getValues()) { - lst.add(asObject(value, valuesClass)); - } - continue props; - } - - Object value = asObject(prop.getValue(), pd.getPropertyType()); - if (value != null) - beanWrapper.setPropertyValue(prop.getName(), value); + /** + * Parse the FQN of a class to string with '/' delimiters Prefix the + * returned string with "/objects/" + */ + public String storagePath(Object obj) { + String clss = obj.getClass().getName(); + StringBuffer buf = new StringBuffer("/objects/"); + StringTokenizer st = new StringTokenizer(clss, "."); + while (st.hasMoreTokens()) { + buf.append(st.nextToken()).append('/'); } + buf.append(obj.toString()); + return buf.toString(); + } - // process children nodes - NodeIterator nodeIt = node.getNodes(); - nodes: while (nodeIt.hasNext()) { - Node childNode = nodeIt.nextNode(); - String name = childNode.getName(); - if (!beanWrapper.isWritableProperty(name)) - continue nodes; - - PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(name); - Class propClass = pd.getPropertyType(); - - if (propClass != null && List.class.isAssignableFrom(propClass)) { - String lstClass = childNode.getProperty(classProperty) - .getString(); - List lst; - try { - lst = (List) loadClass(lstClass).newInstance(); - } catch (Exception e) { - lst = new ArrayList(); - } + @SuppressWarnings("unchecked") + /** + * Transforms a node into an object of the class defined by classProperty Property + */ + protected Object nodeToBean(Node node) throws RepositoryException { - NodeIterator valuesIt = childNode.getNodes(); - while (valuesIt.hasNext()) { - Node lstValueNode = valuesIt.nextNode(); - Object lstValue = nodeToBean(lstValueNode); - lst.add(lstValue); + try { + String clssName = node.getProperty(classProperty).getValue() + .getString(); + + BeanWrapper beanWrapper = createBeanWrapper(loadClass(clssName)); + + // process properties + PropertyIterator propIt = node.getProperties(); + props: while (propIt.hasNext()) { + Property prop = propIt.nextProperty(); + if (!beanWrapper.isWritableProperty(prop.getName())) + continue props; + + PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(prop + .getName()); + Class propClass = pd.getPropertyType(); + + // Process case of List and its derived classes + // primitive list + if (propClass != null && List.class.isAssignableFrom(propClass)) { + List lst = new ArrayList(); + Class valuesClass = classFromProperty(prop); + if (valuesClass != null) + for (Value value : prop.getValues()) { + lst.add(asObject(value, valuesClass)); + } + continue props; } - beanWrapper.setPropertyValue(name, lst); - continue nodes; + // Case of other type of property accepted by jcr + // Long, Double, String, Binary, Date, Boolean, Name + Object value = asObject(prop.getValue(), pd.getPropertyType()); + if (value != null) + beanWrapper.setPropertyValue(prop.getName(), value); } - if (propClass != null && Map.class.isAssignableFrom(propClass)) { - String mapClass = childNode.getProperty(classProperty) - .getString(); - Map map; - try { - map = (Map) loadClass(mapClass) - .newInstance(); - } catch (Exception e) { - map = new HashMap(); - } + // process children nodes + NodeIterator nodeIt = node.getNodes(); + nodes: while (nodeIt.hasNext()) { + Node childNode = nodeIt.nextNode(); + String name = childNode.getName(); + if (!beanWrapper.isWritableProperty(name)) + continue nodes; + + PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(name); + Class propClass = pd.getPropertyType(); + + // objects list + if (propClass != null && List.class.isAssignableFrom(propClass)) { + String lstClass = childNode.getProperty(classProperty) + .getString(); + List lst; + try { + lst = (List) loadClass(lstClass).newInstance(); + } catch (Exception e) { + lst = new ArrayList(); + } - // properties - PropertyIterator keysPropIt = childNode.getProperties(); - keyProps: while (keysPropIt.hasNext()) { - Property keyProp = keysPropIt.nextProperty(); - // FIXME: use property editor - String key = keyProp.getName(); - if (classProperty.equals(key)) - continue keyProps; - - Class keyPropClass = classFromProperty(keyProp); - if (keyPropClass != null) { - Object mapValue = asObject(keyProp.getValue(), - keyPropClass); - map.put(key, mapValue); + NodeIterator valuesIt = childNode.getNodes(); + while (valuesIt.hasNext()) { + Node lstValueNode = valuesIt.nextNode(); + Object lstValue = nodeToBean(lstValueNode); + lst.add(lstValue); } + + beanWrapper.setPropertyValue(name, lst); + continue nodes; } - // node - NodeIterator keysIt = childNode.getNodes(); - while (keysIt.hasNext()) { - Node mapValueNode = keysIt.nextNode(); - // FIXME: use property editor - Object key = mapValueNode.getName(); + // objects map + if (propClass != null && Map.class.isAssignableFrom(propClass)) { + String mapClass = childNode.getProperty(classProperty) + .getString(); + Map map; + try { + map = (Map) loadClass(mapClass) + .newInstance(); + } catch (Exception e) { + map = new HashMap(); + } + + // properties + PropertyIterator keysPropIt = childNode.getProperties(); + keyProps: while (keysPropIt.hasNext()) { + Property keyProp = keysPropIt.nextProperty(); + // FIXME: use property editor + String key = keyProp.getName(); + if (classProperty.equals(key)) + continue keyProps; + + Class keyPropClass = classFromProperty(keyProp); + if (keyPropClass != null) { + Object mapValue = asObject(keyProp.getValue(), + keyPropClass); + map.put(key, mapValue); + } + } + + // node + NodeIterator keysIt = childNode.getNodes(); + while (keysIt.hasNext()) { + Node mapValueNode = keysIt.nextNode(); + // FIXME: use property editor + Object key = mapValueNode.getName(); - Object mapValue = nodeToBean(mapValueNode); + Object mapValue = nodeToBean(mapValueNode); - map.put(key, mapValue); + map.put(key, mapValue); + } + beanWrapper.setPropertyValue(name, map); + continue nodes; } - beanWrapper.setPropertyValue(name, map); - continue nodes; - } - // default - Object value = nodeToBean(childNode); - beanWrapper.setPropertyValue(name, value); + // default + Object value = nodeToBean(childNode); + beanWrapper.setPropertyValue(name, value); + } + return beanWrapper.getWrappedInstance(); + } catch (Exception e) { + throw new ArgeoException("Cannot map node " + node, e); } - return beanWrapper.getWrappedInstance(); } + /** + * Transforms an object to the specified jcr Node in order to persist it. + * + * @param beanWrapper + * @param node + * @throws RepositoryException + */ protected void beanToNode(BeanWrapper beanWrapper, Node node) throws RepositoryException { - if (log.isTraceEnabled()) - log.debug("Map bean to node " + node.getPath()); - properties: for (PropertyDescriptor pd : beanWrapper .getPropertyDescriptors()) { String name = pd.getName(); - if (!beanWrapper.isReadableProperty(name)) continue properties;// skip @@ -255,6 +319,7 @@ public class BeanNodeMapper { continue properties; } + // Some bean reference other classes. We must deal with this case if (value instanceof Class) { node.setProperty(name, ((Class) value).getName()); continue properties; @@ -298,6 +363,14 @@ public class BeanNodeMapper { } } + /** + * Process specific case of list + * + * @param node + * @param name + * @param lst + * @throws RepositoryException + */ protected void addList(Node node, String name, List lst) throws RepositoryException { if (node.hasNode(name)) {// update @@ -336,6 +409,14 @@ public class BeanNodeMapper { } } + /** + * Process specific case of maps. + * + * @param node + * @param name + * @param map + * @throws RepositoryException + */ protected void addMap(Node node, String name, Map map) throws RepositoryException { if (node.hasNode(name)) {// update @@ -473,7 +554,7 @@ public class BeanNodeMapper { } protected Class loadClass(String name) { - //log.debug("Class loader: " + classLoader); + // log.debug("Class loader: " + classLoader); try { return classLoader.loadClass(name); } catch (ClassNotFoundException e) { @@ -509,4 +590,15 @@ public class BeanNodeMapper { this.classLoader = classLoader; } + public void setNodeMapperProvider(NodeMapperProvider nodeMapperProvider) { + this.nodeMapperProvider = nodeMapperProvider; + } + + public String getPrimaryNodeType() { + return this.primaryNodeType; + } + + public String getClassProperty() { + return this.classProperty; + } }