Java tutorial
/* * Copyright 2017 Open Web IT B.V. (https://www.openweb.nl/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package nl.openweb.jcr; import nl.openweb.jcr.domain.NodeBean; import nl.openweb.jcr.domain.PropertyBean; import nl.openweb.jcr.json.JsonUtils; import nl.openweb.jcr.utils.NodeTypeUtils; import nl.openweb.jcr.utils.PathUtils; import nl.openweb.jcr.utils.ReflectionUtils; import org.apache.commons.beanutils.ConvertUtilsBean; import javax.jcr.*; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.*; import static nl.openweb.jcr.utils.ReflectionUtils.unwrapNodeDecorator; /** * Created by Ebrahim on 5/20/2017. */ public class Importer { public static final String JCR_PRIMARY_TYPE = "jcr:primaryType"; public static final String JCR_MIXIN_TYPES = "jcr:mixinTypes"; public static final String JCR_UUID = "jcr:uuid"; private final Set<String> protectedProperties; private final boolean setProtectedProperties; private final boolean saveSession; private final boolean addMixins; private final boolean addUuid; private final boolean addUnknownTypes; private final Node rootNode; private JAXBContext jaxbContext; private Importer(Builder builder) { try { this.addMixins = builder.addMixins; this.rootNode = builder.rootNodeSupplier.get(); this.addUuid = builder.addUuid; this.setProtectedProperties = builder.setProtectedProperties; this.saveSession = builder.saveSession; this.addUnknownTypes = builder.addUnknownTypes; HashSet<String> set = new HashSet<>(); set.add(JCR_PRIMARY_TYPE); set.add(JCR_MIXIN_TYPES); set.add(JCR_UUID); this.protectedProperties = Collections.unmodifiableSet(set); this.jaxbContext = JAXBContext.newInstance(NodeBean.class, PropertyBean.class); } catch (Exception e) { throw new JcrImporterException(e.getMessage(), e); } } public Node createNodesFromJson(String json) { return createNodesFromJson(json, null, null); } public Node createNodesFromJson(String json, String path) { return createNodesFromJson(json, path, null); } public Node createNodesFromJson(String json, String path, String intermediateNodeType) { try { return createNodeFromNodeBean(JsonUtils.parseJsonMap(json), path, intermediateNodeType); } catch (IOException e) { throw new JcrImporterException(e.getMessage(), e); } } public Node createNodesFromJson(InputStream inputStream) { return createNodesFromJson(inputStream, null, null); } public Node createNodesFromJson(InputStream inputStream, String path) { return createNodesFromJson(inputStream, path, null); } public Node createNodesFromJson(InputStream inputStream, String path, String intermediateNodeType) { try { validate(inputStream); return createNodeFromNodeBean(JsonUtils.parseJsonMap(inputStream), path, intermediateNodeType); } catch (IOException e) { throw new JcrImporterException(e.getMessage(), e); } } public Node createNodesFromXml(String xml) { return createNodesFromXml(xml, null); } public Node createNodesFromXml(String xml, String path) { return createNodesFromXml(xml, path, null); } public Node createNodesFromXml(String xml, String path, String intermediateNodeType) { return this.createNodesFromXml(new ByteArrayInputStream(xml.getBytes()), path, intermediateNodeType); } public Node createNodesFromXml(InputStream inputStream) { return createNodesFromXml(inputStream, null, null); } public Node createNodesFromXml(InputStream inputStream, String path) { return createNodesFromXml(inputStream, path, null); } public Node createNodesFromXml(InputStream inputStream, String path, String intermediateNodeType) { try { validate(inputStream); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); Object unmarshaled = unmarshaller.unmarshal(inputStream); if (unmarshaled instanceof NodeBean) { return createNodeFromNodeBean(NodeBeanUtils.nodeBeanToMap((NodeBean) unmarshaled), path, intermediateNodeType); } else { throw new JcrImporterException("The given XML file is not of the right format"); } } catch (JAXBException e) { throw new JcrImporterException(e.getMessage(), e); } } private void validate(InputStream inputStream) { if (inputStream == null) { throw new JcrImporterException("InputSteam may not be null."); } } private Node createNodeFromNodeBean(Map<String, Object> map, String path, String intermediateNodeType) { try { Node node = getOrCreateNode(rootNode, path, intermediateNodeType, map); updateNode(node, map); return rootNode; } catch (Exception e) { throw new JcrImporterException(e.getMessage(), e); } } private Node getOrCreateNode(Node rootNode, String path, String intermediateNodeType, Map<String, Object> map) { Node result = rootNode; if (path != null && !path.isEmpty()) { String[] nodes = PathUtils.normalizePath(path).split("/"); for (int i = 0; i < nodes.length; i++) { String n = nodes[i]; String nodeType = i + 1 == nodes.length && map.containsKey(JCR_PRIMARY_TYPE) ? getPrimaryType(map) : intermediateNodeType; result = getOrCreateNode(nodeType, result, n, i + 1 == nodes.length ? (String) map.get(JCR_UUID) : null); } } return result; } private Node getOrCreateNode(String nodeType, Node node, String name, String uuid) { try { Node result; if (node.hasNode(name)) { result = node.getNode(name); } else if (nodeType != null && !nodeType.isEmpty()) { if (addUnknownTypes) { NodeTypeUtils.createNodeType(node.getSession(), nodeType); } result = addSubnodeWithPrimaryType(node, name, nodeType, uuid); } else { result = addSubnodeWithoutPrimaryType(node, name, uuid); } return result; } catch (RepositoryException e) { throw new JcrImporterException(e.getMessage(), e); } } private void updateNode(Node node, Map<String, Object> map) throws RepositoryException { for (Map.Entry<String, Object> entry : map.entrySet()) { if (NodeBeanUtils.isProperty(entry)) { addProperty(node, entry); } else { addSubnode(node, entry.getKey(), entry.getValue()); } } if (saveSession) { node.getSession().save(); } } private void addSubnode(Node node, String name, Object obj) throws RepositoryException { Node subNode; if (obj instanceof Map) { Map map = (Map) obj; if (map.containsKey(JCR_PRIMARY_TYPE) && !"".equals(map.get(JCR_PRIMARY_TYPE))) { subNode = addSubnodeWithPrimaryType(node, name, getPrimaryType(map), (String) map.get(JCR_UUID)); } else { subNode = addSubnodeWithoutPrimaryType(node, name, (String) map.get(JCR_UUID)); } updateNode(subNode, map); } else if (obj instanceof Collection) { for (Object item : (Collection) obj) { addSubnode(node, name, item); } } } private Node addSubnodeWithoutPrimaryType(Node node, String name, String uuid) throws RepositoryException { Node subNode; Node realNode = unwrapNodeDecorator(node); Method method = ReflectionUtils.getMethod(realNode, "addNodeWithUuid", String.class, String.class); if (addUuid && uuid != null && method != null) { subNode = (Node) ReflectionUtils.invokeMethod(method, realNode, name, uuid); } else { subNode = realNode.addNode(name); } return subNode; } private Node addSubnodeWithPrimaryType(Node node, String name, String nodeTypeName, String uuid) throws RepositoryException { Node subNode; if (addUnknownTypes) { NodeTypeUtils.createNodeType(node.getSession(), nodeTypeName); if (name.contains(":")) { NodeTypeUtils.getOrRegisterNamespace(node.getSession(), name); } } Node realNode = unwrapNodeDecorator(node); Method method = ReflectionUtils.getMethod(realNode, "addNodeWithUuid", String.class, String.class, String.class); if (addUuid && uuid != null && method != null) { subNode = (Node) ReflectionUtils.invokeMethod(method, realNode, name, nodeTypeName, uuid); } else { subNode = realNode.addNode(name, nodeTypeName); } return subNode; } private String getPrimaryType(Map map) { String result; Object value = map.get(JCR_PRIMARY_TYPE); if (value instanceof Map && ((Map) value).containsKey("value")) { result = ((Map) value).get("value").toString(); } else { result = value.toString(); } return result; } private void addProperty(Node node, Map.Entry<String, Object> entry) throws RepositoryException { String name = entry.getKey(); if (addUnknownTypes) { NodeTypeUtils.getOrRegisterNamespace(node.getSession(), name); } Object value = entry.getValue(); if (value instanceof Collection) { addMultivalueProperty(node, entry, name); } else if (NodeBeanUtils.isValueNotNull(value)) { addSingleValueProperty(node, entry, name); } } private void addSingleValueProperty(Node node, Map.Entry<String, Object> entry, String name) throws RepositoryException { Value jcrValue = toJcrValue(node.getSession(), entry.getValue(), name); if ("jcr:uuid".equals(name) && addUuid) { setUuid(node, jcrValue.getString()); } if (JCR_MIXIN_TYPES.equals(name) && addMixins) { String mixinName = jcrValue.getString(); if (addUnknownTypes) { NodeTypeUtils.createMixin(node.getSession(), mixinName); } node.addMixin(mixinName); if (setProtectedProperties) { node.setProperty(name, new Value[] { jcrValue }); } } else if (!protectedProperties.contains(name) || setProtectedProperties) { node.setProperty(name, jcrValue); } } private void addMultivalueProperty(Node node, Map.Entry<String, Object> entry, String name) throws RepositoryException { Collection<Object> values = (Collection) entry.getValue(); List<Value> jcrValues = new ArrayList<>(); for (Iterator<Object> iterator = values.iterator(); iterator.hasNext();) { Object value = iterator.next(); if (NodeBeanUtils.isValueNotNull(value)) { Value jcrValue = toJcrValue(node.getSession(), value, name); addMixinIfRequired(node, name, jcrValue); jcrValues.add(jcrValue); } } if (!protectedProperties.contains(name) || setProtectedProperties) { node.setProperty(name, jcrValues.toArray(new Value[jcrValues.size()])); } } private void addMixinIfRequired(Node node, String name, Value jcrValue) throws RepositoryException { if (JCR_MIXIN_TYPES.equals(name) && addMixins) { String mixinName = jcrValue.getString(); if (addUnknownTypes) { NodeTypeUtils.createMixin(node.getSession(), mixinName); } node.addMixin(mixinName); } } private void setUuid(Node node, String uuid) { Method setter = ReflectionUtils.getMethod(node, "setIdentifier", String.class); if (setter != null) { ReflectionUtils.invokeMethod(setter, node, uuid); } } private Value toJcrValue(Session session, Object value, String propertyName) throws RepositoryException { Value result; ValueFactory valueFactory = session.getValueFactory(); String valueType = NodeBeanUtils.getValueType(value); ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean(); int type = PropertyType.valueFromName(valueType); String valueAsString = NodeBeanUtils.getValueAsString(value); if (addUnknownTypes && type == PropertyType.NAME) { if (JCR_MIXIN_TYPES.equals(propertyName)) { NodeTypeUtils.createMixin(session, valueAsString); } else { NodeTypeUtils.createNodeType(session, valueAsString); } } switch (type) { case PropertyType.BOOLEAN: result = valueFactory.createValue((Boolean) convertUtilsBean.convert(valueAsString, Boolean.class)); break; case PropertyType.DOUBLE: result = valueFactory.createValue((Double) convertUtilsBean.convert(valueAsString, Double.class)); break; case PropertyType.LONG: result = valueFactory.createValue((Long) convertUtilsBean.convert(valueAsString, Long.class)); break; default: result = valueFactory.createValue(valueAsString, type); break; } return result; } public static class Builder { private boolean addMixins = true; private boolean addUuid = false; private boolean setProtectedProperties = false; private boolean saveSession = true; private boolean addUnknownTypes = false; private final SupplierWithException<Node> rootNodeSupplier; public Builder(SupplierWithException<Node> rootNodeSupplier) { this.rootNodeSupplier = rootNodeSupplier; if (this.rootNodeSupplier == null) { throw new IllegalArgumentException("supplier is required."); } } public Builder addMixins(boolean addMixins) { this.addMixins = addMixins; return this; } public Builder addUuid(boolean addUuid) { this.addUuid = addUuid; return this; } public Builder setProtectedProperties(boolean setProtectedProperties) { this.setProtectedProperties = setProtectedProperties; return this; } public Builder saveSession(boolean saveSession) { this.saveSession = saveSession; return this; } public Builder addUnknownTypes(boolean addUnknownTypes) { this.addUnknownTypes = addUnknownTypes; return this; } public Importer build() { return new Importer(this); } } @FunctionalInterface public interface SupplierWithException<T> { T get() throws Exception; } }