package org.carlspring.strongbox.client; import org.carlspring.maven.commons.util.ArtifactUtils; import javax.ws.rs.ServerErrorException; import javax.ws.rs.client.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import org.apache.http.HttpStatus; import org.apache.maven.artifact.Artifact; import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; import org.springframework.security.authentication.AuthenticationServiceException; import static org.apache.http.HttpStatus.SC_FORBIDDEN; import static org.apache.http.HttpStatus.SC_UNAUTHORIZED; /** * @author mtodorov */ public class ArtifactClient extends BaseArtifactClient implements Closeable { private static final String HEADER_VALUE_MAVEN = "Maven/*"; private static final String HEADER_NAME_USER_AGENT = "user-agent"; protected String username = "maven"; protected String password = "password"; private String protocol = "http"; private String host = System.getProperty("strongbox.host") != null ? System.getProperty("strongbox.host") : "localhost"; private int port = System.getProperty("strongbox.port") != null ? Integer.parseInt(System.getProperty("strongbox.port")) : 48080; private String contextBaseUrl; private Client client; public ArtifactClient() { } public static ArtifactClient getTestInstance() { return getTestInstance("maven", "password"); } public static ArtifactClient getTestInstanceLoggedInAsAdmin() { return getTestInstance("admin", "password"); } public static ArtifactClient getTestInstance(String username, String password) { String host = System.getProperty("strongbox.host") != null ? System.getProperty("strongbox.host") : "localhost"; int port = System.getProperty("strongbox.port") != null ? Integer.parseInt(System.getProperty("strongbox.port")) : 48080; ArtifactClient client = new ArtifactClient(); client.setUsername(username); client.setPassword(password); client.setPort(port); client.setContextBaseUrl("http://" + host + ":" + client.getPort()); return client; } public Client getClientInstance() { if (client == null) { ClientConfig config = getClientConfig(); client = ClientBuilder.newClient(config); return client; } else { return client; } } private ClientConfig getClientConfig() { ClientConfig config = new ClientConfig(); config.connectorProvider(new ApacheConnectorProvider()); return config; } @Override public void close() { if (client != null) { client.close(); } } public void deployFile(InputStream is, String url, String fileName) throws ArtifactOperationException { put(is, url, fileName, MediaType.APPLICATION_OCTET_STREAM); } public void deployMetadata(InputStream is, String url, String fileName) throws ArtifactOperationException { put(is, url, fileName, MediaType.APPLICATION_XML); } public void put(InputStream is, String url, String fileName, String mediaType) throws ArtifactOperationException { String contentDisposition = "attachment; filename=\"" + fileName + "\""; WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Response response = resource.request(mediaType) .header("Content-Disposition", contentDisposition) .header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN) .put(Entity.entity(is, mediaType)); handleFailures(response, "Failed to upload file!"); } public void getArtifact(Artifact artifact, String repository) throws ArtifactTransportException, IOException { String url = getContextBaseUrl() + "/" + repository + "/" + ArtifactUtils.convertArtifactToPath(artifact); logger.debug("Getting " + url + "..."); WebTarget webResource = getClientInstance().target(url); setupAuthentication(webResource); Response response = webResource.request(MediaType.TEXT_PLAIN).header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN).get(); final InputStream is = response.readEntity(InputStream.class); int total = 0; int len; final int size = 4096; byte[] bytes = new byte[size]; while ((len = is.read(bytes, 0, size)) != -1) { total += len; } logger.debug("Response code: " + response.getStatus() + ". Read: " + total + " bytes."); int status = response.getStatus(); if (status != 200) { throw new ArtifactTransportException("Failed to resolve artifact!"); } if (total == 0) { throw new ArtifactTransportException("Artifact size was zero!"); } } public InputStream getResource(String path, long offset) throws ArtifactTransportException, IOException { String url = getContextBaseUrl() + (!path.startsWith("/") ? "/" : "") + path; logger.debug("Getting " + url + "..."); WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Invocation.Builder request = resource.request(); Response response; if (offset > 0) { response = request.header("Range", "bytes=" + offset + "-").get(); } else { response = request.get(); } return response.readEntity(InputStream.class); } public Response getResourceWithResponse(String path) throws ArtifactTransportException, IOException { String url = getContextBaseUrl() + (!path.startsWith("/") ? "/" : "") + path; logger.debug("Getting " + url + "..."); WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); return resource.request(MediaType.TEXT_PLAIN).header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN).get(); } public void deleteArtifact(Artifact artifact, String storageId, String repositoryId) throws ArtifactOperationException { String url = getUrlForArtifact(artifact, storageId, repositoryId); logger.info("Getting " + url + "..."); WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Response response = resource.request().header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN).delete(); handleFailures(response, "Failed to delete artifact!"); } public void delete(String storageId, String repositoryId, String path) throws ArtifactOperationException { delete(storageId, repositoryId, path, false); } public void delete(String storageId, String repositoryId, String path, boolean force) throws ArtifactOperationException { String url = getContextBaseUrl() + "/storages/" + storageId + "/" + repositoryId + "/" + path + (force ? "?force=" + force : ""); logger.info("Getting " + url + "..."); WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Response response = resource.request().header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN).delete(); handleFailures(response, "Failed to delete artifact!"); } public void deleteTrash(String storageId, String repositoryId) throws ArtifactOperationException { String url = getUrlForTrash(storageId, repositoryId); WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Response response = resource.request().header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN).delete(); handleFailures(response, "Failed to delete the trash for " + storageId + ":" + repositoryId + "!"); } public void deleteTrash() throws ArtifactOperationException { String url = getContextBaseUrl() + "/trash"; WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Response response = resource.request().header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN).delete(); handleFailures(response, "Failed to delete trash for all repositories!"); } public void undelete(String storageId, String repositoryId, String path) throws ArtifactOperationException { @SuppressWarnings("ConstantConditions") String url = getUrlForTrash(storageId, repositoryId) + "/" + path; WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Response response = resource.request(MediaType.TEXT_PLAIN) .header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN) .post(Entity.entity("Undelete", MediaType.TEXT_PLAIN)); handleFailures(response, "Failed to delete the trash for " + storageId + ":" + repositoryId + "!"); } public void undeleteTrash(String storageId, String repositoryId) throws ArtifactOperationException { String url = getUrlForTrash(storageId, repositoryId); WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Response response = resource.request(MediaType.TEXT_PLAIN) .header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN) .post(Entity.entity("Undelete", MediaType.TEXT_PLAIN)); handleFailures(response, "Failed to delete the trash for " + storageId + ":" + repositoryId + "!"); } public void undeleteTrash() throws ArtifactOperationException { String url = getContextBaseUrl() + "/trash"; WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Response response = resource.request(MediaType.TEXT_PLAIN) .header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN) .post(Entity.entity("Undelete", MediaType.TEXT_PLAIN)); handleFailures(response, "Failed to delete the trash!"); } public boolean artifactExists(Artifact artifact, String storageId, String repositoryId) throws ResponseException { Response response = artifactExistsStatusCode(artifact, storageId, repositoryId); try { if (response.getStatus() == HttpStatus.SC_OK) { return true; } else if (response.getStatus() == HttpStatus.SC_NOT_FOUND) { return false; } else { throw new ResponseException(response.getStatusInfo().getReasonPhrase(), response.getStatus()); } } finally { response.close(); } } public Response artifactExistsStatusCode(Artifact artifact, String storageId, String repositoryId) throws ResponseException { String url = getUrlForArtifact(artifact, storageId, repositoryId); logger.debug("Path to artifact: " + url); WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); return resource.request(MediaType.TEXT_PLAIN).header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN).get(); } public boolean pathExists(String path) { String url = escapeUrl(path); logger.debug("Path to artifact: " + url); WebTarget resource = getClientInstance().target(url); setupAuthentication(resource); Response response = resource.request(MediaType.TEXT_PLAIN).header(HEADER_NAME_USER_AGENT, HEADER_VALUE_MAVEN).get(); try { return response.getStatus() == HttpStatus.SC_OK; } finally { response.close(); } } public void handleFailures(Response response, String message) throws ArtifactOperationException, AuthenticationServiceException { int status = response.getStatus(); if (status == SC_UNAUTHORIZED || status == SC_FORBIDDEN) { // TODO Handle authentication exceptions in a right way throw new AuthenticationServiceException(message + "\nUser is unauthorized to execute that operation. " + "Check assigned roles and privileges."); } else if (status != 200) { StringBuilder messageBuilder = new StringBuilder(); messageBuilder.append("\n ERROR ").append(status).append(" ").append(message).append("\n"); Object entity = response.getEntity(); if (entity != null) { messageBuilder.append(entity.toString()); } logger.error(messageBuilder.toString()); } } public String getUrlForArtifact(Artifact artifact, String storageId, String repositoryId) { return getContextBaseUrl() + "/storages/" + storageId + "/" + repositoryId + "/" + ArtifactUtils.convertArtifactToPath(artifact); } public String getUrlForTrash(String storageId, String repositoryId) { return getContextBaseUrl() + "/trash/" + storageId + "/" + repositoryId; } public WebTarget setupAuthentication(WebTarget target) { if (username != null && password != null) { logger.trace("[setupAuthentication] " + username + "@" + password); target.register(HttpAuthenticationFeature.basic(username, password)); return target; } else { throw new ServerErrorException("Unable to setup authentication", Response.Status.INTERNAL_SERVER_ERROR); } } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getContextBaseUrl() { if (contextBaseUrl == null) { contextBaseUrl = protocol + "://" + host + ":" + port; } return contextBaseUrl; } public void setContextBaseUrl(String contextBaseUrl) { this.contextBaseUrl = contextBaseUrl; } 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; } }