Java tutorial
/* * Copyright 2012 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.artificer.client; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HttpContext; import org.artificer.atom.ArtificerAtomUtils; import org.artificer.atom.archive.ArtificerArchive; import org.artificer.atom.beans.HttpResponseBean; import org.artificer.client.audit.AuditResultSet; import org.artificer.client.auth.AuthenticationProvider; import org.artificer.client.auth.BasicAuthenticationProvider; import org.artificer.client.i18n.Messages; import org.artificer.client.ontology.OntologySummary; import org.artificer.common.query.ArtifactSummary; import org.artificer.client.query.QueryResultSet; import org.artificer.common.ArtifactType; import org.artificer.common.ArtificerConstants; import org.artificer.common.ArtificerModelUtils; import org.artificer.common.MediaType; import org.artificer.common.error.ArtificerServerException; import org.jboss.downloads.artificer._2013.auditing.AuditEntry; import org.jboss.resteasy.client.ClientExecutor; import org.jboss.resteasy.client.ClientResponse; import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor; import org.jboss.resteasy.plugins.providers.atom.Category; import org.jboss.resteasy.plugins.providers.atom.Entry; import org.jboss.resteasy.plugins.providers.atom.Feed; import org.jboss.resteasy.plugins.providers.atom.app.AppCategories; import org.jboss.resteasy.plugins.providers.atom.app.AppCollection; import org.jboss.resteasy.plugins.providers.atom.app.AppService; import org.jboss.resteasy.plugins.providers.atom.app.AppWorkspace; import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.MultipartConstants; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput; import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartRelatedOutput; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactType; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.StoredQuery; import org.w3._1999._02._22_rdf_syntax_ns_.RDF; import javax.xml.namespace.QName; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; /** * Class used to communicate with the S-RAMP server via the S-RAMP Atom API. * * @author eric.wittmann@redhat.com * @author Brett Meyer */ public class ArtificerAtomApiClient { private String endpoint; private String srampEndpoint; private String artificerEndpoint; private boolean validating; private Set<String> enabledFeatures = new HashSet<String>(); private Locale locale; private AuthenticationProvider authProvider; /** * Constructor. * @param endpoint */ public ArtificerAtomApiClient(String endpoint) { this.endpoint = endpoint; if (this.endpoint.endsWith("/")) { this.endpoint = this.endpoint.substring(0, this.endpoint.length() - 1); } if (this.endpoint.endsWith("/s-ramp")) { this.endpoint = this.endpoint.substring(0, this.endpoint.length() - 7); } if (this.endpoint.endsWith("/artificer")) { this.endpoint = this.endpoint.substring(0, this.endpoint.length() - 10); } srampEndpoint = endpoint + "/s-ramp"; artificerEndpoint = endpoint + "/artificer"; } /** * Constructor. * @param endpoint * @param validating * @throws ArtificerClientException * @throws ArtificerServerException */ public ArtificerAtomApiClient(String endpoint, boolean validating) throws ArtificerClientException, ArtificerServerException { this(endpoint); this.validating = validating; if (this.validating) { discoverAvailableFeatures(); } } /** * Constructor. * @param endpoint * @param username * @param password * @param validating * @throws ArtificerClientException * @throws ArtificerServerException */ public ArtificerAtomApiClient(final String endpoint, final String username, final String password, final boolean validating) throws ArtificerClientException, ArtificerServerException { this(endpoint, new BasicAuthenticationProvider(username, password), validating); } /** * Constructor. * @param endpoint * @param authenticationProvider * @param validating * @throws ArtificerClientException * @throws ArtificerServerException */ public ArtificerAtomApiClient(final String endpoint, AuthenticationProvider authenticationProvider, final boolean validating) throws ArtificerClientException, ArtificerServerException { this(endpoint); this.authProvider = authenticationProvider; this.validating = validating; if (this.validating) { discoverAvailableFeatures(); } } /** * @return the s-ramp endpoint */ public String getEndpoint() { return this.endpoint; } /** * This method will grab the /s-ramp/servicedocument from the S-RAMP repository * and examine its contents in order to determine the features supported by the * repository. * @throws ArtificerClientException * @throws ArtificerServerException */ private void discoverAvailableFeatures() throws ArtificerClientException, ArtificerServerException { AppService serviceDoc = getServiceDocument(); for (AppWorkspace workspace : serviceDoc.getWorkspace()) { for (AppCollection collection : workspace.getCollection()) { for (AppCategories cats : collection.getCategories()) { for (Category category : cats.getCategory()) { this.enabledFeatures.add(category.getTerm()); } } } } } /** * Asserts that the given feature is enabled. If it is not, then an exception * is thrown. If this client is not set to validating, then this method will * always pass. * @param feature * @throws ArtificerClientException */ private void assertFeatureEnabled(String feature) throws ArtificerClientException { if (this.validating) { if (!this.enabledFeatures.contains(feature)) { throw new ArtificerClientException(Messages.i18n.format("FEATURE_NOT_SUPPORTED")); } } } /** * Asserts that the given feature is enabled. If it is not, then an exception * is thrown. If this client is not set to validating, then this method will * always pass. * @param feature * @throws ArtificerClientException */ private void assertFeatureEnabled(ArtifactType feature) throws ArtificerClientException { if (this.validating) { if (!this.enabledFeatures.contains(feature.getArtifactType().getType())) { throw new ArtificerClientException(Messages.i18n.format("FEATURE_NOT_SUPPORTED")); } } } /** * Gets the S-RAMP service document. * @throws ArtificerClientException * @throws ArtificerServerException */ public AppService getServiceDocument() throws ArtificerClientException, ArtificerServerException { ClientResponse<AppService> response = null; try { String atomUrl = String.format("%1$s/servicedocument", srampEndpoint); ClientRequest request = createClientRequest(atomUrl); response = request.get(AppService.class); return response.getEntity(); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets the full meta-data listing for an Artifact in the S-RAMP repository. This method * does not require the type of artifact. However, it should be noted that if you * <b>have</b> the artifact type, you should instead call: * * {@link ArtificerAtomApiClient#getArtifactMetaData(ArtifactType, String)} * * Use this variant only if you don't know the artifact type (you only know the UUID). * The reason is that the client must first do a query to determine the artifact type * and then make another call to fetch the meta data. * * @param artifactUuid * @throws ArtificerClientException * @throws ArtificerServerException */ public BaseArtifactType getArtifactMetaData(String artifactUuid) throws ArtificerClientException, ArtificerServerException { try { QueryResultSet uuidRS = buildQuery("/s-ramp[@uuid = ?]").parameter(artifactUuid).count(1).query(); if (uuidRS.size() == 0) throw new ArtificerClientException(Messages.i18n.format("ARTIFACT_NOT_FOUND", artifactUuid)); return getArtifactMetaData(uuidRS.iterator().next()); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } } /** * Convenience method to get the artifact meta-data given an artifact summary (which are typically * returned when performing s-ramp queries). * @param artifact * @throws ArtificerClientException * @throws ArtificerServerException */ public BaseArtifactType getArtifactMetaData(ArtifactSummary artifact) throws ArtificerClientException, ArtificerServerException { return getArtifactMetaData(artifact.getArtifactType(), artifact.getUuid()); } /** * Gets the full meta-data listing for an Artifact in the S-RAMP repository. * @param artifactType * @param artifactUuid * @throws ArtificerClientException * @throws ArtificerServerException */ public BaseArtifactType getArtifactMetaData(ArtifactType artifactType, String artifactUuid) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled(artifactType); ClientResponse<Entry> response = null; try { String atomUrl = String.format("%1$s/%2$s/%3$s/%4$s", srampEndpoint, artifactType.getArtifactType().getModel(), artifactType.getArtifactType().getType(), artifactUuid); ClientRequest request = createClientRequest(atomUrl); response = request.get(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapSrampArtifact(artifactType, entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets the content for an artifact as an input stream. The caller must close the resulting * @param artifactType the artifact type * @param artifactUuid the S-RAMP uuid of the artifact * @return an {@link InputStream} to the S-RAMP artifact content * @throws ArtificerClientException * @throws ArtificerServerException */ public InputStream getArtifactContent(ArtifactType artifactType, String artifactUuid) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled(artifactType); try { String atomUrl = String.format("%1$s/%2$s/%3$s/%4$s/media", srampEndpoint, artifactType.getArtifactType().getModel(), artifactType.getArtifactType().getType(), artifactUuid); DefaultHttpClient httpClient = new DefaultHttpClient(); if (this.authProvider != null) { httpClient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { authProvider.provideAuthentication(request); } }); } HttpResponse response = httpClient.execute(new HttpGet(atomUrl)); HttpEntity entity = response.getEntity(); return entity.getContent(); } catch (Throwable e) { throw new ArtificerClientException(e); } } /** * Convenience method for getting the artifact content given an artifact summary (which are typically * returned when performing s-ramp queries). * @param artifact * @throws ArtificerClientException * @throws ArtificerServerException */ public InputStream getArtifactContent(ArtifactSummary artifact) throws ArtificerClientException, ArtificerServerException { return getArtifactContent(artifact.getArtifactType(), artifact.getUuid()); } /** * Creates a new artifact in the S-RAMP repository. Use this method when creating * logical artifacts in the repository (i.e. artifacts without document content). * @param artifact */ public BaseArtifactType createArtifact(BaseArtifactType artifact) throws ArtificerClientException, ArtificerServerException { ArtifactType artifactType = ArtifactType.valueOf(artifact); if (ArtificerModelUtils.isDocumentArtifact(artifact)) { throw new ArtificerClientException(Messages.i18n.format("MISSING_ARTIFACT_CONTEN")); } assertFeatureEnabled(artifactType); ClientResponse<Entry> response = null; try { String type = artifactType.getType(); String atomUrl = String.format("%1$s/%2$s/%3$s", srampEndpoint, artifactType.getModel(), type); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.APPLICATION_ATOM_XML_ENTRY, ArtificerAtomUtils.wrapSrampArtifact(artifact)); response = request.post(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapSrampArtifact(artifactType, entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Creates a new artifact in the repository by uploading a document. The document will * become the core of a new S-RAMP artifact. * @param artifactType * @param content * @param artifactFileName * @throws ArtificerClientException * @throws ArtificerServerException */ public BaseArtifactType uploadArtifact(ArtifactType artifactType, InputStream content, String artifactFileName) throws ArtificerClientException, ArtificerServerException { if (artifactType == null) { return uploadArtifact(content, artifactFileName); } assertFeatureEnabled(artifactType); ClientResponse<Entry> response = null; try { String type = artifactType.getType(); String atomUrl = String.format("%1$s/%2$s/%3$s", srampEndpoint, artifactType.getArtifactType().getModel(), type); ClientRequest request = createClientRequest(atomUrl); if (artifactFileName != null) request.header("Slug", artifactFileName); request.body(artifactType.getMimeType(), content); response = request.post(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapSrampArtifact(artifactType, entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Creates a new artifact in the repository by uploading a document. The document will * become the core of a new S-RAMP artifact. The artifact content is assumed to be available to the server, * so the absolute path is provided (instead of the content itself). * * @param artifactType * @param path * @throws ArtificerClientException * @throws ArtificerServerException */ public BaseArtifactType uploadArtifact(ArtifactType artifactType, String path) throws ArtificerClientException, ArtificerServerException { if (artifactType == null) { return uploadArtifact(path); } assertFeatureEnabled(artifactType); ClientResponse<Entry> response = null; try { String type = artifactType.getType(); String atomUrl = String.format("%1$s/%2$s/%3$s", srampEndpoint, artifactType.getArtifactType().getModel(), type); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.TEXT_PLAIN, path); response = request.post(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapSrampArtifact(artifactType, entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Creates a new artifact in the repository by uploading a document. The document will * become the core of a new S-RAMP artifact. The artifact content is assumed to be available to the server, * so the absolute path is provided (instead of the content itself). * * @param path * @throws ArtificerClientException * @throws ArtificerServerException */ public BaseArtifactType uploadArtifact(String path) throws ArtificerClientException, ArtificerServerException { ClientResponse<Entry> response = null; try { ClientRequest request = createClientRequest(srampEndpoint + "/autodetect"); request.body(MediaType.TEXT_PLAIN, path); response = request.post(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapSrampArtifact(entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Creates a new artifact in the repository by uploading a document. The document will * become the core of a new S-RAMP artifact. * * @param content * @param artifactFileName * @throws ArtificerClientException * @throws ArtificerServerException */ public BaseArtifactType uploadArtifact(InputStream content, String artifactFileName) throws ArtificerClientException, ArtificerServerException { ClientResponse<Entry> response = null; try { ClientRequest request = createClientRequest(srampEndpoint + "/autodetect"); request.header("Slug", artifactFileName); request.body("application/octet-stream", content); response = request.post(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapSrampArtifact(entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Creates a new artifact in the repository by uploading a document. The document will * become the core of a new S-RAMP artifact. * @param baseArtifactType * @param content * @throws ArtificerClientException * @throws ArtificerServerException */ public BaseArtifactType uploadArtifact(BaseArtifactType baseArtifactType, InputStream content) throws ArtificerClientException, ArtificerServerException { ArtifactType artifactType = ArtifactType.valueOf(baseArtifactType); assertFeatureEnabled(artifactType); ClientResponse<Entry> response = null; try { String type = artifactType.getType(); String atomUrl = String.format("%1$s/%2$s/%3$s", srampEndpoint, artifactType.getArtifactType().getModel(), type); ClientRequest request = createClientRequest(atomUrl); MultipartRelatedOutput output = new MultipartRelatedOutput(); //1. Add first part, the S-RAMP entry Entry atomEntry = ArtificerAtomUtils.wrapSrampArtifact(baseArtifactType); MediaType mediaType = new MediaType("application", "atom+xml"); output.addPart(atomEntry, mediaType); //2. Add second part, the content request.body(artifactType.getMimeType(), content); MediaType mediaType2 = MediaType.getInstance(artifactType.getMimeType()); output.addPart(content, mediaType2); //3. Send the request request.body(MultipartConstants.MULTIPART_RELATED, output); response = request.post(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapSrampArtifact(artifactType, entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Performs a batch operation by uploading an s-ramp package archive to the s-ramp server * for processing. The contents of the s-ramp archive will be processed, and the results * will be returned as a Map. The Map is indexed by the S-RAMP Archive entry path, and each * each value in the Map will either be a {@link BaseArtifactType} or an * {@link ArtificerServerException}, depending on success vs. failure of that entry. * * @param archive the s-ramp package archive to upload * @return the collection of results (one per entry in the s-ramp package) * @throws ArtificerClientException * @throws ArtificerServerException */ public Map<String, ?> uploadBatch(ArtificerArchive archive) throws ArtificerClientException, ArtificerServerException { File packageFile = null; InputStream packageStream = null; ClientResponse<MultipartInput> clientResponse = null; try { if (archive.getEntries().isEmpty()) { return new HashMap<String, Object>(); } packageFile = archive.pack(); packageStream = FileUtils.openInputStream(packageFile); ClientRequest request = createClientRequest(srampEndpoint); request.header("Content-Type", "application/zip"); request.body(MediaType.APPLICATION_ZIP, packageStream); clientResponse = request.post(MultipartInput.class); MultipartInput response = clientResponse.getEntity(); List<InputPart> parts = response.getParts(); Map<String, Object> rval = new HashMap<String, Object>(parts.size()); for (InputPart part : parts) { String contentId = part.getHeaders().getFirst("Content-ID"); String path = contentId.substring(1, contentId.lastIndexOf('@')); HttpResponseBean rbean = part.getBody(HttpResponseBean.class, null); if (rbean.getCode() == 201) { Entry entry = (Entry) rbean.getBody(); BaseArtifactType artifact = ArtificerAtomUtils.unwrapSrampArtifact(entry); rval.put(path, artifact); } else if (rbean.getCode() == 409) { if (MediaType.APPLICATION_ARTIFICER_SERVER_EXCEPTION .equals(rbean.getHeaders().get("Content-Type"))) { ArtificerServerException exception = (ArtificerServerException) rbean.getBody(); rval.put(path, exception); } else { String errorReason = (String) rbean.getBody(); ArtificerServerException exception = new ArtificerServerException(errorReason); rval.put(path, exception); } } else { // Only a non-compliant s-ramp impl could cause this ArtificerServerException exception = new ArtificerServerException( Messages.i18n.format("BAD_RETURN_CODE", rbean.getCode(), contentId)); rval.put(path, exception); } } return rval; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { IOUtils.closeQuietly(packageStream); FileUtils.deleteQuietly(packageFile); closeQuietly(clientResponse); } } /** * Called to update the meta-data stored in the s-ramp repository for the given s-ramp * artifact. * @param artifact * @throws ArtificerClientException * @throws ArtificerServerException */ public void updateArtifactMetaData(BaseArtifactType artifact) throws ArtificerClientException, ArtificerServerException { ArtifactType type = ArtifactType.valueOf(artifact); assertFeatureEnabled(type); ClientResponse<?> response = null; try { String artifactModel = type.getArtifactType().getModel(); String artifactType = type.getArtifactType().getType(); if ("ext".equals(type.getArtifactType().getModel()) && type.getExtendedType() != null) { artifactType = type.getExtendedType(); } String artifactUuid = artifact.getUuid(); String atomUrl = String.format("%1$s/%2$s/%3$s/%4$s", srampEndpoint, artifactModel, artifactType, artifactUuid); ClientRequest request = createClientRequest(atomUrl); Entry entry = ArtificerAtomUtils.wrapSrampArtifact(artifact); request.body(MediaType.APPLICATION_ATOM_XML_ENTRY, entry); response = request.put(); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } public BaseArtifactType addComment(String uuid, ArtifactType type, String text) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled(type); ClientResponse<Entry> response = null; try { String artifactModel = type.getArtifactType().getModel(); String artifactType = type.getArtifactType().getType(); if ("ext".equals(type.getArtifactType().getModel()) && type.getExtendedType() != null) { artifactType = type.getExtendedType(); } String atomUrl = String.format("%1$s/%2$s/%3$s/%4$s/comment", srampEndpoint, artifactModel, artifactType, uuid); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.TEXT_PLAIN, text); response = request.post(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapSrampArtifact(type, entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Delets an artifact from the s-ramp repository. * @param uuid * @param type * @throws ArtificerClientException * @throws ArtificerServerException */ public void deleteArtifact(String uuid, ArtifactType type) throws ArtificerClientException, ArtificerServerException { deleteArtifact(uuid, type, false); } /** * Delets an artifact from the s-ramp repository. * @param uuid * @param type * @param force * @throws ArtificerClientException * @throws ArtificerServerException */ public void deleteArtifact(String uuid, ArtifactType type, boolean force) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled(type); ClientResponse<?> response = null; try { String artifactModel = type.getArtifactType().getModel(); String artifactType = type.getArtifactType().getType(); if ("ext".equals(type.getArtifactType().getModel()) && type.getExtendedType() != null) { artifactType = type.getExtendedType(); } String artifactUuid = uuid; String atomUrl; if (force) { atomUrl = String.format("%1$s/%2$s/%3$s/%4$s/force", srampEndpoint, artifactModel, artifactType, artifactUuid); } else { atomUrl = String.format("%1$s/%2$s/%3$s/%4$s", srampEndpoint, artifactModel, artifactType, artifactUuid); } ClientRequest request = createClientRequest(atomUrl); response = request.delete(); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Provides a very simple mechanism for querying. Defaults many of the parameters. * @param srampQuery the s-ramp query (xpath formatted) * @throws ArtificerClientException * @throws ArtificerServerException */ public QueryResultSet query(String srampQuery) throws ArtificerClientException, ArtificerServerException { return query(srampQuery, 0, 20, "name", true); } /** * Executes the given s-ramp query xpath and returns a Feed of the matching artifacts. * @param srampQuery the s-ramp query (xpath formatted) * @param startIndex which index within the result to start (0 indexed) * @param count the size of the page of results to return * @param orderBy the s-ramp property to use for sorting (name, uuid, createdOn, etc) * @param ascending the direction of the sort * @return an Atom {@link Feed} * @throws ArtificerClientException * @throws ArtificerServerException */ public QueryResultSet query(String srampQuery, int startIndex, int count, String orderBy, boolean ascending) throws ArtificerClientException, ArtificerServerException { return query(srampQuery, startIndex, count, orderBy, ascending, null); } /** * Executes the given s-ramp query xpath and returns a Feed of the matching artifacts. * @param srampQuery the s-ramp query (xpath formatted) * @param startIndex which index within the result to start (0 indexed) * @param count the size of the page of results to return * @param orderBy the s-ramp property to use for sorting (name, uuid, createdOn, etc) * @param ascending the direction of the sort * @param propertyNames an optional collection of names of custom s-ramp properties to be returned as part of the result set * @return an Atom {@link Feed} * @throws ArtificerClientException * @throws ArtificerServerException */ public QueryResultSet query(String srampQuery, int startIndex, int count, String orderBy, boolean ascending, Collection<String> propertyNames) throws ArtificerClientException, ArtificerServerException { ClientResponse<Feed> response = null; try { String xpath = srampQuery; if (xpath == null) throw new Exception(Messages.i18n.format("INVALID_QUERY_FORMAT")); String atomUrl = srampEndpoint; // Do a GET if multiple propertyNames are provided. We would like to always // do a POST but the RESTEasy client doesn't seem to have a way to send multiple // values for a single multipart/form-data part name. if (propertyNames == null || propertyNames.size() < 2) { ClientRequest request = createClientRequest(atomUrl); MultipartFormDataOutput formData = new MultipartFormDataOutput(); formData.addFormData("query", xpath, MediaType.TEXT_PLAIN_TYPE); formData.addFormData("startIndex", String.valueOf(startIndex), MediaType.TEXT_PLAIN_TYPE); formData.addFormData("count", String.valueOf(count), MediaType.TEXT_PLAIN_TYPE); formData.addFormData("orderBy", orderBy, MediaType.TEXT_PLAIN_TYPE); formData.addFormData("ascending", String.valueOf(ascending), MediaType.TEXT_PLAIN_TYPE); if (propertyNames != null) { for (String propertyName : propertyNames) { formData.addFormData("propertyName", propertyName, MediaType.TEXT_PLAIN_TYPE); } } request.body(MediaType.MULTIPART_FORM_DATA_TYPE, formData); response = request.post(Feed.class); return new QueryResultSet(response.getEntity()); } else { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(atomUrl); urlBuilder.append("?query="); urlBuilder.append(URLEncoder.encode(srampQuery, "UTF8")); urlBuilder.append("&startIndex="); urlBuilder.append(String.valueOf(startIndex)); urlBuilder.append("&count="); urlBuilder.append(String.valueOf(count)); urlBuilder.append("&orderBy="); urlBuilder.append(URLEncoder.encode(orderBy, "UTF8")); urlBuilder.append("&ascending="); urlBuilder.append(String.valueOf(ascending)); for (String propName : propertyNames) { urlBuilder.append("&propertyName="); urlBuilder.append(URLEncoder.encode(propName, "UTF8")); } ClientRequest request = createClientRequest(urlBuilder.toString()); response = request.get(Feed.class); return new QueryResultSet(response.getEntity()); } } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Build a query that can be parameterized and then executed. The format * of the query can either be a complete valid query or a query with JDBC style * parameters (using the ? placeholder for parameters). * * Additionally, the start index, count, order-by, ascending, and extra propertyNames * can all be set after calling this method. * * <code> * String uuid = ... * client.buildQuery("/s-ramp/core/Document[@uuid = ?]") * .parameter(uuid) * .startIndex(3) * .count(20) * .orderBy("name") * .ascending() * .propertyName("custom-prop-1") * .propertyName("custom-prop-2") * .query(); * </code> * * @param query * @return a client query object */ public ArtificerClientQuery buildQuery(String query) { return new ArtificerClientQuery(this, query); } /** * Adds on ontology in RDF format to the S-RAMP repository. This will only work if the S-RAMP * repository supports the ontology collection, which is not a part of the S-RAMP 1.0 * specification. * @param ontology * @throws ArtificerClientException * @throws ArtificerServerException */ public RDF addOntology(RDF ontology) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("ontology"); ClientResponse<Entry> response = null; try { String atomUrl = String.format("%1$s/ontology", srampEndpoint); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.APPLICATION_RDF_XML_TYPE, ontology); response = request.post(Entry.class); Entry entry = response.getEntity(); RDF rdf = ArtificerAtomUtils.unwrap(entry, RDF.class); rdf.getOtherAttributes().put(new QName(ArtificerConstants.SRAMP_NS, "uuid"), entry.getId().toString().replace("urn:uuid:", "")); return rdf; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Uploads an ontology to the S-RAMP repository. This will only work if the S-RAMP * repository supports the ontology collection, which is not a part of the S-RAMP 1.0 * specification. * @param content * @throws ArtificerClientException * @throws ArtificerServerException */ public RDF uploadOntology(InputStream content) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("ontology"); ClientResponse<Entry> response = null; try { String atomUrl = String.format("%1$s/ontology", srampEndpoint); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.APPLICATION_RDF_XML_TYPE, content); response = request.post(Entry.class); Entry entry = response.getEntity(); RDF rdf = ArtificerAtomUtils.unwrap(entry, RDF.class); rdf.getOtherAttributes().put(new QName(ArtificerConstants.SRAMP_NS, "uuid"), entry.getId().toString().replace("urn:uuid:", "")); return rdf; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Uploads a new version of an ontology to the S-RAMP repository. The ontology will be * replaced with this new version. This may fail if the new version removes classes from * the ontology that are currently in-use. * @param ontologyUuid * @param content */ public void updateOntology(String ontologyUuid, InputStream content) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("ontology"); ClientResponse<?> response = null; try { String atomUrl = String.format("%1$s/ontology/%2$s", srampEndpoint, ontologyUuid); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.APPLICATION_RDF_XML_TYPE, content); response = request.put(); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Uploads a new version of an ontology to the S-RAMP repository. The ontology will be * replaced with this new version. This may fail if the new version removes classes from * the ontology that are currently in-use. * @param ontologyUuid */ public void updateOntology(String ontologyUuid, RDF ontology) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("ontology"); ClientResponse<?> response = null; try { String atomUrl = String.format("%1$s/ontology/%2$s", srampEndpoint, ontologyUuid); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.APPLICATION_RDF_XML_TYPE, ontology); response = request.put(); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets a list of all the ontologies currently installed in the S-RAMP repository. This * will only work if the S-RAMP repository supports the ontology collection, which is not * a part of the S-RAMP 1.0 specification. * @throws ArtificerClientException * @throws ArtificerServerException */ public List<OntologySummary> getOntologies() throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("ontology"); ClientResponse<Feed> response = null; try { String atomUrl = String.format("%1$s/ontology", srampEndpoint); ClientRequest request = createClientRequest(atomUrl); response = request.get(Feed.class); Feed feed = response.getEntity(); List<OntologySummary> rval = new ArrayList<OntologySummary>(feed.getEntries().size()); for (Entry entry : feed.getEntries()) { OntologySummary summary = new OntologySummary(entry); rval.add(summary); } return rval; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets a single ontology by UUID. This returns all of the ontology meta-data * as well as all of the classes. * @param ontologyUuid * @throws ArtificerClientException * @throws ArtificerServerException */ public RDF getOntology(String ontologyUuid) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("ontology"); ClientResponse<Entry> response = null; try { String atomUrl = String.format("%1$s/ontology/%2$s", srampEndpoint, ontologyUuid); ClientRequest request = createClientRequest(atomUrl); response = request.get(Entry.class); Entry entry = response.getEntity(); RDF rdf = ArtificerAtomUtils.unwrapRDF(entry); rdf.getOtherAttributes().put(new QName(ArtificerConstants.SRAMP_NS, "uuid"), entry.getId().toString().replace("urn:uuid:", "")); return rdf; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Deletes a single ontology by its UUID. Note that this will only work if the S-RAMP * repository supports the ontology collection, which is not a part of the S-RAMP 1.0 * specification. * @param ontologyUuid * @throws ArtificerClientException * @throws ArtificerServerException */ public void deleteOntology(String ontologyUuid) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("ontology"); ClientResponse<?> response = null; try { String atomUrl = String.format("%1$s/ontology/%2$s", srampEndpoint, ontologyUuid); ClientRequest request = createClientRequest(atomUrl); response = request.delete(); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Adds a stored query to the S-RAMP repository. * * @param storedQuery * @throws ArtificerClientException * @throws ArtificerServerException */ public StoredQuery createStoredQuery(StoredQuery storedQuery) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("query"); ClientResponse<Entry> response = null; try { String atomUrl = String.format("%1$s/query", srampEndpoint); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.APPLICATION_ATOM_XML_ENTRY, ArtificerAtomUtils.wrapStoredQuery(storedQuery)); response = request.post(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapStoredQuery(entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Uploads a new version of a stored query to the S-RAMP repository. The stored query will be * replaced with this new version. * * @param queryName * @param storedQuery */ public void updateStoredQuery(String queryName, StoredQuery storedQuery) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("query"); ClientResponse<?> response = null; try { String atomUrl = String.format("%1$s/query/%2$s", srampEndpoint, queryName); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.APPLICATION_ATOM_XML_ENTRY, ArtificerAtomUtils.wrapStoredQuery(storedQuery)); response = request.put(); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets a list of all the stored queries currently installed in the S-RAMP repository. * * @throws ArtificerClientException * @throws ArtificerServerException */ public List<StoredQuery> getStoredQueries() throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("query"); ClientResponse<Feed> response = null; try { String atomUrl = String.format("%1$s/query", srampEndpoint); ClientRequest request = createClientRequest(atomUrl); response = request.get(Feed.class); Feed feed = response.getEntity(); List<StoredQuery> storedQueries = new ArrayList<StoredQuery>(feed.getEntries().size()); for (Entry entry : feed.getEntries()) { storedQueries.add(ArtificerAtomUtils.unwrapStoredQuery(entry)); } return storedQueries; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets a single stored query by name. * * @param queryName * @throws ArtificerClientException * @throws ArtificerServerException */ public StoredQuery getStoredQuery(String queryName) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("query"); ClientResponse<Entry> response = null; try { String atomUrl = String.format("%1$s/query/%2$s", srampEndpoint, queryName); ClientRequest request = createClientRequest(atomUrl); response = request.get(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapStoredQuery(entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Deletes a single stored query by name. * * @param queryName * @throws ArtificerClientException * @throws ArtificerServerException */ public void deleteStoredQuery(String queryName) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("query"); ClientResponse<?> response = null; try { String atomUrl = String.format("%1$s/query/%2$s", srampEndpoint, queryName); ClientRequest request = createClientRequest(atomUrl); response = request.delete(); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * See {@link #query(String)} * * @param queryName * @return QueryResultSet * @throws ArtificerClientException * @throws ArtificerServerException */ public QueryResultSet queryWithStoredQuery(String queryName) throws ArtificerClientException, ArtificerServerException { return queryWithStoredQuery(queryName, Collections.EMPTY_MAP); } /** * See {@link #query(String, int, int, String, boolean, Collection)}. * Note that {@link StoredQuery#getPropertyName()} is automatically given to #query. * * @param startIndex * @param count * @param orderBy * @param ascending * @return SrampClientQuery * @throws ArtificerClientException * @throws ArtificerServerException */ public QueryResultSet queryWithStoredQuery(String queryName, int startIndex, int count, String orderBy, boolean ascending) throws ArtificerClientException, ArtificerServerException { return queryWithStoredQuery(queryName, startIndex, count, orderBy, ascending, Collections.EMPTY_MAP); } /** * See {@link #buildQuery(String)} * * @param storedQuery * @return SrampClientQuery */ public ArtificerClientQuery buildQueryWithStoredQuery(StoredQuery storedQuery) { return buildQuery(storedQuery.getQueryExpression()); } /** * See {@link #query(String)} * * @param queryName * @param params Keys and values used for parameter substitution. * @return QueryResultSet * @throws ArtificerClientException * @throws ArtificerServerException */ public QueryResultSet queryWithStoredQuery(String queryName, Map<String, String> params) throws ArtificerClientException, ArtificerServerException { return queryWithStoredQuery(queryName, 0, 20, "name", true, params); } /** * See {@link #query(String, int, int, String, boolean, Collection)}. * Note that {@link StoredQuery#getPropertyName()} is automatically given to #query. * * @param queryName * @param params Keys and values used for parameter substitution. * @param startIndex * @param count * @param orderBy * @param ascending * @return SrampClientQuery * @throws ArtificerClientException * @throws ArtificerServerException */ public QueryResultSet queryWithStoredQuery(String queryName, int startIndex, int count, String orderBy, boolean ascending, Map<String, String> params) throws ArtificerClientException, ArtificerServerException { ClientResponse<Feed> response = null; try { String atomUrl = String.format("%1$s/query/%2$s/results", srampEndpoint, queryName); StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(atomUrl); urlBuilder.append("?startIndex="); urlBuilder.append(String.valueOf(startIndex)); urlBuilder.append("&count="); urlBuilder.append(String.valueOf(count)); urlBuilder.append("&orderBy="); urlBuilder.append(URLEncoder.encode(orderBy, "UTF8")); urlBuilder.append("&ascending="); urlBuilder.append(String.valueOf(ascending)); if (params != null) { for (String paramName : params.keySet()) { urlBuilder.append("&" + paramName + "="); urlBuilder.append(URLEncoder.encode(params.get(paramName), "UTF8")); } } ClientRequest request = createClientRequest(urlBuilder.toString()); response = request.get(Feed.class); return new QueryResultSet(response.getEntity()); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Adds a new audit entry on the artifact with the given UUID. * @param artifactUuid * @param auditEntry * @throws ArtificerClientException * @throws ArtificerServerException */ public AuditEntry addAuditEntry(String artifactUuid, AuditEntry auditEntry) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("audit"); ClientResponse<Entry> response = null; try { String atomUrl = String.format("%1$s/audit/artifact/%2$s", srampEndpoint, artifactUuid); ClientRequest request = createClientRequest(atomUrl); request.body(MediaType.APPLICATION_AUDIT_ENTRY_XML_TYPE, auditEntry); response = request.post(Entry.class); Entry entry = response.getEntity(); if (entry == null) throw new ArtificerServerException(Messages.i18n.format("AUDIT_ENTRY_ADD_FAILED")); return ArtificerAtomUtils.unwrap(entry, AuditEntry.class); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets the audit trail for the artifact with the given UUID. * @param artifactUuid * @throws ArtificerClientException * @throws ArtificerServerException */ public AuditResultSet getAuditTrailForArtifact(String artifactUuid) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("audit"); ClientResponse<Feed> response = null; try { String atomUrl = String.format("%1$s/audit/artifact/%2$s", srampEndpoint, artifactUuid); ClientRequest request = createClientRequest(atomUrl); response = request.get(Feed.class); Feed feed = response.getEntity(); AuditResultSet rs = new AuditResultSet(feed); return rs; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets the audit trail for the artifact with the given UUID. * @param artifactUuid * @param startIndex * @param count * @throws ArtificerClientException * @throws ArtificerServerException */ public AuditResultSet getAuditTrailForArtifact(String artifactUuid, int startIndex, int count) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("audit"); ClientResponse<Feed> response = null; try { String atomUrl = String.format("%1$s/audit/artifact/%2$s?startIndex=%3$s&count=%4$s", srampEndpoint, artifactUuid, String.valueOf(startIndex), String.valueOf(count)); ClientRequest request = createClientRequest(atomUrl); response = request.get(Feed.class); Feed feed = response.getEntity(); AuditResultSet rs = new AuditResultSet(feed); return rs; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets the audit trail for the artifact with the given UUID. * @param username * @throws ArtificerClientException * @throws ArtificerServerException */ public AuditResultSet getAuditTrailForUser(String username) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("audit"); ClientResponse<Feed> response = null; try { String atomUrl = String.format("%1$s/audit/user/%2$s", srampEndpoint, username); ClientRequest request = createClientRequest(atomUrl); response = request.get(Feed.class); Feed feed = response.getEntity(); AuditResultSet rs = new AuditResultSet(feed); return rs; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets the audit trail for the artifact with the given UUID. * @param username * @param startIndex * @param count * @throws ArtificerClientException * @throws ArtificerServerException */ public AuditResultSet getAuditTrailForUser(String username, int startIndex, int count) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("audit"); ClientResponse<Feed> response = null; try { String atomUrl = String.format("%1$s/audit/user/%2$s?startIndex=%3$s&count=%4$s", srampEndpoint, username, String.valueOf(startIndex), String.valueOf(count)); ClientRequest request = createClientRequest(atomUrl); response = request.get(Feed.class); Feed feed = response.getEntity(); AuditResultSet rs = new AuditResultSet(feed); return rs; } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Gets the full audit entry for the given artifact + audit event pair. * @param artifactUuid * @param auditEntryUuid * @throws ArtificerClientException * @throws ArtificerServerException */ public AuditEntry getAuditEntry(String artifactUuid, String auditEntryUuid) throws ArtificerClientException, ArtificerServerException { assertFeatureEnabled("audit"); ClientResponse<Entry> response = null; try { String atomUrl = String.format("%1$s/audit/artifact/%2$s/%3$s", srampEndpoint, artifactUuid, auditEntryUuid); ClientRequest request = createClientRequest(atomUrl); response = request.get(Entry.class); Entry entry = response.getEntity(); return ArtificerAtomUtils.unwrapAuditEntry(entry); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Creates the RESTEasy client request object, configured appropriately. * @param atomUrl */ protected ClientRequest createClientRequest(String atomUrl) { ClientExecutor executor = createClientExecutor(); ClientRequest request = new ClientRequest(atomUrl, executor); return request; } /** * Creates the client executor that will be used by RESTEasy when * making the request. */ private ClientExecutor createClientExecutor() { // TODO I think the http client is thread safe - so let's try to create just one of these DefaultHttpClient httpClient = new DefaultHttpClient(); httpClient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { Locale l = getLocale(); if (l == null) { l = Locale.getDefault(); } request.addHeader("Accept-Language", l.toString()); } }); if (this.authProvider != null) { httpClient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { authProvider.provideAuthentication(request); } }); } return new ApacheHttpClient4Executor(httpClient); } /** * @return the locale */ public Locale getLocale() { return locale; } /** * @param locale the locale to set */ public void setLocale(Locale locale) { this.locale = locale; } public QueryResultSet reverseRelationships(String uuid) throws ArtificerClientException, ArtificerServerException { ClientResponse<Feed> response = null; try { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(artificerEndpoint); urlBuilder.append("/reverseRelationship/"); urlBuilder.append(uuid); ClientRequest request = createClientRequest(urlBuilder.toString()); response = request.get(Feed.class); return new QueryResultSet(response.getEntity()); } catch (ArtificerServerException e) { throw e; } catch (Throwable e) { throw new ArtificerClientException(e); } finally { closeQuietly(response); } } /** * Quietly closes the resteasy client (releases the connection). * @param response */ private void closeQuietly(ClientResponse<?> response) { if (response != null) { response.releaseConnection(); } } }