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