| author | pmcmahan | 2012-09-28 12:22:19 (EDT) |
|---|---|---|
| committer | pmcmahan | 2012-09-28 12:22:19 (EDT) |
| commit | 32994209666b0b55651832ab54a92f3ddb25a846 (patch) (side-by-side diff) | |
| tree | 8f278084bdd0dd8e5829cb8d400164855f816a1c | |
| parent | 8ad7db9ca8ce30c54c8138554a64f572200d45e0 (diff) | |
| download | org.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
Change-Id: I9a38a0ad0616e81b9609ffee60d38700d230f027
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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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 --- a/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 Binary files differnew file mode 100644 index 0000000..cc81c5a --- a/dev/null +++ b/org.eclipse.lyo.samples.clients/src/main/java/org/eclipse/lyo/client/oslc/samples/automation/sample.png |

