001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors. 
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.repository.util;
023    
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.util.HashMap;
027    import java.util.Map;
028    import javax.jcr.Node;
029    import javax.jcr.NodeIterator;
030    import javax.jcr.PathNotFoundException;
031    import javax.jcr.Property;
032    import javax.jcr.PropertyType;
033    import javax.jcr.RepositoryException;
034    import javax.jcr.Session;
035    import javax.jcr.Value;
036    import javax.jcr.ValueFormatException;
037    import org.jboss.dna.common.collection.Problem;
038    import org.jboss.dna.common.collection.Problems;
039    import org.jboss.dna.common.util.IoUtil;
040    import org.jboss.dna.common.util.Logger;
041    import org.jboss.dna.common.util.StringUtil;
042    import org.jboss.dna.repository.RepositoryI18n;
043    
044    /**
045     * @author Randall Hauch
046     */
047    public class JcrTools {
048    
049        public Map<String, Object> loadProperties( Node propertyContainer, Problems problems ) {
050            return loadProperties(propertyContainer, null, problems);
051        }
052    
053        public Map<String, Object> loadProperties( Node propertyContainer, Map<String, Object> properties, Problems problems ) {
054            if (properties == null) properties = new HashMap<String, Object>();
055            if (propertyContainer != null) {
056                try {
057                    NodeIterator iter = propertyContainer.getNodes();
058                    while (iter.hasNext()) {
059                        Node propertyNode = iter.nextNode();
060                        if (propertyNode != null && propertyNode.getPrimaryNodeType().isNodeType("dna:property")) {
061                            String propertyName = propertyNode.getName();
062                            Object propertyValue = getPropertyValue(propertyNode, "dna:propertyValue", true, problems);
063                            properties.put(propertyName, propertyValue);
064                        }
065                    }
066                } catch (RepositoryException e) {
067                    problems.addError(e, RepositoryI18n.errorReadingPropertiesFromContainerNode, getReadable(propertyContainer));
068                }
069            }
070    
071            return properties;
072        }
073    
074        public boolean removeProblems( Node parent ) throws RepositoryException {
075            Node problemsNode = null;
076            if (parent.hasNode("dna:problems")) {
077                problemsNode = parent.getNode("dna:problems");
078                problemsNode.remove();
079                return true;
080            }
081            return false;
082        }
083    
084        public boolean storeProblems( Node parent, Problems problems ) throws RepositoryException {
085            Node problemsNode = null;
086            if (parent.hasNode("dna:problems")) {
087                problemsNode = parent.getNode("dna:problems");
088                // Delete all problems ...
089                removeAllChildren(problemsNode);
090            }
091            if (problems.isEmpty()) {
092                return false;
093            }
094            if (problemsNode == null) {
095                problemsNode = parent.addNode("dna:problems"); // primary type dictated by child definition
096            }
097    
098            // Add a child for each problem ...
099            for (Problem problem : problems) {
100                Node problemNode = problemsNode.addNode("problem", "dna:problem");
101                // - dna:status (string) mandatory copy
102                // < 'ERROR', 'WARNING', 'INFO'
103                // - dna:message (string) mandatory copy
104                // - dna:code (string) copy
105                // - dna:type (string) copy
106                // - dna:resource (string) copy
107                // - dna:location (string) copy
108                // - dna:trace (string) copy
109                problemNode.setProperty("dna:status", problem.getStatus().name());
110                problemNode.setProperty("dna:message", problem.getMessageString());
111                if (problem.getCode() != Problem.DEFAULT_CODE) {
112                    problemNode.setProperty("dna:code", Integer.toString(problem.getCode()));
113                }
114                String resource = problem.getResource();
115                if (resource != null) {
116                    problemNode.setProperty("dna:resource", resource);
117                }
118                String location = problem.getLocation();
119                if (location != null) {
120                    problemNode.setProperty("dna:location", location);
121                }
122                Throwable t = problem.getThrowable();
123                if (t != null) {
124                    String trace = StringUtil.getStackTrace(t);
125                    problemNode.setProperty("dna:trace", trace);
126                }
127            }
128            return true;
129        }
130    
131        public int removeAllChildren( Node node ) throws RepositoryException {
132            int childrenRemoved = 0;
133            NodeIterator iter = node.getNodes();
134            while (iter.hasNext()) {
135                Node child = iter.nextNode();
136                child.remove();
137                ++childrenRemoved;
138            }
139            return childrenRemoved;
140        }
141    
142        public String getPropertyAsString( Node node, String propertyName, boolean required, Problems problems ) {
143            return getPropertyAsString(node, propertyName, required, null);
144        }
145    
146        public String getPropertyAsString( Node node, String propertyName, boolean required, String defaultValue, Problems problems ) {
147            try {
148                Property property = node.getProperty(propertyName);
149                return property.getString();
150            } catch (ValueFormatException e) {
151                if (required) {
152                    problems.addError(e, RepositoryI18n.requiredPropertyOnNodeWasExpectedToBeStringValue, propertyName, getReadable(node));
153                } else {
154                    problems.addError(e, RepositoryI18n.optionalPropertyOnNodeWasExpectedToBeStringValue, propertyName, getReadable(node));
155                }
156            } catch (PathNotFoundException e) {
157                if (required) {
158                    problems.addError(e, RepositoryI18n.requiredPropertyIsMissingFromNode, propertyName, getReadable(node));
159                }
160                if (!required) return defaultValue;
161            } catch (RepositoryException err) {
162                if (required) {
163                    problems.addError(err, RepositoryI18n.errorGettingRequiredPropertyFromNode, propertyName, getReadable(node));
164                } else {
165                    problems.addError(err, RepositoryI18n.errorGettingOptionalPropertyFromNode, propertyName, getReadable(node));
166                }
167            }
168            return null;
169        }
170    
171        public Object getPropertyValue( Node node, String propertyName, boolean required, Problems problems ) {
172            try {
173                Property property = node.getProperty(propertyName);
174                switch (property.getType()) {
175                    case PropertyType.BINARY: {
176                        InputStream stream = property.getStream();
177                        try {
178                            stream = property.getStream();
179                            return IoUtil.readBytes(stream);
180                        } finally {
181                            if (stream != null) {
182                                try {
183                                    stream.close();
184                                } catch (IOException e) {
185                                    // Log ...
186                                    Logger.getLogger(this.getClass()).error(e, RepositoryI18n.errorClosingBinaryStreamForPropertyFromNode, propertyName, node.getPath());
187                                }
188                            }
189                        }
190                    }
191                    default: {
192                        return property.getString();
193                    }
194                }
195            } catch (IOException e) {
196                if (required) {
197                    problems.addError(e, RepositoryI18n.requiredPropertyOnNodeCouldNotBeRead, propertyName, getReadable(node));
198                } else {
199                    problems.addError(e, RepositoryI18n.optionalPropertyOnNodeCouldNotBeRead, propertyName, getReadable(node));
200                }
201            } catch (PathNotFoundException e) {
202                if (required) {
203                    problems.addError(e, RepositoryI18n.requiredPropertyIsMissingFromNode, propertyName, getReadable(node));
204                }
205            } catch (RepositoryException err) {
206                if (required) {
207                    problems.addError(err, RepositoryI18n.errorGettingRequiredPropertyFromNode, propertyName, getReadable(node));
208                } else {
209                    problems.addError(err, RepositoryI18n.errorGettingOptionalPropertyFromNode, propertyName, getReadable(node));
210                }
211            }
212            return null;
213        }
214    
215        public String[] getPropertyAsStringArray( Node node, String propertyName, boolean required, Problems problems, String... defaultValues ) {
216            String[] result = defaultValues;
217            try {
218                Property property = node.getProperty(propertyName);
219                if (property.getDefinition().isMultiple()) {
220                    Value[] values = property.getValues();
221                    result = new String[values.length];
222                    int i = 0;
223                    for (Value value : values) {
224                        result[i++] = value.getString();
225                    }
226                } else {
227                    result = new String[] {property.getString()};
228                }
229            } catch (ValueFormatException e) {
230                if (required) {
231                    problems.addError(e, RepositoryI18n.requiredPropertyOnNodeWasExpectedToBeStringArrayValue, propertyName, getReadable(node));
232                } else {
233                    problems.addError(e, RepositoryI18n.optionalPropertyOnNodeWasExpectedToBeStringArrayValue, propertyName, getReadable(node));
234                }
235            } catch (PathNotFoundException e) {
236                if (required) {
237                    problems.addError(e, RepositoryI18n.requiredPropertyIsMissingFromNode, propertyName, getReadable(node));
238                }
239            } catch (RepositoryException err) {
240                if (required) {
241                    problems.addError(err, RepositoryI18n.errorGettingRequiredPropertyFromNode, propertyName, getReadable(node));
242                } else {
243                    problems.addError(err, RepositoryI18n.errorGettingOptionalPropertyFromNode, propertyName, getReadable(node));
244                }
245            }
246            return result;
247        }
248    
249        public Node getNode( Node node, String relativePath, boolean required, Problems problems ) {
250            Node result = null;
251            try {
252                result = node.getNode(relativePath);
253            } catch (PathNotFoundException e) {
254                if (required) problems.addError(e, RepositoryI18n.requiredNodeDoesNotExistRelativeToNode, relativePath, getReadable(node));
255            } catch (RepositoryException err) {
256                problems.addError(err, RepositoryI18n.errorGettingNodeRelativeToNode, relativePath, getReadable(node));
257            }
258            return result;
259        }
260    
261        public String getReadable( Node node ) {
262            if (node == null) return "";
263            try {
264                return node.getPath();
265            } catch (RepositoryException err) {
266                return node.toString();
267            }
268        }
269    
270        public Node findOrCreateNode( Session session, String path ) throws RepositoryException {
271            return findOrCreateNode(session, path, null, null);
272        }
273    
274        public Node findOrCreateNode( Session session, String path, String nodeType ) throws RepositoryException {
275            return findOrCreateNode(session, path, nodeType, nodeType);
276        }
277    
278        public Node findOrCreateNode( Session session, String path, String defaultNodeType, String finalNodeType ) throws RepositoryException {
279            Node root = session.getRootNode();
280            return findOrCreateNode(session, root, path, defaultNodeType, finalNodeType);
281        }
282    
283        public Node findOrCreateNode( Session session, Node parentNode, String path, String defaultNodeType, String finalNodeType ) throws RepositoryException {
284            // Remove leading and trailing slashes ...
285            String relPath = path.replaceAll("^/+", "").replaceAll("/+$", "");
286    
287            // Look for the node first ...
288            try {
289                return parentNode.getNode(relPath);
290            } catch (PathNotFoundException e) {
291                // continue
292            }
293            // Create the node, which has to be done segment by segment ...
294            String[] pathSegments = relPath.split("/");
295            Node node = parentNode;
296            for (int i = 0, len = pathSegments.length; i != len; ++i) {
297                String pathSegment = pathSegments[i];
298                pathSegment = pathSegment.trim();
299                if (pathSegment.length() == 0) continue;
300                if (node.hasNode(pathSegment)) {
301                    // Find the existing node ...
302                    node = node.getNode(pathSegment);
303                } else {
304                    // Make sure there is no index on the final segment ...
305                    String pathSegmentWithNoIndex = pathSegment.replaceAll("(\\[\\d+\\])+$", "");
306                    // Create the node ...
307                    String nodeType = defaultNodeType;
308                    if (i == len - 1 && finalNodeType != null) nodeType = finalNodeType;
309                    if (nodeType != null) {
310                        node = node.addNode(pathSegmentWithNoIndex, nodeType);
311                    } else {
312                        node = node.addNode(pathSegmentWithNoIndex);
313                    }
314                }
315            }
316            return node;
317        }
318    
319        public Node findOrCreateChild( Session session, Node parent, String name ) throws RepositoryException {
320            return findOrCreateChild(session, parent, name, null);
321        }
322    
323        public Node findOrCreateChild( Session session, Node parent, String name, String nodeType ) throws RepositoryException {
324            return findOrCreateNode(session, parent, name, nodeType, nodeType);
325        }
326    
327    }