diff --git a/platform-api/che-core-api-builder/pom.xml b/platform-api/che-core-api-builder/pom.xml index 5220a8233..c9053c04f 100644 --- a/platform-api/che-core-api-builder/pom.xml +++ b/platform-api/che-core-api-builder/pom.xml @@ -223,49 +223,4 @@ - - - default - - true - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - **/*Test.java - - - - - - - - unix - - - unix - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${project.build.directory} - - - **/*Test.java - - - - - - - diff --git a/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/BuilderUtils.java b/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/BuilderUtils.java index 89df31264..97c3cb417 100644 --- a/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/BuilderUtils.java +++ b/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/BuilderUtils.java @@ -37,10 +37,10 @@ public static HttpJsonResponse builderRequest(HttpJsonRequest req) throws Builde try { return req.request(); } catch (IOException e) { - throw new BuilderException(e); + throw new BuilderException(e.getMessage(), e); } catch (ServerException | UnauthorizedException | ForbiddenException | NotFoundException | ConflictException | BadRequestException e) { - throw new BuilderException(e.getServiceError()); + throw new BuilderException(e.getMessage(), e); } } diff --git a/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/RemoteTask.java b/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/RemoteTask.java index 5463f8aea..0c90a72cb 100644 --- a/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/RemoteTask.java +++ b/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/RemoteTask.java @@ -17,20 +17,16 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; import org.eclipse.che.api.core.rest.HttpOutputMessage; +import org.eclipse.che.api.core.rest.HttpResponse; import org.eclipse.che.api.core.rest.shared.dto.Link; -import org.eclipse.che.commons.env.EnvironmentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.ws.rs.HttpMethod; import javax.ws.rs.core.HttpHeaders; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import static com.google.common.base.MoreObjects.firstNonNull; import static org.eclipse.che.api.builder.BuilderUtils.builderRequest; /** @@ -217,15 +213,7 @@ public void downloadResultArchive(String archType, HttpOutputMessage output) thr } private void readFromUrl(String url, final HttpOutputMessage output) throws IOException { - final HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection(); - conn.setConnectTimeout(60 * 1000); - conn.setReadTimeout(60 * 1000); - conn.setRequestMethod(HttpMethod.GET); - final EnvironmentContext context = EnvironmentContext.getCurrent(); - if (context.getUser() != null && context.getUser().getToken() != null) { - conn.setRequestProperty(HttpHeaders.AUTHORIZATION, context.getUser().getToken()); - } - try { + try (HttpResponse conn = requestFactory.target(url).setTimeout(60 * 1000).request()) { output.setStatus(conn.getResponseCode()); final String contentType = conn.getContentType(); if (contentType != null) { @@ -237,13 +225,10 @@ private void readFromUrl(String url, final HttpOutputMessage output) throws IOEx output.addHttpHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition); } - try (InputStream in = firstNonNull(conn.getErrorStream(), conn.getInputStream()); + try (InputStream in = conn.getInputStream(); OutputStream out = output.getOutputStream()) { ByteStreams.copy(in, out); } - - } finally { - conn.disconnect(); } } } diff --git a/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/internal/SourcesManagerImpl.java b/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/internal/SourcesManagerImpl.java index 9b2a3c342..32a03b4a5 100644 --- a/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/internal/SourcesManagerImpl.java +++ b/platform-api/che-core-api-builder/src/main/java/org/eclipse/che/api/builder/internal/SourcesManagerImpl.java @@ -11,8 +11,12 @@ package org.eclipse.che.api.builder.internal; import org.eclipse.che.api.builder.dto.BaseBuilderRequest; +import org.eclipse.che.api.core.rest.HttpJsonRequest; +import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; +import org.eclipse.che.api.core.rest.HttpRequest; +import org.eclipse.che.api.core.rest.HttpRequest.BodyWriter; +import org.eclipse.che.api.core.rest.HttpResponse; import org.eclipse.che.api.core.util.ValueHolder; -import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.json.JsonHelper; import org.eclipse.che.commons.json.JsonParseException; import org.eclipse.che.commons.lang.IoUtil; @@ -29,6 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -40,7 +45,9 @@ import java.io.StringReader; import java.io.Writer; import java.net.HttpURLConnection; -import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.text.ParseException; import java.util.HashMap; import java.util.LinkedList; @@ -82,19 +89,21 @@ public class SourcesManagerImpl implements SourcesManager { private final AtomicReference projectKeyHolder; private final Set listeners; private final ScheduledExecutorService executor; + private final HttpJsonRequestFactory provider; private static final long KEEP_PROJECT_TIME = TimeUnit.MINUTES.toMillis(30); private static final int CONNECT_TIMEOUT = (int)TimeUnit.MINUTES.toMillis(4);//This time is chosen empirically and private static final int READ_TIMEOUT = (int)TimeUnit.MINUTES.toMillis(4);//necessary for some large projects. See IDEX-1957. @Inject - public SourcesManagerImpl(@Named(Constants.BASE_DIRECTORY) File rootDirectory) { + public SourcesManagerImpl(@Named(Constants.BASE_DIRECTORY) File rootDirectory, HttpJsonRequestFactory provider) { this.rootDirectory = rootDirectory; tasks = new ConcurrentHashMap<>(); projectKeyHolder = new AtomicReference<>(); executor = Executors.newSingleThreadScheduledExecutor( new ThreadFactoryBuilder().setNameFormat(getClass().getSimpleName() + "-FileCleaner-%d").setDaemon(true).build()); listeners = new CopyOnWriteArraySet<>(); + this.provider = provider; } @PostConstruct @@ -202,53 +211,51 @@ public void write(byte[] b) throws IOException { }; private void download(String downloadUrl, java.io.File downloadTo, File directory) throws IOException { - HttpURLConnection conn = null; - try { - final LinkedList q = new LinkedList<>(); - q.add(downloadTo); - final long start = System.currentTimeMillis(); - final List> md5sums = new LinkedList<>(); - while (!q.isEmpty()) { - java.io.File current = q.pop(); - java.io.File[] list = current.listFiles(); - if (list != null) { - for (java.io.File f : list) { - if (f.isDirectory()) { - q.push(f); - } else { - md5sums.add(Pair.of(com.google.common.io.Files.hash(f, Hashing.md5()).toString(), - downloadTo.toPath().relativize(f.toPath()).toString() - .replace("\\", "/"))); //Replacing of "\" is need for windows support - } + final LinkedList q = new LinkedList<>(); + q.add(downloadTo); + final long start = System.currentTimeMillis(); + final List> md5sums = new LinkedList<>(); + while (!q.isEmpty()) { + java.io.File current = q.pop(); + java.io.File[] list = current.listFiles(); + if (list != null) { + for (java.io.File f : list) { + if (f.isDirectory()) { + q.push(f); + } else { + md5sums.add(Pair.of(com.google.common.io.Files.hash(f, Hashing.md5()).toString(), + downloadTo.toPath().relativize(f.toPath()).toString() + .replace("\\", "/"))); //Replacing of "\" is need for windows support } } } - final long end = System.currentTimeMillis(); - if (md5sums.size() > 0) { - LOG.debug("count md5sums of {} files, time: {}ms", md5sums.size(), (end - start)); - } - conn = (HttpURLConnection)new URL(downloadUrl).openConnection(); - conn.setConnectTimeout(CONNECT_TIMEOUT); - conn.setReadTimeout(READ_TIMEOUT); - final EnvironmentContext context = EnvironmentContext.getCurrent(); - if (context.getUser() != null && context.getUser().getToken() != null) { - conn.setRequestProperty(HttpHeaders.AUTHORIZATION, context.getUser().getToken()); - } - if (!md5sums.isEmpty()) { - conn.setRequestMethod(HttpMethod.POST); - conn.setRequestProperty("Content-type", MediaType.TEXT_PLAIN); - conn.setRequestProperty(HttpHeaders.ACCEPT, MediaType.MULTIPART_FORM_DATA); - conn.setDoOutput(true); - try (OutputStream output = conn.getOutputStream(); - Writer writer = new OutputStreamWriter(output)) { - for (Pair pair : md5sums) { - writer.write(pair.first); - writer.write(' '); - writer.write(pair.second); - writer.write('\n'); + } + final long end = System.currentTimeMillis(); + if (md5sums.size() > 0) { + LOG.debug("count md5sums of {} files, time: {}ms", md5sums.size(), (end - start)); + } + // Create the main request + HttpRequest request = provider.target(downloadUrl).setTimeout(READ_TIMEOUT); + // handle case where checksums are sent + if (!md5sums.isEmpty()) { + request.usePostMethod(); + request.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN); + request.addHeader(HttpHeaders.ACCEPT, MediaType.MULTIPART_FORM_DATA); + request.setBodyWriter(new BodyWriter() { + @Override + public void writeTo(OutputStream output) throws IOException { + try (Writer writer = new OutputStreamWriter(output)) { + for (Pair pair : md5sums) { + writer.write(pair.first); + writer.write(' '); + writer.write(pair.second); + writer.write('\n'); + } } } - } + }); + } + try (HttpResponse conn = request.request()) { final int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { final String contentType = conn.getHeaderField("content-type"); @@ -314,10 +321,6 @@ private void download(String downloadUrl, java.io.File downloadTo, File director } } catch (ParseException | JsonParseException e) { throw new IOException(e.getMessage(), e); - } finally { - if (conn != null) { - conn.disconnect(); - } } } diff --git a/platform-api/che-core-api-builder/src/test/java/org/eclipse/che/api/builder/BuilderTest.java b/platform-api/che-core-api-builder/src/test/java/org/eclipse/che/api/builder/BuilderTest.java index f7916c4f4..e223472bd 100644 --- a/platform-api/che-core-api-builder/src/test/java/org/eclipse/che/api/builder/BuilderTest.java +++ b/platform-api/che-core-api-builder/src/test/java/org/eclipse/che/api/builder/BuilderTest.java @@ -211,14 +211,12 @@ public void testRemoteBuildSameEnvironment(){ Assert.assertEquals(remoteBuilder.getBuilderEnvironment(), builder.getEnvironments()); } - private void waitForTask(BuildTask task) throws Exception { + private static void waitForTask(BuildTask task) throws Exception { final long end = System.currentTimeMillis() + 5000; - synchronized (this) { - while (!task.isDone()) { - wait(100); - if (System.currentTimeMillis() > end) { - Assert.fail("timeout"); - } + while (!task.isDone()) { + Thread.sleep(100); + if (System.currentTimeMillis() > end) { + Assert.fail("timeout"); } } } diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpJsonRequest.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpJsonRequest.java index e897b6d28..8d2533494 100644 --- a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpJsonRequest.java +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpJsonRequest.java @@ -18,11 +18,11 @@ import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; import org.eclipse.che.api.core.UnauthorizedException; +import org.eclipse.che.api.core.rest.HttpRequest.BodyWriter; import org.eclipse.che.api.core.rest.shared.dto.Link; import org.eclipse.che.api.core.rest.shared.dto.ServiceError; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.lang.Pair; -import org.eclipse.che.commons.user.User; import org.eclipse.che.dto.server.DtoFactory; import org.eclipse.che.dto.server.JsonArrayImpl; import org.eclipse.che.dto.server.JsonSerializable; @@ -33,7 +33,6 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; import java.io.IOException; import java.io.InputStream; @@ -41,9 +40,7 @@ import java.io.OutputStream; import java.io.Reader; import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -60,32 +57,16 @@ * @author Yevhenii Voevodin * @see DefaultHttpJsonRequestFactory */ -public class DefaultHttpJsonRequest implements HttpJsonRequest { - - private static final int DEFAULT_QUERY_PARAMS_LIST_SIZE = 5; - private static final Object[] EMPTY_ARRAY = new Object[0]; +public class DefaultHttpJsonRequest extends DefaultHttpRequestBase implements HttpJsonRequest { - private final String url; - - private int timeout; - private String method; - private Object body; - private List> queryParams; + protected Object body; protected DefaultHttpJsonRequest(String url) { - this.url = requireNonNull(url, "Required non-null url"); - this.method = HttpMethod.GET; // Default is GET for convenience + super(url, HttpMethod.GET); } protected DefaultHttpJsonRequest(Link link) { - this(requireNonNull(link, "Required non-null link").getHref()); - this.method = link.getMethod(); - } - - @Override - public HttpJsonRequest setMethod(@NotNull String method) { - this.method = requireNonNull(method, "Required non-null http method"); - return this; + super(requireNonNull(link, "Required non-null link").getHref(), link.getMethod()); } @Override @@ -106,23 +87,6 @@ public HttpJsonRequest setBody(@NotNull List list) { return this; } - @Override - public HttpJsonRequest addQueryParam(@NotNull String name, @NotNull Object value) { - requireNonNull(name, "Required non-null query parameter name"); - requireNonNull(value, "Required non-null query parameter value"); - if (queryParams == null) { - queryParams = new ArrayList<>(DEFAULT_QUERY_PARAMS_LIST_SIZE); - } - queryParams.add(Pair.of(name, value)); - return this; - } - - @Override - public HttpJsonRequest setTimeout(int timeout) { - this.timeout = timeout; - return this; - } - @Override public HttpJsonResponse request() throws IOException, ServerException, @@ -131,9 +95,7 @@ public HttpJsonResponse request() throws IOException, NotFoundException, ConflictException, BadRequestException { - if (method == null) { - throw new IllegalStateException("Could not perform request, request method wasn't set"); - } + beforeRequest(); return doRequest(timeout, url, method, body, queryParams); } @@ -181,100 +143,56 @@ protected DefaultHttpJsonResponse doRequest(int timeout, UnauthorizedException, ConflictException, BadRequestException { - final String authToken = getAuthenticationToken(); - final boolean hasQueryParams = parameters != null && !parameters.isEmpty(); - if (hasQueryParams || authToken != null) { - final UriBuilder ub = UriBuilder.fromUri(url); - //remove sensitive information from url. - ub.replaceQueryParam("token", EMPTY_ARRAY); - - if (hasQueryParams) { - for (Pair parameter : parameters) { - String name = URLEncoder.encode(parameter.first, "UTF-8"); - String value = parameter.second == null ? - null : - URLEncoder.encode(String.valueOf(parameter.second), "UTF-8"); - ub.queryParam(name, value); + Map headers = new HashMap<>(); + headers.put(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); + BodyWriter bw = null; + if (body != null) { + headers.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + bw = new BodyWriter() { + @Override + public void writeTo(OutputStream out) throws IOException { + out.write(DtoFactory.getInstance().toJson(body).getBytes()); } - } - url = ub.build().toString(); + }; } - final HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection(); - conn.setConnectTimeout(timeout > 0 ? timeout : 60000); - conn.setReadTimeout(timeout > 0 ? timeout : 60000); - try { - conn.setRequestMethod(method); - //drop a hint for server side that we want to receive application/json - conn.addRequestProperty(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); - if (authToken != null) { - conn.setRequestProperty(HttpHeaders.AUTHORIZATION, authToken); - } - if (body != null) { - conn.addRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - conn.setDoOutput(true); - - if (HttpMethod.DELETE.equals(method)) { //to avoid jdk bug described here http://bugs.java.com/view_bug.do?bug_id=7157360 - conn.setRequestMethod(HttpMethod.POST); - conn.setRequestProperty("X-HTTP-Method-Override", HttpMethod.DELETE); - } - - try (OutputStream output = conn.getOutputStream()) { - output.write(DtoFactory.getInstance().toJson(body).getBytes()); - } - } - - final int responseCode = conn.getResponseCode(); - if ((responseCode / 100) != 2) { - InputStream in = conn.getErrorStream(); - if (in == null) { - in = conn.getInputStream(); - } - final String str; - try (Reader reader = new InputStreamReader(in)) { + int responseCode; + String contentType; + String str = null; + try (HttpResponse conn = doGeneralRequest(timeout, url, method, bw, headers, parameters)) { + responseCode = conn.getResponseCode(); + contentType = conn.getContentType(); + if (contentType != null) { + try (InputStream in = conn.getInputStream(); Reader reader = new InputStreamReader(in)) { str = CharStreams.toString(reader); } - final String contentType = conn.getContentType(); - if (contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON)) { - final ServiceError serviceError = DtoFactory.getInstance().createDtoFromJson(str, ServiceError.class); - if (serviceError.getMessage() != null) { - if (responseCode == Response.Status.FORBIDDEN.getStatusCode()) { - throw new ForbiddenException(serviceError); - } else if (responseCode == Response.Status.NOT_FOUND.getStatusCode()) { - throw new NotFoundException(serviceError); - } else if (responseCode == Response.Status.UNAUTHORIZED.getStatusCode()) { - throw new UnauthorizedException(serviceError); - } else if (responseCode == Response.Status.CONFLICT.getStatusCode()) { - throw new ConflictException(serviceError); - } else if (responseCode == Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) { - throw new ServerException(serviceError); - } else if (responseCode == Response.Status.BAD_REQUEST.getStatusCode()) { - throw new BadRequestException(serviceError); - } - throw new ServerException(serviceError); - } - } - // Can't parse content as json or content has format other we expect for error. - throw new IOException(String.format("Failed access: %s, method: %s, response code: %d, message: %s", - UriBuilder.fromUri(url).replaceQuery("token").build(), method, responseCode, str)); - } - final String contentType = conn.getContentType(); - if (contentType != null && !contentType.startsWith(MediaType.APPLICATION_JSON)) { - throw new IOException(conn.getResponseMessage() + " [ Content-Type: " + contentType + " ]"); } - - try (Reader reader = new InputStreamReader(conn.getInputStream())) { - return new DefaultHttpJsonResponse(CharStreams.toString(reader), responseCode); + } + if ((responseCode / 100) != 2 && contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON)) { + final ServiceError serviceError = DtoFactory.getInstance().createDtoFromJson(str, ServiceError.class); + if (serviceError.getMessage() != null) { + if (responseCode == Response.Status.FORBIDDEN.getStatusCode()) { + throw new ForbiddenException(serviceError); + } else if (responseCode == Response.Status.NOT_FOUND.getStatusCode()) { + throw new NotFoundException(serviceError); + } else if (responseCode == Response.Status.UNAUTHORIZED.getStatusCode()) { + throw new UnauthorizedException(serviceError); + } else if (responseCode == Response.Status.CONFLICT.getStatusCode()) { + throw new ConflictException(serviceError); + } else if (responseCode == Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) { + throw new ServerException(serviceError); + } else if (responseCode == Response.Status.BAD_REQUEST.getStatusCode()) { + throw new BadRequestException(serviceError); + } + throw new ServerException(serviceError); } - } finally { - conn.disconnect(); + // Can't parse content as json or content has format other we expect for error. + throw new IOException(String.format("Failed access: %s, method: %s, response code: %d, message: %s", + url, method, responseCode, str)); } - } - - private String getAuthenticationToken() { - final User user = EnvironmentContext.getCurrent().getUser(); - if (user != null) { - return user.getToken(); + if (contentType != null && !contentType.startsWith(MediaType.APPLICATION_JSON)) { + throw new IOException(Response.Status.Family.familyOf(responseCode) + " [ Content-Type: " + contentType + " ]"); } - return null; + return new DefaultHttpJsonResponse(str, responseCode); } + } diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpJsonRequestFactory.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpJsonRequestFactory.java index 905c67168..5463172a8 100644 --- a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpJsonRequestFactory.java +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpJsonRequestFactory.java @@ -32,4 +32,10 @@ public HttpJsonRequest fromUrl(@NotNull String url) { public HttpJsonRequest fromLink(@NotNull Link link) { return new DefaultHttpJsonRequest(link); } + + @Override + public HttpRequest target(String url) { + return new DefaultHttpRequest(url); + } + } diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpRequest.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpRequest.java new file mode 100644 index 000000000..6e4dc1ce4 --- /dev/null +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpRequest.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.rest; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Simple implementation of {@link HttpJsonRequest} based on {@link HttpURLConnection}. + * + *

+ * The implementation is not thread-safe, instance of this class must be created each time when it's needed. + * + *

+ * The instance of this request is reusable, which means that it is possible to call {@link #request()} method more than + * one time per instance + * + * @author Yevhenii Voevodin + * @see DefaultHttpJsonRequestFactory + */ +public class DefaultHttpRequest extends DefaultHttpRequestBase implements HttpRequest { + + private BodyWriter bodyWriter; + private Map headers; + + public DefaultHttpRequest(String url) { + super(url, null); + } + + @Override + public HttpRequest setBodyWriter(BodyWriter bodyWriter) { + this.bodyWriter = Objects.requireNonNull(bodyWriter, "Required non-null body writer"); + return this; + } + + @Override + public HttpRequest addHeader(String name, String value) { + requireNonNull(name, "Required non-null header name"); + if (headers == null) { + headers = new HashMap<>(); + } + headers.put(name, value); + return this; + } + + @Override + public HttpResponse request() throws IOException { + beforeRequest(); + return doGeneralRequest(timeout, url, method, bodyWriter, headers, queryParams); + } + +} diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpRequestBase.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpRequestBase.java new file mode 100644 index 000000000..2dc990b27 --- /dev/null +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/DefaultHttpRequestBase.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.rest; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.validation.constraints.NotNull; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.UriBuilder; + +import org.eclipse.che.api.core.rest.HttpRequest.BodyWriter; +import org.eclipse.che.commons.env.EnvironmentContext; +import org.eclipse.che.commons.lang.Pair; +import org.eclipse.che.commons.user.User; + +/** + * A base class for all implementations of {@link HttpRequestBase}. + * + * @author Tareq Sharafy + * + * @param + */ +public class DefaultHttpRequestBase> implements HttpRequestBase { + + private static final int DEFAULT_QUERY_PARAMS_LIST_SIZE = 5; + + protected final String url; + protected int timeout; + protected String method; + protected List> queryParams; + + protected DefaultHttpRequestBase(String url, String method) { + this.url = requireNonNull(url, "Required non-null url"); + this.method = method; + } + + @SuppressWarnings("unchecked") + @Override + public RequestT setMethod(@NotNull String method) { + this.method = requireNonNull(method, "Required non-null http method"); + return (RequestT) this; + } + + @SuppressWarnings("unchecked") + @Override + public RequestT addQueryParam(@NotNull String name, @NotNull Object value) { + requireNonNull(name, "Required non-null query parameter name"); + requireNonNull(value, "Required non-null query parameter value"); + if (queryParams == null) { + queryParams = new ArrayList<>(DEFAULT_QUERY_PARAMS_LIST_SIZE); + } + queryParams.add(Pair.of(name, value)); + return (RequestT) this; + } + + @SuppressWarnings("unchecked") + @Override + public RequestT setTimeout(int timeout) { + this.timeout = timeout; + return (RequestT) this; + } + + /** + * Do any required validations before th erequest is executed. + */ + protected void beforeRequest() { + if (method == null) { + throw new IllegalStateException("Could not perform request, request method wasn't set"); + } + } + + protected HttpResponse doGeneralRequest(int timeout, String url, String method, BodyWriter bodyWriter, + Map headers, List> queryParams) throws IOException { + // Set the query parameters + if (queryParams != null && !queryParams.isEmpty()) { + final UriBuilder ub = UriBuilder.fromUri(url); + for (Pair parameter : queryParams) { + String name = URLEncoder.encode(parameter.first, "UTF-8"); + String value = parameter.second == null ? null + : URLEncoder.encode(String.valueOf(parameter.second), "UTF-8"); + ub.queryParam(name, value); + } + url = ub.build().toString(); + } + // Initialize the connection + URL urlObj = new URL(url); + HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection(); + conn.setConnectTimeout(timeout > 0 ? timeout : DEFAULT_TIMEOUT); + conn.setReadTimeout(timeout > 0 ? timeout : DEFAULT_TIMEOUT); + if (method != null) { + conn.setRequestMethod(method); + } + // Set the authorization header if present + String authToken = getAuthenticationToken(urlObj); + if (authToken != null) { + conn.setRequestProperty(HttpHeaders.AUTHORIZATION, authToken); + } + // Set all the custom headers + if (headers != null) { + headers.forEach(conn::setRequestProperty); + } + // Write the body + if (bodyWriter != null) { + conn.setDoOutput(true); + bodyWriter.writeTo(conn.getOutputStream()); + } + // The result + return new URLConnectionHttpResponse(conn); + } + + protected String getAuthenticationToken(URL urlObj) { + final User user = EnvironmentContext.getCurrent().getUser(); + if (user != null) { + return user.getToken(); + } + return null; + } + +} diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpJsonRequest.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpJsonRequest.java index 45b6b62ff..4c4c26d2d 100644 --- a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpJsonRequest.java +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpJsonRequest.java @@ -22,11 +22,9 @@ import org.eclipse.che.dto.server.JsonSerializable; import javax.validation.constraints.NotNull; -import javax.ws.rs.HttpMethod; import java.io.IOException; import java.util.List; import java.util.Map; -import java.util.Objects; /** * Defines simple set of methods for requesting json objects. @@ -60,18 +58,7 @@ * @see HttpJsonRequestFactory */ @Beta -public interface HttpJsonRequest { - - /** - * Sets http method to use in this request(e.g. {@link javax.ws.rs.HttpMethod#GET GET}). - * - * @param method - * http method - * @return this request instance - * @throws NullPointerException - * when {@code method} is null - */ - HttpJsonRequest setMethod(@NotNull String method); +public interface HttpJsonRequest extends HttpRequestBase { /** * Sets request body. @@ -108,27 +95,6 @@ public interface HttpJsonRequest { */ HttpJsonRequest setBody(@NotNull List list); - /** - * Adds query parameter to the request. - * - * @param name - * query parameter name - * @param value - * query parameter value - * @return this request instance - * @throws NullPointerException - * when either name or value is null - */ - HttpJsonRequest addQueryParam(@NotNull String name, @NotNull Object value); - - /** - * Sets request timeout. - * - * @param timeout - * request timeout - * @return this request instance - */ - HttpJsonRequest setTimeout(int timeout); /** * Makes http request with content type "application/json" and authorization headers @@ -160,61 +126,4 @@ HttpJsonResponse request() throws IOException, ConflictException, BadRequestException; - /** - * Uses {@link HttpMethod#GET} as a request method. - * - * @return this request instance - */ - default HttpJsonRequest useGetMethod() { - return setMethod(HttpMethod.GET); - } - - /** - * Uses {@link HttpMethod#OPTIONS} as a request method. - * - * @return this request instance - */ - default HttpJsonRequest useOptionsMethod() { - return setMethod(HttpMethod.OPTIONS); - } - - /** - * Uses {@link HttpMethod#POST} as a request method. - * - * @return this request instance - */ - default HttpJsonRequest usePostMethod() { - return setMethod(HttpMethod.POST); - } - - /** - * Uses {@link HttpMethod#DELETE} as a request method. - * - * @return this request instance - */ - default HttpJsonRequest useDeleteMethod() { - return setMethod(HttpMethod.DELETE); - } - - /** - * Uses {@link HttpMethod#PUT} as a request method. - * - * @return this request instance - */ - default HttpJsonRequest usePutMethod() { - return setMethod(HttpMethod.PUT); - } - - /** - * Adds set of query parameters to this request. - * - * @param params - * query parameters map - * @return this request instance - */ - default HttpJsonRequest addQueryParams(@NotNull Map params) { - Objects.requireNonNull(params, "Required non-null query parameters"); - params.forEach(this::addQueryParam); - return this; - } } diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpJsonRequestFactory.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpJsonRequestFactory.java index 8ab8c17d2..fabe1b941 100644 --- a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpJsonRequestFactory.java +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpJsonRequestFactory.java @@ -24,7 +24,7 @@ */ @Beta @ImplementedBy(DefaultHttpJsonRequestFactory.class) -public interface HttpJsonRequestFactory { +public interface HttpJsonRequestFactory extends HttpRequestFactory { /** * Creates {@link HttpJsonRequest} based on {@code url}, with an initial HTTP method {@code GET}. diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpRequest.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpRequest.java new file mode 100644 index 000000000..da41f3264 --- /dev/null +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpRequest.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.rest; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.validation.constraints.NotNull; + +import com.google.common.annotations.Beta; + +/** + * Defines simple set of methods for requesting json objects. + * + *

+ * Unlike {@link HttpJsonHelper} - provides builder-like style for building requests and getting responses. + * + *

+ * Simple use-cases: + * + *

+ * {@code
+ *     // starting new workspace
+ *     requestFactory.fromUri(apiEndpoint + "/workspace/" + id + "/runtime")
+ *                   .setMethod("POST")
+ *                   .addQueryParam("envName", envName)
+ *                   .addQueryParam("accountId", accountId)
+ *                   .request();
+ *
+ *     // getting user preferences
+ *     Map prefs = requestFactory.fromUri(apiEndpoint + "/profile/prefs")
+ *                                               .setMethod("GET")
+ *                                               .request()
+ *                                               .asProperties();
+ *
+ *    // getting workspace
+ *    UsersWorkspaceDto workspace = requestFactory.fromLink(getWorkspaceLink)
+ *                                                .request()
+ *                                                .asDto(UsersWorkspaceDto.class);
+ * }
+ * 
+ * + *

+ * Do not use this class for requesting content different from "application/json". + * + * @author Yevhenii Voevodin + * @see HttpJsonRequestFactory + */ +@Beta +public interface HttpRequest extends HttpRequestBase { + + public interface BodyWriter { + public void writeTo(OutputStream out) throws IOException; + } + + /** + * Copy the given input stream to the request body. + * + * @param bodyWriter + * write data to the request output stream. + * @return this request instance + * @throws NullPointerException + * when {@code body} is null + */ + HttpRequest setBodyWriter(@NotNull BodyWriter bodyWriter); + + /** + * Adds a header to the request. + * + * @param name + * The name of the header. + * @param value + * The value of the header. + * @return this request instance + */ + HttpRequest addHeader(@NotNull String name, String value); + + /** + * Makes http request. + * + * @return {@link HttpResponse} instance which represents response of this request + * @throws IOException + * when any io error occurs + */ + HttpResponse request() throws IOException; + +} diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpRequestBase.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpRequestBase.java new file mode 100644 index 000000000..945c772cb --- /dev/null +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpRequestBase.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.rest; + +import java.util.Map; +import java.util.Objects; + +import javax.validation.constraints.NotNull; +import javax.ws.rs.HttpMethod; + +import com.google.common.annotations.Beta; + +/** + * Defines simple set of methods for requesting json objects. + * + *

Unlike {@link HttpJsonHelper} - provides builder-like style for building requests and getting responses. + * + *

Simple use-cases: + *

{@code
+ *     // starting new workspace
+ *     requestFactory.fromUri(apiEndpoint + "/workspace/" + id + "/runtime")
+ *                   .setMethod("POST")
+ *                   .addQueryParam("envName", envName)
+ *                   .addQueryParam("accountId", accountId)
+ *                   .request();
+ *
+ *     // getting user preferences
+ *     Map prefs = requestFactory.fromUri(apiEndpoint + "/profile/prefs")
+ *                                               .setMethod("GET")
+ *                                               .request()
+ *                                               .asProperties();
+ *
+ *    // getting workspace
+ *    UsersWorkspaceDto workspace = requestFactory.fromLink(getWorkspaceLink)
+ *                                                .request()
+ *                                                .asDto(UsersWorkspaceDto.class);
+ * }
+ * + *

Do not use this class for requesting content different from "application/json". + * + * @author Yevhenii Voevodin + * @see HttpJsonRequestFactory + */ +@Beta +public interface HttpRequestBase> { + + static final int DEFAULT_TIMEOUT = 60 * 1000; + + /** + * Sets http method to use in this request(e.g. {@link javax.ws.rs.HttpMethod#GET GET}). + * + * @param method + * http method + * @return this request instance + * @throws NullPointerException + * when {@code method} is null + */ + RequestT setMethod(@NotNull String method); + + /** + * Adds query parameter to the request. + * + * @param name + * query parameter name + * @param value + * query parameter value + * @return this request instance + * @throws NullPointerException + * when either name or value is null + */ + RequestT addQueryParam(@NotNull String name, @NotNull Object value); + + /** + * Sets request timeout. + * + * @param timeout + * request timeout + * @return this request instance + */ + RequestT setTimeout(int timeout); + + /** + * Uses {@link HttpMethod#GET} as a request method. + * + * @return this request instance + */ + default RequestT useGetMethod() { + return setMethod(HttpMethod.GET); + } + + /** + * Uses {@link HttpMethod#OPTIONS} as a request method. + * + * @return this request instance + */ + default RequestT useOptionsMethod() { + return setMethod(HttpMethod.OPTIONS); + } + + /** + * Uses {@link HttpMethod#POST} as a request method. + * + * @return this request instance + */ + default RequestT usePostMethod() { + return setMethod(HttpMethod.POST); + } + + /** + * Uses {@link HttpMethod#DELETE} as a request method. + * + * @return this request instance + */ + default RequestT useDeleteMethod() { + return setMethod(HttpMethod.DELETE); + } + + /** + * Uses {@link HttpMethod#PUT} as a request method. + * + * @return this request instance + */ + default RequestT usePutMethod() { + return setMethod(HttpMethod.PUT); + } + + /** + * Adds set of query parameters to this request. + * + * @param params + * query parameters map + * @return this request instance + */ + default RequestT addQueryParams(@NotNull Map params) { + Objects.requireNonNull(params, "Required non-null query parameters"); + params.forEach(this::addQueryParam); + @SuppressWarnings("unchecked") + RequestT r = (RequestT) this; // to avoid suppressing on the whole method + return r; + } +} diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpRequestFactory.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpRequestFactory.java new file mode 100644 index 000000000..772c5de5b --- /dev/null +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpRequestFactory.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.rest; + +import javax.validation.constraints.NotNull; + +import com.google.common.annotations.Beta; +import com.google.inject.ImplementedBy; + +/** + * Factory for {@link HttpRequest} instances. + * + * @author Yevhenii Voevodin + */ +@Beta +@ImplementedBy(DefaultHttpJsonRequestFactory.class) +public interface HttpRequestFactory { + + /** + * Creates {@link HttpJsonRequest} based on {@code url}, with an initial HTTP method {@code GET}. + * + * @param url + * request target url + * @return new instance of {@link HttpRequest} + * @throws NullPointerException + * when url is null + */ + HttpRequest target(@NotNull String url); + +} diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpResponse.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpResponse.java new file mode 100644 index 000000000..8172e5691 --- /dev/null +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/HttpResponse.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.rest; + +import java.io.IOException; +import java.io.InputStream; + +import com.google.common.annotations.Beta; + +/** + * Defines response of {@link HttpRequestFactory}. + * + * @author Tareq Sharafy + */ +@Beta +public interface HttpResponse extends AutoCloseable { + + /** + * Returns a response code. + */ + int getResponseCode() throws IOException; + + /** + * The content type of the response data. + */ + String getHeaderField(String name); + + /** + * The value of the Content-Type header. + */ + String getContentType(); + + /** + * Gets a stream of the response body. + */ + InputStream getInputStream() throws IOException; + + @Override + void close() throws IOException; + +} diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/URLConnectionHttpResponse.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/URLConnectionHttpResponse.java new file mode 100644 index 000000000..a6b35c45a --- /dev/null +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/rest/URLConnectionHttpResponse.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.rest; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; + +import javax.net.ssl.HttpsURLConnection; + +/** + * An {@link HttpResponse} implementation that is based on {@link HttpURLConnection} and {@link HttpsURLConnection} + * connections. + * + * @author Tareq Sharafy + * + */ +public class URLConnectionHttpResponse implements HttpResponse { + + private final HttpURLConnection conn; + + public URLConnectionHttpResponse(HttpURLConnection conn) { + this.conn = conn; + } + + @Override + public void close() { + conn.disconnect(); + } + + @Override + public InputStream getInputStream() throws IOException { + InputStream errStm = conn.getErrorStream(); + return errStm != null ? errStm : conn.getInputStream(); + } + + @Override + public int getResponseCode() throws IOException { + return conn.getResponseCode(); + } + + @Override + public String getHeaderField(String name) { + return conn.getHeaderField(name); + } + + @Override + public String getContentType() { + return conn.getContentType(); + } + +} diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/DownloadPlugin.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/DownloadPlugin.java index bdc12895e..f6af0164d 100644 --- a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/DownloadPlugin.java +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/DownloadPlugin.java @@ -12,11 +12,14 @@ import java.io.IOException; +import com.google.inject.ImplementedBy; + /** * Downloads remote file. * * @author andrew00x */ +@ImplementedBy(DownloadPluginImpl.class) public interface DownloadPlugin { interface Callback { diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/DownloadPluginImpl.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/DownloadPluginImpl.java new file mode 100644 index 000000000..a7489fdbc --- /dev/null +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/DownloadPluginImpl.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2012-2016 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.util; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.eclipse.che.api.core.rest.HttpRequestFactory; +import org.eclipse.che.api.core.rest.HttpResponse; + +/** + * DownloadPlugin that downloads single file. + * + * @author Tareq Sharafy + */ +@Singleton +public class DownloadPluginImpl extends HttpDownloadPlugin { + + private static final int READ_TIMEOUT = (int) TimeUnit.MINUTES.toMillis(3); + + private final HttpRequestFactory provider; + + @Inject + public DownloadPluginImpl(HttpRequestFactory provider) { + this.provider = provider; + } + + @Override + protected HttpResponse openUrlConnection(String downloadUrl) throws IOException { + // Check it + HttpResponse conn = provider.target(downloadUrl).setTimeout(READ_TIMEOUT).request(); + // Connect + final int responseCode = conn.getResponseCode(); + if (responseCode != 200) { + conn.close(); + throw new IOException(String.format("Invalid response status %d from remote server. ", responseCode)); + } + return conn; + } + +} diff --git a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/HttpDownloadPlugin.java b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/HttpDownloadPlugin.java index 6cc01604e..116c4a8cb 100644 --- a/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/HttpDownloadPlugin.java +++ b/platform-api/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/HttpDownloadPlugin.java @@ -10,6 +10,8 @@ *******************************************************************************/ package org.eclipse.che.api.core.util; +import org.eclipse.che.api.core.rest.HttpResponse; +import org.eclipse.che.api.core.rest.URLConnectionHttpResponse; import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.commons.lang.NameGenerator; import org.slf4j.Logger; @@ -30,17 +32,15 @@ * * @author andrew00x */ -public final class HttpDownloadPlugin implements DownloadPlugin { +public class HttpDownloadPlugin implements DownloadPlugin { private static final Logger LOG = LoggerFactory.getLogger(HttpDownloadPlugin.class); private static final int CONNECT_TIMEOUT = (int)TimeUnit.MINUTES.toMillis(3); - private static final int READ_TIMEOUT = (int)TimeUnit.MINUTES.toMillis(3); + protected static final int READ_TIMEOUT = (int)TimeUnit.MINUTES.toMillis(3); @Override public void download(String downloadUrl, java.io.File downloadTo, Callback callback) { - HttpURLConnection conn = null; - try { - conn = openUrlConnection(downloadUrl); + try (HttpResponse conn = openUrlConnection(downloadUrl)) { final String contentDisposition = conn.getHeaderField(HttpHeaders.CONTENT_DISPOSITION); String fileName = null; if (contentDisposition != null) { @@ -65,18 +65,12 @@ public void download(String downloadUrl, java.io.File downloadTo, Callback callb } catch (IOException e) { LOG.debug(String.format("Failed access: %s, error: %s", downloadUrl, e.getMessage()), e); callback.error(e); - } finally { - if (conn != null) { - conn.disconnect(); - } } } @Override public void download(String downloadUrl, java.io.File downloadTo, String fileName, boolean replaceExisting) throws IOException { - HttpURLConnection conn = null; - try { - conn = openUrlConnection(downloadUrl); + try (HttpResponse conn = openUrlConnection(downloadUrl)) { final java.io.File downloadFile = new java.io.File(downloadTo, fileName); try (InputStream in = conn.getInputStream()) { if (replaceExisting) { @@ -85,14 +79,10 @@ public void download(String downloadUrl, java.io.File downloadTo, String fileNam Files.copy(in, downloadFile.toPath()); } } - } finally { - if (conn != null) { - conn.disconnect(); - } } } - private static HttpURLConnection openUrlConnection(String downloadUrl) throws IOException { + protected HttpResponse openUrlConnection(String downloadUrl) throws IOException { HttpURLConnection conn = (HttpURLConnection)new URL(downloadUrl).openConnection(); // Set timeouts conn.setConnectTimeout(CONNECT_TIMEOUT); @@ -107,6 +97,6 @@ private static HttpURLConnection openUrlConnection(String downloadUrl) throws IO if (responseCode != 200) { throw new IOException(String.format("Invalid response status %d from remote server. ", responseCode)); } - return conn; + return new URLConnectionHttpResponse(conn); } } diff --git a/platform-api/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/FactoryServiceTest.java b/platform-api/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/FactoryServiceTest.java index 4454d10b6..c2135edc5 100644 --- a/platform-api/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/FactoryServiceTest.java +++ b/platform-api/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/FactoryServiceTest.java @@ -123,6 +123,7 @@ public void setUp() throws Exception { editValidator, new LinksHelper(), factoryBuilder, + null, projectManager); when(accountDao.getByMember(anyString())).thenReturn(Arrays.asList(new Member().withRoles(Arrays.asList("account/owner")))); diff --git a/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RemoteRunnerProcess.java b/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RemoteRunnerProcess.java index dd786322a..35f3a56f2 100644 --- a/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RemoteRunnerProcess.java +++ b/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RemoteRunnerProcess.java @@ -12,20 +12,14 @@ import com.google.common.io.ByteStreams; -import org.eclipse.che.api.core.ConflictException; -import org.eclipse.che.api.core.ForbiddenException; import org.eclipse.che.api.core.NotFoundException; -import org.eclipse.che.api.core.ServerException; -import org.eclipse.che.api.core.UnauthorizedException; -import org.eclipse.che.api.core.rest.HttpJsonHelper; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; import org.eclipse.che.api.core.rest.HttpOutputMessage; +import org.eclipse.che.api.core.rest.HttpResponse; import org.eclipse.che.api.core.rest.OutputProvider; import org.eclipse.che.api.core.rest.shared.dto.Link; import org.eclipse.che.api.runner.dto.ApplicationProcessDescriptor; import org.eclipse.che.api.runner.internal.Constants; -import org.eclipse.che.commons.env.EnvironmentContext; -import org.eclipse.che.dto.server.DtoFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,8 +31,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; /** * Representation of remote application process. @@ -128,15 +120,7 @@ public void readRecipeFile(OutputProvider output) throws IOException, RunnerExce } private void doRequest(String url, String method, final OutputProvider output) throws IOException { - final HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection(); - conn.setConnectTimeout(60 * 1000); - conn.setReadTimeout(60 * 1000); - conn.setRequestMethod(method); - final EnvironmentContext context = EnvironmentContext.getCurrent(); - if (context.getUser() != null && context.getUser().getToken() != null) { - conn.setRequestProperty(HttpHeaders.AUTHORIZATION, context.getUser().getToken()); - } - try { + try (HttpResponse conn = requestFactory.target(url).setTimeout(60 * 1000).setMethod(method).request()) { if (output instanceof HttpOutputMessage) { HttpOutputMessage httpOutput = (HttpOutputMessage)output; httpOutput.setStatus(conn.getResponseCode()); @@ -155,9 +139,6 @@ private void doRequest(String url, String method, final OutputProvider output) t OutputStream out = output.getOutputStream()) { ByteStreams.copy(in, out); } - - } finally { - conn.disconnect(); } } } diff --git a/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RunQueue.java b/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RunQueue.java index 7f4a3e930..7f1abbd27 100644 --- a/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RunQueue.java +++ b/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RunQueue.java @@ -18,15 +18,12 @@ import org.eclipse.che.api.builder.BuilderService; import org.eclipse.che.api.builder.dto.BuildOptions; import org.eclipse.che.api.builder.dto.BuildTaskDescriptor; -import org.eclipse.che.api.core.ConflictException; -import org.eclipse.che.api.core.ForbiddenException; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.ServerException; -import org.eclipse.che.api.core.UnauthorizedException; import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.core.notification.EventSubscriber; -import org.eclipse.che.api.core.rest.HttpJsonHelper; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; +import org.eclipse.che.api.core.rest.HttpResponse; import org.eclipse.che.api.core.rest.RemoteServiceDescriptor; import org.eclipse.che.api.core.rest.ServiceContext; import org.eclipse.che.api.core.rest.shared.dto.Link; @@ -76,7 +73,6 @@ import static org.eclipse.che.api.runner.RunnerUtils.runnerRequest; import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; @@ -1189,7 +1185,7 @@ public String toString() { // >>>>>>>>>>>>>>>>>>>>>>>>>>>>> application start checker - private static class ApplicationUrlChecker implements Runnable { + private class ApplicationUrlChecker implements Runnable { final long taskId; final URL url; final int healthCheckerTimeout; @@ -1215,13 +1211,7 @@ public void run() { } catch (InterruptedException e) { return; } - HttpURLConnection conn = null; - try { - conn = (HttpURLConnection)url.openConnection(); - conn.setRequestMethod(requestMethod); - conn.setConnectTimeout(1000); - conn.setReadTimeout(1000); - + try (HttpResponse conn = requestFactory.target(url.toString()).setTimeout(1000).setMethod(requestMethod).request()) { LOG.debug(String.format("Response code: %d.", conn.getResponseCode())); if (405 == conn.getResponseCode()) { // In case of Method not allowed, we use get instead of HEAD. X-HTTP-Method-Override would be nice but support is @@ -1249,10 +1239,6 @@ public void run() { } } } catch (IOException ignored) { - } finally { - if (conn != null) { - conn.disconnect(); - } } } } diff --git a/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RunnerUtils.java b/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RunnerUtils.java index 60f09461b..a18323512 100644 --- a/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RunnerUtils.java +++ b/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/RunnerUtils.java @@ -37,10 +37,10 @@ public static HttpJsonResponse runnerRequest(HttpJsonRequest req) throws RunnerE try { return req.request(); } catch (IOException e) { - throw new RunnerException(e); + throw new RunnerException(e.getMessage(), e); } catch (ServerException | UnauthorizedException | ForbiddenException | NotFoundException | ConflictException | BadRequestException e) { - throw new RunnerException(e.getServiceError()); + throw new RunnerException(e.getMessage(), e); } } diff --git a/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/internal/Runner.java b/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/internal/Runner.java index fe3859438..ebc545ed3 100644 --- a/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/internal/Runner.java +++ b/platform-api/che-core-api-runner/src/main/java/org/eclipse/che/api/runner/internal/Runner.java @@ -35,6 +35,8 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import javax.inject.Inject; + import java.io.IOException; import java.nio.file.Files; import java.util.Collections; @@ -90,6 +92,10 @@ public boolean isValid(DeploymentSources deployment) { private ScheduledExecutorService cleanScheduler; private java.io.File deployDirectory; + @Inject + private DownloadPlugin theDownloadPlugin; + /** @deprecated use {@link #downloadFile(String, java.io.File, String, boolean)} */ + @Deprecated protected final DownloadPlugin downloadPlugin; public Runner(java.io.File deployDirectoryRoot, int cleanupDelay, ResourceAllocators allocators, EventService eventService) { @@ -374,7 +380,7 @@ protected DeploymentSources createDeploymentSources(RunRequest request, java.io. return NO_SOURCES; } final DownloadCallback callback = new DownloadCallback(); - downloadPlugin.download(url, dir, callback); + theDownloadPlugin.download(url, dir, callback); if (callback.getError() != null) { throw callback.getError(); } @@ -413,7 +419,7 @@ public IOException getError() { } protected java.io.File downloadFile(String url, java.io.File downloadDir, String fileName, boolean replaceExisting) throws IOException { - downloadPlugin.download(url, downloadDir, fileName, replaceExisting); + theDownloadPlugin.download(url, downloadDir, fileName, replaceExisting); return new java.io.File(downloadDir, fileName); } diff --git a/platform-api/che-core-api-runner/src/test/java/org/eclipse/che/api/runner/RunQueueTest.java b/platform-api/che-core-api-runner/src/test/java/org/eclipse/che/api/runner/RunQueueTest.java index 10c52f1ca..52a70ba3c 100644 --- a/platform-api/che-core-api-runner/src/test/java/org/eclipse/che/api/runner/RunQueueTest.java +++ b/platform-api/che-core-api-runner/src/test/java/org/eclipse/che/api/runner/RunQueueTest.java @@ -25,6 +25,8 @@ import org.eclipse.che.api.core.rest.DefaultHttpJsonResponse; import org.eclipse.che.api.core.rest.HttpJsonRequest; import org.eclipse.che.api.core.rest.HttpJsonRequestFactory; +import org.eclipse.che.api.core.rest.HttpRequest; +import org.eclipse.che.api.core.rest.HttpResponse; import org.eclipse.che.api.core.rest.RemoteServiceDescriptor; import org.eclipse.che.api.core.rest.ServiceContext; import org.eclipse.che.api.core.rest.shared.dto.Link; @@ -148,6 +150,11 @@ public HttpJsonRequest fromUrl(String url) { public HttpJsonRequest fromLink(Link link) { return new TestJsonRequest(link); } + + @Override + public HttpRequest target(String url) { + return null; + } } @BeforeMethod