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.Date;
19  import java.util.GregorianCalendar;
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.UUID;
23  
24  import javax.jcr.Credentials;
25  import javax.jcr.Node;
26  import javax.jcr.Property;
27  import javax.jcr.PropertyIterator;
28  import javax.jcr.Repository;
29  import javax.jcr.Session;
30  import javax.jcr.query.Query;
31  import javax.jcr.query.QueryManager;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.argeo.jcr.JcrUtils;
36  import org.argeo.slc.SlcException;
37  import org.argeo.slc.SlcNames;
38  import org.argeo.slc.SlcTypes;
39  import org.argeo.slc.core.attachment.Attachment;
40  import org.argeo.slc.core.attachment.AttachmentsEnabled;
41  import org.argeo.slc.test.TestResult;
42  import org.argeo.slc.test.TestResultPart;
43  import org.argeo.slc.test.TestRun;
44  import org.argeo.slc.test.TestStatus;
45  
46  /**
47   * {@link TestResult} wrapping a JCR node of type
48   * {@link SlcTypes#SLC_TEST_RESULT}.
49   */
50  public class JcrTestResult implements TestResult, SlcNames, AttachmentsEnabled {
51  	private final static Log log = LogFactory.getLog(JcrTestResult.class);
52  
53  	/** Should only be set for an already existing result. */
54  	private String uuid;
55  	private Repository repository;
56  	private Session session;
57  	/**
58  	 * For testing purposes, best practice is to not set them explicitely but
59  	 * via other mechanisms such as JAAS or SPring Security.
60  	 */
61  	private Credentials credentials = null;
62  	private String resultType = SlcTypes.SLC_TEST_RESULT;
63  
64  	/** cached for performance purposes */
65  	private String nodeIdentifier = null;
66  
67  	private Map<String, String> attributes = new HashMap<String, String>();
68  
69  	public void init() {
70  		try {
71  			session = repository.login(credentials);
72  			if (uuid == null) {
73  				// create new result
74  				uuid = UUID.randomUUID().toString();
75  				String path = SlcJcrUtils.createResultPath(session, uuid);
76  				Node resultNode = JcrUtils.mkdirs(session, path, resultType);
77  				resultNode.setProperty(SLC_UUID, uuid);
78  				for (String attr : attributes.keySet()) {
79  					String property = attr;
80  					// compatibility with legacy applications
81  					if ("testCase".equals(attr))
82  						property = SLC_TEST_CASE;
83  					else if ("testCaseType".equals(attr))
84  						property = SLC_TEST_CASE_TYPE;
85  					resultNode.setProperty(property, attributes.get(attr));
86  				}
87  				session.save();
88  				if (log.isDebugEnabled())
89  					log.debug("Created test result " + uuid);
90  			}
91  		} catch (Exception e) {
92  			JcrUtils.discardQuietly(session);
93  			throw new SlcException("Cannot initialize JCR result", e);
94  		}
95  	}
96  
97  	public void destroy() {
98  		JcrUtils.logoutQuietly(session);
99  		if (log.isTraceEnabled())
100 			log.trace("Logged out session for result " + uuid);
101 	}
102 
103 	public Node getNode() {
104 		try {
105 			Node resultNode;
106 			if (nodeIdentifier != null) {
107 				return session.getNodeByIdentifier(nodeIdentifier);
108 			} else {
109 				QueryManager qm = session.getWorkspace().getQueryManager();
110 				Query q = qm.createQuery("select * from ["
111 						+ SlcTypes.SLC_TEST_RESULT + "] where [slc:uuid]='"
112 						+ uuid + "'", Query.JCR_SQL2);
113 				resultNode = JcrUtils.querySingleNode(q);
114 				if (resultNode != null)
115 					nodeIdentifier = resultNode.getIdentifier();
116 			}
117 			return resultNode;
118 		} catch (Exception e) {
119 			throw new SlcException("Cannot get result node", e);
120 		}
121 	}
122 
123 	public void notifyTestRun(TestRun testRun) {
124 		// TODO store meta data about the test running
125 		// if (log.isDebugEnabled())
126 		// log.debug("Running test "
127 		// + testRun.getTestDefinition().getClass().getName() + "...");
128 	}
129 
130 	public void addResultPart(TestResultPart testResultPart) {
131 		Node node = getNode();
132 
133 		try {
134 			// error : revert all unsaved changes on the resultNode to be sure
135 			// it is in a consistant state
136 			if (testResultPart.getExceptionMessage() != null)
137 				JcrUtils.discardQuietly(node.getSession());
138 			node.getSession().save();
139 
140 			// add the new result part, retrieving status information
141 			Node resultPartNode = node.addNode(SlcNames.SLC_RESULT_PART,
142 					SlcTypes.SLC_CHECK);
143 			resultPartNode.setProperty(SLC_SUCCESS, testResultPart.getStatus()
144 					.equals(TestStatus.PASSED));
145 			if (testResultPart.getMessage() != null)
146 				resultPartNode.setProperty(SLC_MESSAGE,
147 						testResultPart.getMessage());
148 			if (testResultPart.getStatus().equals(TestStatus.ERROR)) {
149 				resultPartNode.setProperty(SLC_ERROR_MESSAGE,
150 						(testResultPart.getExceptionMessage() == null) ? ""
151 								: testResultPart.getExceptionMessage());
152 			}
153 
154 			// helper update aggregate status node
155 			Node mainStatus;
156 			if (!node.hasNode(SLC_AGGREGATED_STATUS)) {
157 
158 				mainStatus = node.addNode(SLC_AGGREGATED_STATUS,
159 						SlcTypes.SLC_CHECK);
160 				mainStatus.setProperty(SLC_SUCCESS,
161 						resultPartNode.getProperty(SLC_SUCCESS).getBoolean());
162 				if (resultPartNode.hasProperty(SLC_MESSAGE))
163 					mainStatus.setProperty(SLC_MESSAGE, resultPartNode
164 							.getProperty(SLC_MESSAGE).getString());
165 				if (resultPartNode.hasProperty(SLC_ERROR_MESSAGE))
166 					mainStatus.setProperty(SLC_ERROR_MESSAGE, resultPartNode
167 							.getProperty(SLC_ERROR_MESSAGE).getString());
168 			} else {
169 				mainStatus = node.getNode(SLC_AGGREGATED_STATUS);
170 				if (mainStatus.hasProperty(SLC_ERROR_MESSAGE)) {
171 					// main status already in error we do nothing
172 				} else if (resultPartNode.hasProperty(SLC_ERROR_MESSAGE)) {
173 					// main status was not in error and new result part is in
174 					// error; we update main status
175 					mainStatus.setProperty(SLC_SUCCESS, false);
176 					mainStatus.setProperty(SLC_ERROR_MESSAGE, resultPartNode
177 							.getProperty(SLC_ERROR_MESSAGE).getString());
178 					if (resultPartNode.hasProperty(SLC_MESSAGE))
179 						mainStatus.setProperty(SLC_MESSAGE, resultPartNode
180 								.getProperty(SLC_MESSAGE).getString());
181 					else
182 						// remove old message to remain consistent
183 						mainStatus.setProperty(SLC_MESSAGE, "");
184 				} else if (!mainStatus.getProperty(SLC_SUCCESS).getBoolean()) {
185 					// main status was already failed and new result part is not
186 					// in error, we do nothing
187 				} else if (!resultPartNode.getProperty(SLC_SUCCESS)
188 						.getBoolean()) {
189 					// new resultPart that is failed
190 					mainStatus.setProperty(SLC_SUCCESS, false);
191 					if (resultPartNode.hasProperty(SLC_MESSAGE))
192 						mainStatus.setProperty(SLC_MESSAGE, resultPartNode
193 								.getProperty(SLC_MESSAGE).getString());
194 					else
195 						// remove old message to remain consistent
196 						mainStatus.setProperty(SLC_MESSAGE, "");
197 				} else if (resultPartNode.hasProperty(SLC_MESSAGE)
198 						&& (!mainStatus.hasProperty(SLC_MESSAGE) || (""
199 								.equals(mainStatus.getProperty(SLC_MESSAGE)
200 										.getString().trim())))) {
201 					mainStatus.setProperty(SLC_MESSAGE, resultPartNode
202 							.getProperty(SLC_MESSAGE).getString());
203 				}
204 			}
205 			JcrUtils.updateLastModified(node);
206 			node.getSession().save();
207 		} catch (Exception e) {
208 			JcrUtils.discardUnderlyingSessionQuietly(node);
209 			throw new SlcException("Cannot add ResultPart to node " + node, e);
210 		}
211 	}
212 
213 	public String getUuid() {
214 		Node node = getNode();
215 		try {
216 			return node.getProperty(SLC_UUID).getString();
217 		} catch (Exception e) {
218 			throw new SlcException("Cannot get UUID from " + node, e);
219 		}
220 	}
221 
222 	/** JCR session is NOT logged out */
223 	public void close() {
224 		Node node = getNode();
225 		try {
226 			if (node.hasNode(SLC_COMPLETED))
227 				return;
228 			node.setProperty(SLC_COMPLETED, new GregorianCalendar());
229 			JcrUtils.updateLastModified(node);
230 			node.getSession().save();
231 		} catch (Exception e) {
232 			JcrUtils.discardUnderlyingSessionQuietly(node);
233 			throw new SlcException("Cannot get close date from " + node, e);
234 		}
235 	}
236 
237 	public Date getCloseDate() {
238 		Node node = getNode();
239 		try {
240 			if (!node.hasNode(SLC_COMPLETED))
241 				return null;
242 			return node.getProperty(SLC_COMPLETED).getDate().getTime();
243 		} catch (Exception e) {
244 			throw new SlcException("Cannot get close date from " + node, e);
245 		}
246 	}
247 
248 	public Map<String, String> getAttributes() {
249 		Node node = getNode();
250 		try {
251 			Map<String, String> map = new HashMap<String, String>();
252 			PropertyIterator pit = node.getProperties();
253 			while (pit.hasNext()) {
254 				Property p = pit.nextProperty();
255 				if (!p.isMultiple())
256 					map.put(p.getName(), p.getValue().getString());
257 			}
258 			return map;
259 		} catch (Exception e) {
260 			throw new SlcException("Cannot get close date from " + node, e);
261 		}
262 	}
263 
264 	public void addAttachment(Attachment attachment) {
265 		// TODO implement it
266 	}
267 
268 	public void setUuid(String uuid) {
269 		this.uuid = uuid;
270 	}
271 
272 	public void setRepository(Repository repository) {
273 		this.repository = repository;
274 	}
275 
276 	public void setResultType(String resultType) {
277 		this.resultType = resultType;
278 	}
279 
280 	public void setAttributes(Map<String, String> attributes) {
281 		if (uuid != null)
282 			throw new SlcException(
283 					"Attributes cannot be set on an already initialized test result."
284 							+ " Update the related JCR node directly instead.");
285 		this.attributes = attributes;
286 	}
287 
288 	public void setCredentials(Credentials credentials) {
289 		this.credentials = credentials;
290 	}
291 }