View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.slc.jcr;
17  
18  import java.util.Calendar;
19  import java.util.GregorianCalendar;
20  import java.util.List;
21  
22  import javax.jcr.Node;
23  import javax.jcr.NodeIterator;
24  import javax.jcr.Property;
25  import javax.jcr.RepositoryException;
26  import javax.jcr.Session;
27  import javax.jcr.nodetype.NodeType;
28  
29  import org.argeo.jcr.JcrUtils;
30  import org.argeo.node.NodeUtils;
31  import org.argeo.slc.DefaultNameVersion;
32  import org.argeo.slc.NameVersion;
33  import org.argeo.slc.SlcException;
34  import org.argeo.slc.SlcNames;
35  import org.argeo.slc.SlcTypes;
36  import org.argeo.slc.core.execution.PrimitiveAccessor;
37  import org.argeo.slc.core.execution.PrimitiveUtils;
38  import org.argeo.slc.deploy.ModuleDescriptor;
39  import org.argeo.slc.test.TestStatus;
40  
41  /**
42   * Utilities around the SLC JCR model. Note that it relies on fixed base paths
43   * (convention over configuration) for optimization purposes.
44   */
45  public class SlcJcrUtils implements SlcNames {
46  	public final static Integer AGENT_FACTORY_DEPTH = 3;
47  
48  	/** Extracts the path of a flow relative to its execution module */
49  	public static String flowRelativePath(String fullFlowPath) {
50  		String[] tokens = fullFlowPath.split("/");
51  		StringBuffer buf = new StringBuffer(fullFlowPath.length());
52  		for (int i = AGENT_FACTORY_DEPTH + 3; i < tokens.length; i++) {
53  			buf.append('/').append(tokens[i]);
54  		}
55  		return buf.toString();
56  	}
57  
58  	/** Extracts the path to the related execution module */
59  	public static String modulePath(String fullFlowPath) {
60  		String[] tokens = fullFlowPath.split("/");
61  		StringBuffer buf = new StringBuffer(fullFlowPath.length());
62  		for (int i = 0; i < AGENT_FACTORY_DEPTH + 3; i++) {
63  			if (!tokens[i].equals(""))
64  				buf.append('/').append(tokens[i]);
65  		}
66  		return buf.toString();
67  	}
68  
69  	/** Extracts the module name from a flow path */
70  	public static String moduleName(String fullFlowPath) {
71  		String[] tokens = fullFlowPath.split("/");
72  		String moduleName = tokens[AGENT_FACTORY_DEPTH + 2];
73  		moduleName = moduleName.substring(0, moduleName.indexOf('_'));
74  		return moduleName;
75  	}
76  
77  	/** Extracts the module name and version from a flow path */
78  	public static NameVersion moduleNameVersion(String fullFlowPath) {
79  		String[] tokens = fullFlowPath.split("/");
80  		String module = tokens[AGENT_FACTORY_DEPTH + 2];
81  		String moduleName = module.substring(0, module.indexOf('_'));
82  		String moduleVersion = module.substring(module.indexOf('_') + 1);
83  		return new DefaultNameVersion(moduleName, moduleVersion);
84  	}
85  
86  	/** Module node name based on module name and version */
87  	public static String getModuleNodeName(ModuleDescriptor moduleDescriptor) {
88  		return moduleDescriptor.getName() + "_" + moduleDescriptor.getVersion();
89  	}
90  
91  	/** Extracts the agent factory of a flow */
92  	public static String flowAgentFactoryPath(String fullFlowPath) {
93  		String[] tokens = fullFlowPath.split("/");
94  		StringBuffer buf = new StringBuffer(fullFlowPath.length());
95  		// first token is always empty
96  		for (int i = 1; i < AGENT_FACTORY_DEPTH + 1; i++) {
97  			buf.append('/').append(tokens[i]);
98  		}
99  		return buf.toString();
100 	}
101 
102 	/** Create a new execution process path based on the current time */
103 	public static String createExecutionProcessPath(Session session, String uuid) {
104 		Calendar now = new GregorianCalendar();
105 		return getSlcProcessesBasePath(session) + '/'
106 				+ JcrUtils.dateAsPath(now, true) + uuid;
107 	}
108 
109 	/** Get the base for the user processi. */
110 	public static String getSlcProcessesBasePath(Session session) {
111 		try {
112 			Node userHome = NodeUtils.getUserHome(session);
113 			if (userHome == null)
114 				throw new SlcException("No user home available for "
115 						+ session.getUserID());
116 			return userHome.getPath() + '/' + SlcNames.SLC_SYSTEM + '/'
117 					+ SlcNames.SLC_PROCESSES;
118 		} catch (RepositoryException re) {
119 			throw new SlcException(
120 					"Unexpected error while getting Slc Results Base Path.", re);
121 		}
122 	}
123 
124 	/**
125 	 * Create a new execution result path in the user home based on the current
126 	 * time
127 	 */
128 	public static String createResultPath(Session session, String uuid)
129 			throws RepositoryException {
130 		Calendar now = new GregorianCalendar();
131 		StringBuffer absPath = new StringBuffer(
132 				SlcJcrResultUtils.getSlcResultsBasePath(session) + '/');
133 		// Remove hours and add title property to the result process path on
134 		// request of O. Capillon
135 		// return getSlcProcessesBasePath(session) + '/'
136 		// + JcrUtils.dateAsPath(now, true) + uuid;
137 		String relPath = JcrUtils.dateAsPath(now, false);
138 		List<String> names = JcrUtils.tokenize(relPath);
139 		for (String name : names) {
140 			absPath.append(name + "/");
141 			Node node = JcrUtils.mkdirs(session, absPath.toString());
142 			try {
143 				node.addMixin(NodeType.MIX_TITLE);
144 				node.setProperty(Property.JCR_TITLE, name.substring(1));
145 			} catch (RepositoryException e) {
146 				throw new SlcException(
147 						"unable to create execution process path", e);
148 			}
149 		}
150 		return absPath.toString() + uuid;
151 	}
152 
153 	/**
154 	 * Set the value of the primitive accessor as a JCR property. Does nothing
155 	 * if the value is null.
156 	 */
157 	public static void setPrimitiveAsProperty(Node node, String propertyName,
158 			PrimitiveAccessor primitiveAccessor) {
159 		String type = primitiveAccessor.getType();
160 		Object value = primitiveAccessor.getValue();
161 		setPrimitiveAsProperty(node, propertyName, type, value);
162 	}
163 
164 	/** Map a primitive value to JCR property value. */
165 	public static void setPrimitiveAsProperty(Node node, String propertyName,
166 			String type, Object value) {
167 		if (value == null)
168 			return;
169 		if (value instanceof CharSequence)
170 			value = PrimitiveUtils.convert(type,
171 					((CharSequence) value).toString());
172 		if (value instanceof char[])
173 			value = new String((char[]) value);
174 
175 		try {
176 			if (type.equals(PrimitiveAccessor.TYPE_STRING))
177 				node.setProperty(propertyName, value.toString());
178 			else if (type.equals(PrimitiveAccessor.TYPE_PASSWORD))
179 				node.setProperty(propertyName, value.toString());
180 			else if (type.equals(PrimitiveAccessor.TYPE_INTEGER))
181 				node.setProperty(propertyName, (long) ((Integer) value));
182 			else if (type.equals(PrimitiveAccessor.TYPE_LONG))
183 				node.setProperty(propertyName, ((Long) value));
184 			else if (type.equals(PrimitiveAccessor.TYPE_FLOAT))
185 				node.setProperty(propertyName, (double) ((Float) value));
186 			else if (type.equals(PrimitiveAccessor.TYPE_DOUBLE))
187 				node.setProperty(propertyName, ((Double) value));
188 			else if (type.equals(PrimitiveAccessor.TYPE_BOOLEAN))
189 				node.setProperty(propertyName, ((Boolean) value));
190 			else
191 				throw new SlcException("Unsupported type " + type);
192 		} catch (RepositoryException e) {
193 			throw new SlcException("Cannot set primitive of " + type
194 					+ " as property " + propertyName + " on " + node, e);
195 		}
196 	}
197 
198 	/** Aggregates the {@link TestStatus} of this sub-tree. */
199 	public static Integer aggregateTestStatus(Node node) {
200 		try {
201 			Integer status = TestStatus.PASSED;
202 			if (node.isNodeType(SlcTypes.SLC_CHECK))
203 				if (node.getProperty(SLC_SUCCESS).getBoolean())
204 					status = TestStatus.PASSED;
205 				else if (node.hasProperty(SLC_ERROR_MESSAGE))
206 					status = TestStatus.ERROR;
207 				else
208 					status = TestStatus.FAILED;
209 
210 			NodeIterator it = node.getNodes();
211 			while (it.hasNext()) {
212 				Node curr = it.nextNode();
213 
214 				// Manually skip aggregated status
215 				if (!SlcNames.SLC_AGGREGATED_STATUS.equals(curr.getName())) {
216 					Integer childStatus = aggregateTestStatus(curr);
217 					if (childStatus > status)
218 						status = childStatus;
219 				}
220 			}
221 			return status;
222 		} catch (Exception e) {
223 			throw new SlcException("Could not aggregate test status from "
224 					+ node, e);
225 		}
226 	}
227 
228 	/**
229 	 * Aggregates the {@link TestStatus} of this sub-tree.
230 	 * 
231 	 * @return the same {@link StringBuffer}, for convenience (typically calling
232 	 *         toString() on it)
233 	 */
234 	public static StringBuffer aggregateTestMessages(Node node,
235 			StringBuffer messages) {
236 		try {
237 			if (node.isNodeType(SlcTypes.SLC_CHECK)) {
238 				if (node.hasProperty(SLC_MESSAGE)) {
239 					if (messages.length() > 0)
240 						messages.append('\n');
241 					messages.append(node.getProperty(SLC_MESSAGE).getString());
242 				}
243 				if (node.hasProperty(SLC_ERROR_MESSAGE)) {
244 					if (messages.length() > 0)
245 						messages.append('\n');
246 					messages.append(node.getProperty(SLC_ERROR_MESSAGE)
247 							.getString());
248 				}
249 			}
250 			NodeIterator it = node.getNodes();
251 			while (it.hasNext()) {
252 				Node child = it.nextNode();
253 				// Manually skip aggregated status
254 				if (!SlcNames.SLC_AGGREGATED_STATUS.equals(child.getName())) {
255 					aggregateTestMessages(child, messages);
256 				}
257 			}
258 			return messages;
259 		} catch (Exception e) {
260 			throw new SlcException("Could not aggregate test messages from "
261 					+ node, e);
262 		}
263 	}
264 
265 	/** Prevents instantiation */
266 	private SlcJcrUtils() {
267 	}
268 }