summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpmcmahan2012-09-28 12:22:19 (EDT)
committer pmcmahan2012-09-28 12:22:19 (EDT)
commit32994209666b0b55651832ab54a92f3ddb25a846 (patch)
tree8f278084bdd0dd8e5829cb8d400164855f816a1c
parent8ad7db9ca8ce30c54c8138554a64f572200d45e0 (diff)
downloadorg.eclipse.lyo.client-32994209666b0b55651832ab54a92f3ddb25a846.zip
org.eclipse.lyo.client-32994209666b0b55651832ab54a92f3ddb25a846.tar.gz
org.eclipse.lyo.client-32994209666b0b55651832ab54a92f3ddb25a846.tar.bz2
Bug 390114 - OSLC Automation for client samplerefs/changes/80/7980/1
-rw-r--r--org.eclipse.lyo.samples.clients/pom.xml12
-rw-r--r--org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/AutomationAdapter.java1325
-rw-r--r--org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/AutomationException.java38
-rw-r--r--org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/IAutomationRequestHandler.java36
-rw-r--r--org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/IConstants.java49
-rw-r--r--org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/RQMAutomationSample.java296
-rw-r--r--org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/adapter.properties14
-rw-r--r--org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/sample.pngbin0 -> 2458 bytes
8 files changed, 1770 insertions, 0 deletions
diff --git a/org.eclipse.lyo.samples.clients/pom.xml b/org.eclipse.lyo.samples.clients/pom.xml
index 2cdff9c..3b2c1f0 100644
--- a/org.eclipse.lyo.samples.clients/pom.xml
+++ b/org.eclipse.lyo.samples.clients/pom.xml
@@ -29,6 +29,18 @@
<artifactId>oslc-java-client</artifactId>
<version>0.1.2-SNAPSHOT</version>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>1.3.2</version>
+ </dependency>
+
+ <dependency>
+ <groupId>xml-apis</groupId>
+ <artifactId>xml-apis</artifactId>
+ <version>1.4.01</version>
+ </dependency>
</dependencies>
</project> \ No newline at end of file
diff --git a/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/AutomationAdapter.java b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/AutomationAdapter.java
new file mode 100644
index 0000000..2d2a82c
--- /dev/null
+++ b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/AutomationAdapter.java
@@ -0,0 +1,1325 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Paul McMahan <pmcmahan@us.ibm.com> - initial implementation
+ *******************************************************************************/
+package org.eclipse.lyo.client.oslc.samples.automation;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import net.oauth.OAuthException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpStatus;
+import org.apache.wink.client.ClientResponse;
+import org.apache.wink.common.model.multipart.BufferedOutMultiPart;
+import org.apache.wink.common.model.multipart.OutPart;
+import org.eclipse.lyo.client.exception.JazzAuthErrorException;
+import org.eclipse.lyo.client.exception.JazzAuthFailedException;
+import org.eclipse.lyo.client.exception.ResourceNotFoundException;
+import org.eclipse.lyo.client.exception.RootServicesException;
+import org.eclipse.lyo.client.oslc.OSLCConstants;
+import org.eclipse.lyo.client.oslc.OslcClient;
+import org.eclipse.lyo.client.oslc.jazz.JazzFormAuthClient;
+import org.eclipse.lyo.client.oslc.jazz.JazzRootServicesHelper;
+import org.eclipse.lyo.client.oslc.resources.AutomationConstants;
+import org.eclipse.lyo.client.oslc.resources.AutomationPlan;
+import org.eclipse.lyo.client.oslc.resources.AutomationRequest;
+import org.eclipse.lyo.client.oslc.resources.AutomationResult;
+import org.eclipse.lyo.client.oslc.resources.TestScript;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcDescription;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcName;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcNamespace;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcOccurs;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcPropertyDefinition;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcRange;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcResourceShape;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcTitle;
+import org.eclipse.lyo.oslc4j.core.annotation.OslcValueType;
+import org.eclipse.lyo.oslc4j.core.model.AbstractResource;
+import org.eclipse.lyo.oslc4j.core.model.Occurs;
+import org.eclipse.lyo.oslc4j.core.model.OslcConstants;
+import org.eclipse.lyo.oslc4j.core.model.OslcMediaType;
+import org.eclipse.lyo.oslc4j.core.model.ValueType;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.ModelFactory;
+import com.hp.hpl.jena.rdf.model.StmtIterator;
+import com.hp.hpl.jena.vocabulary.RDF;
+
+@OslcResourceShape(title = "Automation Adapter Resource Shape", describes = IConstants.TYPE_AUTOMATION_ADAPTER)
+@OslcNamespace(IConstants.NAMESPACE_URI_JAZZ_QM)
+public class AutomationAdapter extends AbstractResource implements IConstants {
+
+ // connection properties for the adapter. These are not part of the
+ // AutomationAdapter resource properties
+ public static final String PROPERTY_SERVER_URL = "serverUrl";
+ public static final String PROPERTY_USERNAME = "username";
+ public static final String PROPERTY_PASSWORD = "password";
+ public static final String PROPERTY_PROJECT_AREA = "projectArea";
+
+ // resource properties
+ private final Set<String> capabilities = new TreeSet<String>();
+
+ public static final String PROPERTY_TITLE = "title";
+ public static final String PROPERTY_DESCRIPTION = "description";
+ public static final String PROPERTY_TYPE = "type";
+ public static final String PROPERTY_HOSTNAME = "hostname";
+ public static final String PROPERTY_IP_ADDRESS = "ipAddress";
+ public static final String PROPERTY_POLLING_INTERVAL = "pollingInterval";
+ public static final String PROPERTY_MAC_ADDRESSS = "macAddress";
+ public static final String PROPERTY_FQDN = "fqdn";
+ public static final String PROPERTY_CAPABIILTY = "capability";
+
+ // connection properties
+ private String serverUrl;
+ private String username;
+ private String password;
+ private String projectArea;
+
+ // properties provided to the server
+ private String title;
+ private String description;
+ private String type;
+ private String hostname;
+ private String ipAddress;
+ private Integer pollingInterval;
+ private String macAddress;
+ private String fqdn;
+
+ // properties provided by the server
+ private URI relation;
+ private URI workAvailableUrl;
+ private Boolean workAvailable;
+ private String identifier;
+ private Date modified;
+ private URI creator;
+ private URI instanceShape;
+ private URI runsOnMachine;
+ private URI serviceProvider;
+ private URI assignedWorkUrl;
+
+ // state of the adapter
+ private boolean isRegistered = false;
+ private boolean isStopped = false;
+
+ private JazzFormAuthClient client = null;
+ private JazzRootServicesHelper rootServicesHelper = null;
+
+ private DocumentBuilder documentBuilder;
+
+ public AutomationAdapter() {
+ this(new Properties());
+ }
+
+ public AutomationAdapter(Properties properties) {
+
+ try {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ documentBuilder = dbf.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+
+ serverUrl = properties.getProperty(PROPERTY_SERVER_URL);
+ username = properties.getProperty(PROPERTY_USERNAME);
+ password = properties.getProperty(PROPERTY_PASSWORD);
+ projectArea = properties.getProperty(PROPERTY_PROJECT_AREA);
+
+ title = properties.getProperty(PROPERTY_TITLE);
+ description = properties.getProperty(PROPERTY_DESCRIPTION);
+ type = properties.getProperty(PROPERTY_TYPE);
+ hostname = properties.getProperty(PROPERTY_HOSTNAME);
+ ipAddress = properties.getProperty(PROPERTY_IP_ADDRESS);
+ macAddress = properties.getProperty(PROPERTY_MAC_ADDRESSS);
+ fqdn = properties.getProperty(PROPERTY_FQDN);
+
+ String pollingIntervalStr = properties
+ .getProperty(PROPERTY_POLLING_INTERVAL);
+ if (pollingIntervalStr != null) {
+ pollingInterval = Integer.valueOf(pollingIntervalStr);
+ } else {
+ pollingInterval = 120;
+ }
+
+ String capabilities = properties.getProperty(PROPERTY_CAPABIILTY);
+ if (capabilities != null) {
+ setCapabilities(capabilities.split(","));
+ }
+ }
+
+ public String getServerUrl() {
+ return serverUrl;
+ }
+
+ public void setServerUrl(String serverUrl) {
+ this.serverUrl = serverUrl;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getProjectArea() {
+ return projectArea;
+ }
+
+ public void setProjectArea(String projectArea) {
+ this.projectArea = projectArea;
+ }
+
+ public OslcClient getClient() {
+ return client;
+ }
+
+ @OslcDescription("Descriptive text (reference: Dublin Core) about resource represented as rich text in XHTML content.")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "description")
+ @OslcTitle("Description")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @OslcDescription("Title (reference: Dublin Core) or often a single line summary of the resource represented as rich text in XHTML content.")
+ @OslcOccurs(Occurs.ExactlyOne)
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "title")
+ @OslcTitle("Title")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ @OslcDescription("Type (reference: Dublin Core) of adapter.")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "type")
+ @OslcTitle("Type")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "hostname")
+ @OslcTitle("Hostname")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getHostname() {
+ return hostname;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "ipAddress")
+ @OslcTitle("IP Address")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "pollingInterval")
+ @OslcTitle("Polling Interval")
+ @OslcValueType(ValueType.Integer)
+ public Integer getPollingInterval() {
+ return pollingInterval;
+ }
+
+ public void setPollingInterval(Integer pollingInterval) {
+ this.pollingInterval = pollingInterval;
+ }
+
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "macAddress")
+ @OslcTitle("MAC Address")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getMacAddress() {
+ return macAddress;
+ }
+
+ public void setMacAddress(String macAddress) {
+ this.macAddress = macAddress;
+ }
+
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "fQDN")
+ @OslcTitle("Fully Qualified Domain Name")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getfQDN() {
+ return fqdn;
+ }
+
+ public void setfQDN(String fqdn) {
+ this.fqdn = fqdn;
+ }
+
+ @OslcDescription("Capability of the adapter like Execute, Record, Import.")
+ @OslcName("capability")
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "capability")
+ @OslcTitle("Capability")
+ @OslcOccurs(Occurs.OneOrMany)
+ public String[] getCapabilities() {
+ return capabilities.toArray(new String[capabilities.size()]);
+ }
+
+ public void addCapability(final String capability) {
+ this.capabilities.add(capability);
+ }
+
+ public void setCapabilities(final String[] capabilities) {
+ this.capabilities.clear();
+
+ if (capabilities != null) {
+ this.capabilities.addAll(Arrays.asList(capabilities));
+ }
+ }
+
+ @OslcDescription("Relation (reference: Dublin Core) of adapter to other QM resources.")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "relation")
+ @OslcTitle("Relation")
+ @OslcValueType(ValueType.Resource)
+ public URI getRelation() {
+ return relation;
+ }
+
+ public void setRelation(URI relation) {
+ this.relation = relation;
+ }
+
+ @OslcDescription("URL to poll for work assigned to the adapter.")
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "workAvailableUrl")
+ @OslcTitle("Work Available URL")
+ @OslcValueType(ValueType.Resource)
+ public URI getWorkAvailableUrl() {
+ return workAvailableUrl;
+ }
+
+ public void setWorkAvailableUrl(URI workAvailableUrl) {
+ this.workAvailableUrl = workAvailableUrl;
+ }
+
+ @OslcDescription("Boolean indicating whether work is available for the adapter.")
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "workAvailable")
+ @OslcTitle("Is Work Available")
+ @OslcValueType(ValueType.Boolean)
+ public Boolean getWorkAvailable() {
+ return workAvailable;
+ }
+
+ public void setWorkAvailable(Boolean workAvailable) {
+ this.workAvailable = workAvailable;
+ }
+
+ @OslcDescription("Identifier (reference: Dublin Core) for the adapter.")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "identifier")
+ @OslcTitle("Identifier")
+ @OslcValueType(ValueType.XMLLiteral)
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ @OslcDescription("Last modification date (reference: Dublin Core) of the adapter.")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "modified")
+ @OslcTitle("Modified")
+ @OslcValueType(ValueType.DateTime)
+ public Date getModified() {
+ return modified;
+ }
+
+ public void setModified(Date modified) {
+ this.modified = modified;
+ }
+
+ @OslcDescription("Creator (reference: Dublin Core) of the adapter.")
+ @OslcPropertyDefinition(OslcConstants.DCTERMS_NAMESPACE + "creator")
+ @OslcTitle("Creator")
+ @OslcValueType(ValueType.Resource)
+ public URI getCreator() {
+ return creator;
+ }
+
+ public void setCreator(URI creator) {
+ this.creator = creator;
+ }
+
+ @OslcDescription("Resource Shape that provides hints as to resource property value-types and allowed values. ")
+ @OslcPropertyDefinition(OslcConstants.OSLC_CORE_NAMESPACE + "instanceShape")
+ @OslcRange(OslcConstants.TYPE_RESOURCE_SHAPE)
+ @OslcTitle("Instance Shape")
+ public URI getInstanceShape() {
+ return instanceShape;
+ }
+
+ public void setInstanceShape(URI instanceShape) {
+ this.instanceShape = instanceShape;
+ }
+
+ @OslcDescription("URL for the machine that the adapter is running on.")
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "runsOnMachine")
+ @OslcTitle("Runs On Machine")
+ @OslcValueType(ValueType.Resource)
+ public URI getRunsOnMachine() {
+ return runsOnMachine;
+ }
+
+ public void setRunsOnMachine(URI runsOnMachine) {
+ this.runsOnMachine = runsOnMachine;
+ }
+
+ @OslcDescription("The scope of a resource is a URI for the resource's OSLC Service Provider.")
+ @OslcPropertyDefinition(OslcConstants.OSLC_CORE_NAMESPACE
+ + "serviceProvider")
+ @OslcRange(OslcConstants.TYPE_SERVICE_PROVIDER)
+ @OslcTitle("Service Provider")
+ public URI getServiceProvider() {
+ return serviceProvider;
+ }
+
+ public void setServiceProvider(URI serviceProvider) {
+ this.serviceProvider = serviceProvider;
+ }
+
+ @OslcDescription("URL for the work assigned to the adapter.")
+ @OslcPropertyDefinition(NAMESPACE_URI_JAZZ_QM + "assignedWorkUrl")
+ @OslcTitle("Work Available URL")
+ @OslcValueType(ValueType.Resource)
+ public URI getAssignedWorkUrl() {
+ return assignedWorkUrl;
+ }
+
+ public void setAssignedWorkUrl(URI assignedWorkUrl) {
+ this.assignedWorkUrl = assignedWorkUrl;
+ }
+
+ /**
+ * Start this adapter. It will begin polling the server for assigned
+ * Automation Requests based on the polling interval. When the server has an
+ * Automation Request for the adapter then control will be passed back to
+ * the caller of this method via the
+ * {@link IAutomationRequestHandler#handleAutomationRequest(AutomationRequest, AutomationAdapter)}
+ * interface. The AutomationResult returned by the IAutomationRequestHandler
+ * will be sent back to the Automation Service Provider, and polling will
+ * resume.
+ *
+ * Before calling this method the adapter needs to be logged in and
+ * registered with the Automation Service Provider.
+ *
+ * @param requestHandler
+ * @throws AutomationException
+ * @throws InterruptedException
+ * @throws IOException
+ * @throws OAuthException
+ * @throws URISyntaxException
+ * @throws ResourceNotFoundException
+ * @see {@link IAutomationRequestHandler}
+ * @see #login()
+ * @see #registerWithServiceProvider()
+ */
+ public void start(IAutomationRequestHandler requestHandler)
+ throws AutomationException, InterruptedException, IOException,
+ URISyntaxException, OAuthException, ResourceNotFoundException {
+
+ if (!isRegistered) {
+
+ throw new AutomationException(
+ "Adapter is not registered with the Automation Service Provider.");
+
+ }
+
+ isStopped = false;
+
+ // continue to poll the automation provider until the adapter is stopped
+ while (true) {
+
+ if (isStopped) {
+ break;
+ }
+
+ String nextAssignmentUrl = getNextAssignmentUrl();
+
+ if (nextAssignmentUrl != null) {
+
+ AutomationRequest request = takeAssignment(nextAssignmentUrl);
+
+ AutomationResult result = requestHandler
+ .handleAutomationRequest(request, this);
+
+ if (result != null) {
+
+ populateResultFromRequest(request, result);
+
+ saveResult(result);
+
+ }
+
+ completeRequest(request);
+
+ }
+
+ if (!isStopped) {
+ Thread.sleep(1000L * pollingInterval);
+ }
+ }
+ }
+
+ /**
+ * Stop the adapter. Calling this method will cause the adapter to stop
+ * polling the Automation Service Provider and eventually return control of
+ * the execution thread back to the caller of this adapter's
+ * {@link #start(IAutomationRequestHandler)} method.
+ */
+ public void stop() {
+ isStopped = true;
+ }
+
+ /**
+ * Send the Automation Result to the Automation Service Provider
+ *
+ * @param result
+ * @throws IOException
+ * @throws OAuthException
+ * @throws URISyntaxException
+ * @throws AutomationException
+ * @throws ResourceNotFoundException
+ */
+ private void saveResult(AutomationResult result) throws IOException,
+ OAuthException, URISyntaxException, AutomationException,
+ ResourceNotFoundException {
+
+ ClientResponse response;
+ String resultCreationFactoryUrl;
+
+ synchronized (client) {
+
+ // Find the OSLC Service Provider for the project area we want to
+ // work with
+ String serviceProviderUrl = client.lookupServiceProviderUrl(
+ rootServicesHelper.getCatalogUrl(), projectArea);
+
+ resultCreationFactoryUrl = client.lookupCreationFactory(
+ serviceProviderUrl, AutomationConstants.AUTOMATION_DOMAIN,
+ AutomationConstants.TYPE_AUTOMATION_RESULT);
+
+ response = client.createResource(resultCreationFactoryUrl, result,
+ OslcMediaType.APPLICATION_RDF_XML);
+
+ response.consumeContent();
+
+ }
+
+ if (response.getStatusCode() != HttpStatus.SC_CREATED) {
+
+ throw new AutomationException(
+ "Failed to create an AutomationResult at "
+ + resultCreationFactoryUrl + ". "
+ + response.getStatusCode() + ": "
+ + response.getMessage());
+
+ }
+
+ }
+
+ /**
+ * Set the Automation Request's properties as complete and update the
+ * Automation Service Provider
+ *
+ * @param request
+ * @throws URISyntaxException
+ * @throws AutomationException
+ */
+ private void completeRequest(AutomationRequest request)
+ throws AutomationException {
+
+ request.setStates(new URI[] { URI.create(
+ AutomationConstants.STATE_COMPLETE) });
+
+ request.getExtendedProperties().put(PROPERTY_RQM_PROGRESS,
+ new Integer(100));
+
+ URI updateUri = appendOslcProperties(request.getAbout(),
+ "oslc_auto:state", "rqm_qm:progress");
+
+ ClientResponse response;
+
+ synchronized (client) {
+
+ response = client.updateResource(updateUri.toString(), request,
+ OslcMediaType.APPLICATION_RDF_XML);
+
+ response.consumeContent();
+
+ }
+
+ if (response.getStatusCode() != HttpStatus.SC_OK) {
+
+ throw new AutomationException(
+ "Failed to update AutomationResult at "
+ + updateUri.toString() + ". "
+ + response.getStatusCode() + ": "
+ + response.getMessage());
+
+ }
+ }
+
+ /**
+ * Check for any queued Automation Requests at the Automation Service
+ * Provider. If an AutomationRequest is queued then return its URL.
+ *
+ * @return
+ * @throws AutomationException
+ * @throws IOException
+ * @throws OAuthException
+ * @throws URISyntaxException
+ */
+ private String getNextAssignmentUrl() throws AutomationException,
+ IOException, OAuthException, URISyntaxException {
+
+ if (assignedWorkUrl == null) {
+
+ throw new AutomationException(
+ "The assignedWorkUrl property must be set in order to poll the automation provider.");
+
+ }
+
+ Model model;
+
+ synchronized (client) {
+
+ ClientResponse response = client.getResource(
+ assignedWorkUrl.toString(),
+ OslcMediaType.APPLICATION_RDF_XML);
+
+ InputStream is = response.getEntity(InputStream.class);
+
+ model = ModelFactory.createDefaultModel();
+
+ model.read(is, assignedWorkUrl.toString());
+
+ }
+
+ StmtIterator stmtIter = model.listStatements(null, RDF.type, model
+ .createResource(AutomationConstants.TYPE_AUTOMATION_REQUEST));
+
+ if (stmtIter.hasNext()) {
+ return stmtIter.next().getSubject().getURI();
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Notify the Automation Service Provider that the request has been taken by
+ * this adapter.
+ *
+ * @param requestUrl
+ * @return
+ * @throws IOException
+ * @throws OAuthException
+ * @throws URISyntaxException
+ * @throws AutomationException
+ */
+ private AutomationRequest takeAssignment(String requestUrl)
+ throws IOException, OAuthException, URISyntaxException,
+ AutomationException {
+
+ AutomationRequest request;
+ URI updateUri;
+ ClientResponse response;
+
+ synchronized (client) {
+
+ request = client.getResource(requestUrl,
+ OslcMediaType.APPLICATION_RDF_XML).getEntity(
+ AutomationRequest.class);
+
+ request.getExtendedProperties().put(PROPERTY_RQM_TAKEN,
+ Boolean.TRUE);
+
+ request.setStates(new URI[] { URI.create(
+ AutomationConstants.STATE_IN_PROGRESS) });
+
+ updateUri = appendOslcProperties(URI.create(requestUrl),
+ "oslc_auto:state", "rqm_qm:taken");
+
+ response = client.updateResource(updateUri.toString(), request,
+ OslcMediaType.APPLICATION_RDF_XML);
+
+ response.consumeContent();
+
+ }
+
+ if (response.getStatusCode() != HttpStatus.SC_OK) {
+
+ throw new AutomationException(
+ "Failed to update AutomationRequest at "
+ + updateUri.toString() + ". "
+ + response.getStatusCode() + ": "
+ + response.getMessage());
+
+ }
+
+ return request;
+ }
+
+ /**
+ * Register with the Automation Service Provider. Before calling this method
+ * the adapter needs to be logged in to the Automation Service Provider.
+ *
+ * @throws AutomationException
+ * @throws IOException
+ * @throws OAuthException
+ * @throws URISyntaxException
+ * @throws ResourceNotFoundException
+ * @see #login()
+ */
+ public void register() throws AutomationException, IOException,
+ OAuthException, URISyntaxException, ResourceNotFoundException {
+
+ if (client == null) {
+
+ throw new AutomationException(
+ "The adapter has not logged into the server.");
+
+ }
+
+ AutomationAdapter registeredAdapter;
+ String adapterCreationFactoryUrl;
+ ClientResponse response;
+
+ synchronized (client) {
+
+ // Find the OSLC Service Provider for the project area we want to
+ // work with
+ String serviceProviderUrl = client.lookupServiceProviderUrl(
+ rootServicesHelper.getCatalogUrl(), projectArea);
+
+ adapterCreationFactoryUrl = client.lookupCreationFactory(
+ serviceProviderUrl, AutomationConstants.AUTOMATION_DOMAIN,
+ TYPE_AUTOMATION_ADAPTER);
+
+ response = client.createResource(adapterCreationFactoryUrl, this,
+ OslcMediaType.APPLICATION_RDF_XML);
+
+ response.consumeContent();
+ }
+
+ if (response.getStatusCode() != HttpStatus.SC_CREATED) {
+
+ throw new AutomationException("Failed to register the adapter at "
+ + adapterCreationFactoryUrl + ". "
+ + response.getStatusCode() + ": " + response.getMessage());
+
+ }
+
+ String adapterUrl = response.getHeaders()
+ .getFirst(HttpHeaders.LOCATION);
+
+ synchronized (client) {
+
+ registeredAdapter = client.getResource(adapterUrl,
+ OslcMediaType.APPLICATION_RDF_XML).getEntity(
+ AutomationAdapter.class);
+
+ }
+
+ // copy the server side properties into this local adapter instance
+ copyServerProperties(registeredAdapter);
+
+ isRegistered = true;
+ }
+
+ /**
+ * Unregister with the Automation Service Provider. Before calling this
+ * method the adapter needs to be logged in.
+ *
+ * @throws AutomationException
+ * @throws IOException
+ * @throws OAuthException
+ * @throws URISyntaxException
+ * @see #login()
+ */
+ public void unregister() throws AutomationException, IOException,
+ OAuthException, URISyntaxException {
+
+ if (!isRegistered) {
+
+ throw new AutomationException(
+ "Adapter is not registered with the Automation Service Provider.");
+
+ }
+
+ ClientResponse response;
+
+ synchronized (client) {
+
+ response = client.deleteResource(getAbout().toString());
+
+ response.consumeContent();
+
+ }
+
+ int statusCode = response.getStatusCode();
+
+ if ((statusCode != HttpStatus.SC_OK)
+ && (statusCode != HttpStatus.SC_NOT_FOUND)) {
+
+ throw new AutomationException(
+ "Failed to unregister the adapter at "
+ + getAbout().toString() + ". "
+ + response.getStatusCode() + ": "
+ + response.getMessage());
+
+ }
+
+ isRegistered = false;
+ }
+
+ /**
+ * Copy the server-side properties from a registered adapter into this
+ * adapter
+ *
+ * @param registeredAdapter
+ */
+ private void copyServerProperties(AutomationAdapter registeredAdapter) {
+ setAbout(registeredAdapter.getAbout());
+ setRelation(registeredAdapter.getRelation());
+ setWorkAvailableUrl(registeredAdapter.getWorkAvailableUrl());
+ setWorkAvailable(registeredAdapter.getWorkAvailable());
+ setIdentifier(registeredAdapter.getIdentifier());
+ setModified(registeredAdapter.getModified());
+ setCreator(registeredAdapter.getCreator());
+ setInstanceShape(registeredAdapter.getInstanceShape());
+ setRunsOnMachine(registeredAdapter.getRunsOnMachine());
+ setServiceProvider(registeredAdapter.getServiceProvider());
+ setAssignedWorkUrl(registeredAdapter.getAssignedWorkUrl());
+ }
+
+ /**
+ * Login to the Automation Service Provider with the username, password, and
+ * project area currently set on this adapter
+ *
+ * @throws RootServicesException
+ * @throws JazzAuthErrorException
+ * @throws JazzAuthFailedException
+ */
+ public void login() throws RootServicesException, JazzAuthFailedException,
+ JazzAuthErrorException {
+
+ login(getServerUrl(), getUsername(), getPassword(), getProjectArea());
+
+ }
+
+ /**
+ * Login to the Automation Service Provider
+ *
+ * @param serverUrl
+ * @param username
+ * @param password
+ * @param projectArea
+ * @throws RootServicesException
+ * @throws JazzAuthErrorException
+ * @throws JazzAuthFailedException
+ */
+ public void login(String serverUrl, String username, String password,
+ String projectArea) throws RootServicesException,
+ JazzAuthFailedException, JazzAuthErrorException {
+
+ assert serverUrl != null;
+ assert username != null;
+ assert projectArea != null;
+
+ // Initialize a Jazz rootservices helper and indicate we're looking for
+ // the Automation catalog
+ rootServicesHelper = new JazzRootServicesHelper(serverUrl,
+ OSLCConstants.OSLC_AUTO);
+
+ // Create a new Form Auth client with the supplied user/password
+ client = rootServicesHelper.initFormClient(username, password);
+
+ synchronized (client) {
+
+ client.formLogin();
+
+ }
+
+ }
+
+ /**
+ * Logout of the Automation Service Provider
+ *
+ * <strong>NOT IMPLEMENTED</strong>
+ */
+ public void logout() {
+
+ if (client != null) {
+ // TODO implement when logout support is added to OSLC client
+ // client.logout();
+ }
+
+ client = null;
+
+ }
+
+ /**
+ * Get the Test Script document associated with the Automation Request's
+ * oslc_qm:executesTestScript property.
+ *
+ * Before calling this method the adapter must be logged into the server.
+ *
+ * @param request
+ * @return
+ * @throws IOException
+ * @throws OAuthException
+ * @throws URISyntaxException
+ * @throws AutomationException
+ * @throws SAXException
+ */
+ public Document getScriptDocument(AutomationRequest request)
+ throws IOException, OAuthException, URISyntaxException,
+ AutomationException, SAXException {
+
+ if (client == null) {
+
+ throw new AutomationException(
+ "The adapter has not logged into the server.");
+
+ }
+
+ URI scriptURI = (URI) request.getExtendedProperties().get(
+ PROPERTY_QM_EXECUTES_TEST_SCRIPT);
+
+ if (scriptURI == null) {
+
+ throw new AutomationException(
+ "The AutomationRequest does not have a value for the property : "
+ + PROPERTY_QM_EXECUTES_TEST_SCRIPT);
+
+ }
+
+ scriptURI = appendOslcProperties(scriptURI, "dcterms:relation");
+
+ InputStream is;
+
+ synchronized (client) {
+
+ TestScript script = client.getResource(scriptURI.toString(),
+ OslcMediaType.APPLICATION_RDF_XML).getEntity(
+ TestScript.class);
+
+ URI scriptUri = (URI) script.getExtendedProperties().get(
+ PROPERTY_DC_RELATION);
+
+ is = client.getResource(scriptUri.toString(),
+ OslcMediaType.APPLICATION_XML).getEntity(InputStream.class);
+
+ }
+
+ Document document = documentBuilder.parse(is);
+
+ return document;
+ }
+
+ /**
+ * Utility method to add the oslc.properties request parameter onto a URI
+ *
+ * @param uri
+ * @param properties
+ * @return
+ */
+ private URI appendOslcProperties(URI uri, String... properties) {
+
+ String str = uri.toString();
+
+ if (str.indexOf("?") < 0) {
+ str += "?";
+ } else {
+ str += "&";
+ }
+
+ str += "oslc.properties=";
+
+ boolean first = true;
+ for (String property : properties) {
+ if (!first) {
+ str += ",";
+ }
+ str += property;
+ first = false;
+ }
+
+ return URI.create(str);
+
+ }
+
+ /**
+ * Populate the AutomationResult's properties from the Automation Request
+ *
+ * @param request
+ * @param result
+ * @throws AutomationException
+ * @throws URISyntaxException
+ * @throws OAuthException
+ * @throws IOException
+ */
+ private void populateResultFromRequest(AutomationRequest request,
+ AutomationResult result) throws AutomationException, IOException,
+ OAuthException, URISyntaxException {
+
+ result.setProducedByAutomationRequest(request.getAbout());
+
+ URI automationPlan = request.getExecutesAutomationPlan();
+
+ result.setInputParameters(request.getInputParameters());
+
+ Map<QName, Object> requestExtProperties = request
+ .getExtendedProperties();
+
+ Map<QName, Object> resultExtProperties = result.getExtendedProperties();
+
+ resultExtProperties.put(PROPERTY_QM_EXECUTES_TEST_SCRIPT,
+ requestExtProperties.get(PROPERTY_QM_EXECUTES_TEST_SCRIPT));
+
+ if (automationPlan != null) {
+
+ result.setReportsOnAutomationPlan(request
+ .getExecutesAutomationPlan());
+
+ AutomationPlan plan;
+
+ synchronized (client) {
+
+ plan = client.getResource(automationPlan.toString(),
+ OslcMediaType.APPLICATION_RDF_XML).getEntity(
+ AutomationPlan.class);
+
+ }
+
+ URI testcase = (URI) plan.getExtendedProperties().get(
+ PROPERTY_QM_RUNS_TEST_CASE);
+
+ result.getExtendedProperties().put(
+ PROPERTY_QM_REPORTS_ON_TEST_CASE, testcase);
+ }
+
+ }
+
+ /**
+ * Send a heartbeat to the AutomationProvider to let it know that the
+ * adapter is still available for work.
+ *
+ * @throws URISyntaxException
+ * @throws AutomationException
+ */
+ private void sendHeartbeat() throws URISyntaxException, AutomationException {
+
+ if (!isRegistered) {
+
+ throw new AutomationException(
+ "Adapter is not registered with the Automation Service Provider");
+
+ }
+
+ URI updateUrl = appendOslcProperties(getAbout(),
+ "rqm_qm:pollingInterval");
+
+ ClientResponse response;
+
+ synchronized (client) {
+
+ response = client.updateResource(updateUrl.toString(), this,
+ OslcMediaType.APPLICATION_RDF_XML);
+
+ response.consumeContent();
+
+ }
+
+ if (response.getStatusCode() != HttpStatus.SC_OK) {
+
+ throw new AutomationException(
+ "Failed to update adapter heartbeat at "
+ + updateUrl.toString() + ". "
+ + response.getStatusCode() + ": "
+ + response.getMessage());
+
+ }
+
+ }
+
+ /**
+ * Upload a File to the Automation Service Provider using the
+ * rqm_qm:uploadAttachmentUrl property in the Automation Request.
+ *
+ * Before calling this method the adapter needs to be logged into the
+ * Automation Service Provider.
+ *
+ * @param file
+ * @param request
+ * @return
+ * @throws AutomationException
+ * @throws URISyntaxException
+ * @throws IOException
+ */
+ public URI uploadAttachment(File file, AutomationRequest request)
+ throws AutomationException, URISyntaxException, IOException {
+
+ if (client == null) {
+
+ throw new AutomationException(
+ "The adapter has not logged into the server.");
+
+ }
+
+ URI attachmentUploadUrl = (URI) request.getExtendedProperties().get(
+ PROPERTY_RQM_UPLOAD_ATTACHMENT_URL);
+
+ String fileName = file.getName();
+
+ byte[] bytes = FileUtils.readFileToByteArray(file);
+
+ OutPart outPart = new OutPart();
+ outPart.setContentType("application/octet-stream; name=" + fileName);
+ outPart.addHeader("Content-Transfer-Encoding", "binary");
+ outPart.addHeader("Content-Disposition", "form-data; name=\""
+ + fileName + "\"; filename=\"" + fileName + "\"");
+ outPart.addHeader("Content-Length", String.valueOf(bytes.length));
+ outPart.setBody(bytes);
+
+ String boundary = "---------------------------"
+ + UUID.randomUUID().toString();
+
+ BufferedOutMultiPart requestEntity = new BufferedOutMultiPart();
+ requestEntity.addPart(outPart);
+ requestEntity.setBoundary(boundary);
+
+ ClientResponse response;
+
+ synchronized (client) {
+
+ response = client.createResource(attachmentUploadUrl.toString(),
+ requestEntity, "multipart/form-data; boundary=" + boundary);
+
+ response.consumeContent();
+
+ }
+
+ if (response.getStatusCode() != HttpStatus.SC_CREATED) {
+
+ throw new AutomationException("Failed to upload attachment at "
+ + attachmentUploadUrl.toString() + ". "
+ + response.getStatusCode() + ": " + response.getMessage());
+
+ }
+
+ String location = response.getHeaders().getFirst(HttpHeaders.LOCATION);
+
+ return URI.create(location);
+
+ }
+
+ /**
+ * Set the current progress percentage of the Automation. If the provided
+ * value is greater than 99 then it is adjusted down to 99 since the
+ * progress should not reach 100 until the {@link IAutomationRequestHandler}
+ * has finished processing the request and returned control back to this
+ * Adapter.
+ *
+ * Before calling this method the adapter needs to be logged in and
+ * registered with the automation service provider.
+ *
+ * @param request
+ * @param i
+ * An integer less than 100
+ * @throws URISyntaxException
+ * @throws AutomationException
+ */
+ public void setProgress(AutomationRequest request, int i)
+ throws URISyntaxException, AutomationException {
+
+ if (!isRegistered) {
+
+ throw new AutomationException(
+ "Adapter is not registered with the Automation Service Provider");
+
+ }
+
+ // don't allow progress to surpass 99 until the request has completed
+ if (i > 99) {
+ i = 99;
+ }
+
+ request.getExtendedProperties().put(PROPERTY_RQM_PROGRESS,
+ new Integer(i));
+
+ URI updateUri = appendOslcProperties(request.getAbout(),
+ "rqm_qm:progress");
+
+ ClientResponse response;
+
+ synchronized (client) {
+
+ response = client.updateResource(updateUri.toString(), request,
+ OslcMediaType.APPLICATION_RDF_XML);
+
+ response.consumeContent();
+
+ }
+
+ if (response.getStatusCode() != HttpStatus.SC_OK) {
+
+ throw new AutomationException(
+ "Failed to update AutomationResult at "
+ + updateUri.toString() + ". "
+ + response.getStatusCode() + ": "
+ + response.getMessage());
+
+ }
+
+ }
+
+ /**
+ * Cancel an Automation Request by updating its desiredState property to
+ * Canceled.
+ *
+ * Before calling this method the adapter needs to be logged into the
+ * Automation Service Provider.
+ *
+ * @param request
+ * @throws AutomationException
+ * @see http://open-services.net/wiki/automation/OSLC-Automation-Specification-Version-2.0/#Canceling-the-execution-of-an-automation
+ */
+ public void cancel(AutomationRequest request) throws AutomationException {
+
+ if (client == null) {
+
+ throw new AutomationException(
+ "The adapter has not logged into the server.");
+
+ }
+
+ request.setDesiredState(URI.create(AutomationConstants.STATE_CANCELED));
+
+ // Some automation providers require the client to set the state to canceled
+ request.setStates(new URI[] { URI
+ .create(AutomationConstants.STATE_CANCELED) });
+
+ URI updateUri = appendOslcProperties(request.getAbout(),
+ "oslc_auto:state");
+
+ ClientResponse response;
+
+ synchronized (client) {
+
+ response = client.updateResource(updateUri.toString(), request,
+ OslcMediaType.APPLICATION_RDF_XML);
+
+ response.consumeContent();
+
+ }
+
+ if (response.getStatusCode() != HttpStatus.SC_OK) {
+
+ throw new AutomationException(
+ "Failed to update AutomationResult at "
+ + updateUri.toString() + ". "
+ + response.getStatusCode() + ": "
+ + response.getMessage());
+
+ }
+ }
+
+ /**
+ * A runnable which can be used by a Thread created by the program
+ * controlling the adapter.
+ *
+ * The runnable will send heart beats to the Automation Service Provider at
+ * the polling interval.
+ *
+ * Stopping the adapter will also stop this runnable.
+ *
+ * @see AutomationAdapter#stop()
+ *
+ */
+ public class HeartbeatRunnable implements Runnable {
+
+ public HeartbeatRunnable() {
+ }
+
+ public void run() {
+ try {
+ while (true) {
+
+ if (isStopped)
+ break;
+
+ sendHeartbeat();
+
+ Thread.sleep(1000L * pollingInterval);
+
+ }
+ } catch (Exception e) {
+
+ throw new RuntimeException(e);
+
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/AutomationException.java b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/AutomationException.java
new file mode 100644
index 0000000..a08a75d
--- /dev/null
+++ b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/AutomationException.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Paul McMahan <pmcmahan@us.ibm.com> - initial implementation
+ *******************************************************************************/
+package org.eclipse.lyo.client.oslc.samples.automation;
+
+public class AutomationException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public AutomationException() {
+ super();
+ }
+
+ public AutomationException(String message) {
+ super(message);
+ }
+
+ public AutomationException(String message, Throwable t) {
+ super(message, t);
+ }
+
+ public AutomationException(Throwable t) {
+ super(t);
+ }
+
+}
diff --git a/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/IAutomationRequestHandler.java b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/IAutomationRequestHandler.java
new file mode 100644
index 0000000..3c79f46
--- /dev/null
+++ b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/IAutomationRequestHandler.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Paul McMahan <pmcmahan@us.ibm.com> - initial implementation
+ *******************************************************************************/
+package org.eclipse.lyo.client.oslc.samples.automation;
+
+import org.eclipse.lyo.client.oslc.resources.AutomationRequest;
+import org.eclipse.lyo.client.oslc.resources.AutomationResult;
+
+public interface IAutomationRequestHandler {
+
+ /**
+ * Handle the Automation Request assigned to an Automation Adapter and
+ * return an Automation Response.
+ *
+ * @param request
+ * @param adapter
+ * @return
+ * An Automation Response, or null if there is no response to be returned.
+ * @throws AutomationException
+ */
+ AutomationResult handleAutomationRequest(AutomationRequest request,
+ AutomationAdapter adapter) throws AutomationException;
+
+}
diff --git a/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/IConstants.java b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/IConstants.java
new file mode 100644
index 0000000..0a3ebc7
--- /dev/null
+++ b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/IConstants.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Paul McMahan <pmcmahan@us.ibm.com> - initial implementation
+ *******************************************************************************/
+package org.eclipse.lyo.client.oslc.samples.automation;
+
+import javax.xml.namespace.QName;
+
+import org.eclipse.lyo.client.oslc.OSLCConstants;
+
+public interface IConstants {
+
+ String NAMESPACE_URI_JAZZ_QM = "http://jazz.net/ns/qm/rqm#"; //$NON-NLS-1$
+ String NAMESPACE_URI_XHTML = "http://www.w3.org/1999/xhtml"; //$NON-NLS-1$
+ String NAMESPACE_URI_DC_ELEMENTS = "http://purl.org/dc/elements/1.1/"; //$NON-NLS-1$
+
+ String TYPE_AUTOMATION_ADAPTER = NAMESPACE_URI_JAZZ_QM + "AutomationAdapter";
+
+ QName PROPERTY_DC_RELATION = new QName(OSLCConstants.DC, "relation");
+
+ QName PROPERTY_QM_REPORTS_ON_TEST_CASE = new QName(OSLCConstants.OSLC_QM_V2, "reportsOnTestCase");
+ QName PROPERTY_QM_RUNS_TEST_CASE = new QName(OSLCConstants.OSLC_QM_V2, "runsTestCase");
+ QName PROPERTY_QM_EXECUTES_TEST_SCRIPT = new QName(OSLCConstants.OSLC_QM_V2, "executesTestScript");
+ QName PROPERTY_QM_REPORTS_ON_TEST_PLAN = new QName(OSLCConstants.OSLC_QM_V2, "reportsOnTestPlan");
+
+ QName PROPERTY_RQM_TAKEN = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "taken");
+ QName PROPERTY_RQM_PROGRESS = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "progress");
+ QName PROPERTY_RQM_EXECUTES_ON_ADAPTER = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "executesOnAdapter");
+ QName PROPERTY_RQM_REQUEST_TYPE = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "requestType");
+ QName PROPERTY_RQM_UPLOAD_ATTACHMENT_URL = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "uploadAttachmentUrl");
+ QName PROPERTY_RQM_TEST_SUITE_RESULT = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "testSuiteResult");
+ QName PROPERTY_RQM_STATE_URL = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "stateUrl");
+ QName PROPERTY_RQM_START_TIME = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "startTime");
+ QName PROPERTY_RQM_END_TIME = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "endTime");
+ QName PROPERTY_RQM_ATTACHMENT = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "attachment");
+ QName PROPERTY_RQM_EXECUTED_ON_MACHINE = new QName(IConstants.NAMESPACE_URI_JAZZ_QM, "executedOnMachine");
+
+}
diff --git a/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/RQMAutomationSample.java b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/RQMAutomationSample.java
new file mode 100644
index 0000000..2833048
--- /dev/null
+++ b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/RQMAutomationSample.java
@@ -0,0 +1,296 @@
+/*******************************************************************************
+ * Copyright (c) 2012 IBM Corporation.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *
+ * Paul McMahan <pmcmahan@us.ibm.com> - initial implementation
+ *******************************************************************************/
+package org.eclipse.lyo.client.oslc.samples.automation;
+
+import java.io.File;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Date;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.eclipse.lyo.client.oslc.resources.AutomationConstants;
+import org.eclipse.lyo.client.oslc.resources.AutomationRequest;
+import org.eclipse.lyo.client.oslc.resources.AutomationResult;
+import org.eclipse.lyo.client.oslc.resources.ParameterInstance;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Sample of registering an external agent (adapter) with an Automation Service
+ * Provider and executing Automation Requests like an RQM test execution adapter.
+ */
+public class RQMAutomationSample implements IConstants, IAutomationRequestHandler, UncaughtExceptionHandler {
+
+ private static final Logger logger = Logger.getLogger(RQMAutomationSample.class.getName());
+
+ private AutomationAdapter adapter;
+
+ /**
+ * Start the sample
+ *
+ * @param args
+ * @throws Exception
+ */
+ public static void main(String[] args) throws Exception {
+
+ new RQMAutomationSample().begin();
+
+ }
+
+ /**
+ * Login to the Automation Service Provider and start polling for Automation
+ * Requests
+ *
+ * @param args
+ * @throws Exception
+ */
+ private void begin() throws Exception {
+
+ // Initialize a local adapter instance
+ Properties adapterProperties = new Properties();
+
+ adapterProperties.load(RQMAutomationSample.class
+ .getResourceAsStream("adapter.properties"));
+
+ adapter = new AutomationAdapter(adapterProperties);
+
+ logger.info("Logging into service provider at "
+ + adapter.getServerUrl());
+
+ // Login to the server using the properties provided in the constructor
+ adapter.login();
+
+ logger.info("Registering with service provider");
+
+ // Register the adapter with the service provider, letting it know that
+ // the adapter is available for work
+ adapter.register();
+
+ try {
+
+ logger.info("Starting heart beat thread for adapter at "
+ + adapter.getAbout());
+
+ // create a heartbeat thread and start it
+ Thread heartbeatThread = new Thread(
+ adapter.new HeartbeatRunnable(), "Adapter Heartbeat Thread");
+ heartbeatThread.setUncaughtExceptionHandler(this);
+ heartbeatThread.start();
+
+ logger.info("Starting adapter polling at "
+ + adapter.getAssignedWorkUrl());
+
+ // start polling the service provider for Automation Requests.
+ // this call will block until adapter.stop() is called in
+ // handleAutomationRequest() below
+ adapter.start(this);
+
+ } finally {
+
+ // notify the Automation Service Provider that this adapter is no
+ // longer available for work
+ adapter.unregister();
+
+ // stop the adapter, in case we are in this catch block due to
+ // an exception being thrown
+ adapter.stop();
+
+ // logout of the Automation Service Provider in order to free server
+ // side resources, licenses, etc. Currently this is a no-op.
+ adapter.logout();
+ }
+ }
+
+ /**
+ * Example callback that demonstrates a few useful things an adapter can do.
+ *
+ * @see IAutomationRequestHandler#handleAutomationRequest(AutomationRequest, AutomationAdapter)
+ */
+ public AutomationResult handleAutomationRequest(AutomationRequest request, AutomationAdapter adapter)
+ throws AutomationException {
+
+ logger.info("Adapter has been assigned an Automation Request at "
+ + request.getAbout());
+
+ AutomationResult result = null;
+
+ try {
+
+ // Create a new automation result
+ result = new AutomationResult();
+
+ // Save the start time in the result
+ result.getExtendedProperties().put(PROPERTY_RQM_START_TIME,
+ new Date(System.currentTimeMillis()));
+
+ // An example of how to get the script for the AutomationRequest.
+ // The script might contain references to resources needed to
+ // execute the test.
+ Document script = adapter.getScriptDocument(request);
+
+ // update progress indication
+ adapter.setProgress(request, 50);
+
+ // execute the script with the parameters from the Automation Request
+ executeScript(script, request.getInputParameters());
+
+ // update progress indication
+ adapter.setProgress(request, 99);
+
+ // Upload an attachment for the result
+ File attachment = getSampleFile();
+ URI attachmentURI = adapter.uploadAttachment(attachment, request);
+
+ // Set the attachment URI in the result
+ result.getExtendedProperties().put(PROPERTY_RQM_ATTACHMENT, attachmentURI);
+
+ // Add some rich text to the result
+ Element xhtmlTableElement = createXhtmlTable();
+ QName contributionQname = new QName(AutomationConstants.AUTOMATION_DOMAIN, "contribution");
+ result.getExtendedProperties().put(contributionQname, xhtmlTableElement);
+
+ // Set the verdict for the result
+ result.addVerdict(new URI(AutomationConstants.VERDICT_PASSED));
+
+ // Save the end time in the result
+ result.getExtendedProperties().put(PROPERTY_RQM_END_TIME,
+ new Date(System.currentTimeMillis()));
+
+ } catch (Exception e) {
+
+ // cancel the request since it could not be completed
+ adapter.cancel(request);
+
+ throw new AutomationException(e);
+
+ }
+
+ logger.info("Returning a result with verdict "
+ + result.getVerdicts()[0]);
+
+ return result;
+ }
+
+ /**
+ * Execute the script with the provided input parameters.
+ *
+ * @param script
+ * @param inputParameters
+ * @throws InterruptedException
+ */
+ private void executeScript(Document script,
+ ParameterInstance[] inputParameters) throws InterruptedException {
+
+ String scriptTitle = script.getDocumentElement()
+ .getElementsByTagNameNS(NAMESPACE_URI_DC_ELEMENTS, "title")
+ .item(0).getTextContent();
+
+ logger.info("Running script named '" + scriptTitle +"'");
+
+ logger.info("Input parameters:");
+ for (ParameterInstance parameter : inputParameters) {
+ String paramStr = "\t" + parameter.getName() + ": "
+ + parameter.getValue();
+ logger.info(paramStr);
+ }
+
+ /*
+ * Add code here to execute the test script
+ */
+ Thread.sleep(1000);
+
+ }
+
+ /**
+ * Create an element for a simple XHTML table
+ *
+ * @return
+ * @throws ParserConfigurationException
+ */
+ private Element createXhtmlTable() throws ParserConfigurationException {
+
+ Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+
+ Element divElement = document.createElementNS(IConstants.NAMESPACE_URI_XHTML, "div");
+
+ Element tableElement = document.createElementNS(IConstants.NAMESPACE_URI_XHTML, "table");
+ divElement.appendChild(tableElement);
+ tableElement.setAttribute("border", "1");
+
+ Element tr1Element = document.createElementNS(IConstants.NAMESPACE_URI_XHTML, "tr");
+ Element tr2Element = document.createElementNS(IConstants.NAMESPACE_URI_XHTML, "tr");
+ tableElement.appendChild(tr1Element);
+ tableElement.appendChild(tr2Element);
+
+ Element th1Element = document.createElementNS(IConstants.NAMESPACE_URI_XHTML, "th");
+ Element th2Element = document.createElementNS(IConstants.NAMESPACE_URI_XHTML, "th");
+ tr1Element.appendChild(th1Element);
+ tr1Element.appendChild(th2Element);
+ th1Element.setTextContent("Column 1");
+ th2Element.setTextContent("Column 2");
+
+ Element td1Element = document.createElementNS(IConstants.NAMESPACE_URI_XHTML, "td");
+ Element td2Element = document.createElementNS(IConstants.NAMESPACE_URI_XHTML, "td");
+ tr2Element.appendChild(td1Element);
+ tr2Element.appendChild(td2Element);
+ td1Element.setTextContent("Value 1");
+ td2Element.setTextContent("Value 2");
+
+ return divElement;
+ }
+
+ /**
+ * Get a sample file from the class loader
+ *
+ * @return
+ * @throws URISyntaxException
+ */
+ private File getSampleFile () throws URISyntaxException {
+
+ String packagePath = getClass().getPackage().getName().replace('.', '/');
+
+ String sampleFilePath = packagePath + "/sample.png";
+
+ URL sampleURL = getClass().getClassLoader().getResource(sampleFilePath);
+
+ File sampleImageFile = new File(sampleURL.toURI());
+
+ return sampleImageFile;
+ }
+
+ /**
+ * Called when the heartbeat thread throws an uncaught Exception
+ *
+ * @param thread
+ * @param throwable
+ */
+ public void uncaughtException(Thread thread, Throwable throwable) {
+
+ logger.severe("Adapter heartbeat running in Thread " + thread.getName()
+ + " threw an uncaught exception.");
+
+ adapter.stop();
+
+ }
+
+}
diff --git a/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/adapter.properties b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/adapter.properties
new file mode 100644
index 0000000..702a00d
--- /dev/null
+++ b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/adapter.properties
@@ -0,0 +1,14 @@
+serverUrl=https://rqm.ibm.com:9443/qm
+username=TestJazzAdmin1
+password=password
+projectArea=Quality Manager
+title=Lyo Test Automation Adapter
+description=Test Automation Adapter created by Eclipse Lyo sample
+type=org.eclipse.lyo.clients.oslc.sample
+hostname=rqm.ibm.com
+ipAddress=127.0.0.1
+pollingInterval=5
+macAddress=50:c5:47:97:55:15
+fqdn=ibm.com
+capability=Execute
+
diff --git a/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/sample.png b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/sample.png
new file mode 100644
index 0000000..cc81c5a
--- /dev/null
+++ b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/sample.png
Binary files differ