/* * Copyright 2013-2017 the original author or authors. * * 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.cloudfoundry.reactor.client.v2.applications; import org.cloudfoundry.client.v2.applications.ApplicationEnvironmentRequest; import org.cloudfoundry.client.v2.applications.ApplicationEnvironmentResponse; import org.cloudfoundry.client.v2.applications.ApplicationInstancesRequest; import org.cloudfoundry.client.v2.applications.ApplicationInstancesResponse; import org.cloudfoundry.client.v2.applications.ApplicationStatisticsRequest; import org.cloudfoundry.client.v2.applications.ApplicationStatisticsResponse; import org.cloudfoundry.client.v2.applications.ApplicationsV2; import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteRequest; import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteResponse; import org.cloudfoundry.client.v2.applications.CopyApplicationRequest; import org.cloudfoundry.client.v2.applications.CopyApplicationResponse; import org.cloudfoundry.client.v2.applications.CreateApplicationRequest; import org.cloudfoundry.client.v2.applications.CreateApplicationResponse; import org.cloudfoundry.client.v2.applications.DeleteApplicationRequest; import org.cloudfoundry.client.v2.applications.DownloadApplicationDropletRequest; import org.cloudfoundry.client.v2.applications.DownloadApplicationRequest; import org.cloudfoundry.client.v2.applications.GetApplicationRequest; import org.cloudfoundry.client.v2.applications.GetApplicationResponse; import org.cloudfoundry.client.v2.applications.ListApplicationRoutesRequest; import org.cloudfoundry.client.v2.applications.ListApplicationRoutesResponse; import org.cloudfoundry.client.v2.applications.ListApplicationServiceBindingsRequest; import org.cloudfoundry.client.v2.applications.ListApplicationServiceBindingsResponse; import org.cloudfoundry.client.v2.applications.ListApplicationsRequest; import org.cloudfoundry.client.v2.applications.ListApplicationsResponse; import org.cloudfoundry.client.v2.applications.RemoveApplicationRouteRequest; import org.cloudfoundry.client.v2.applications.RemoveApplicationServiceBindingRequest; import org.cloudfoundry.client.v2.applications.RestageApplicationRequest; import org.cloudfoundry.client.v2.applications.RestageApplicationResponse; import org.cloudfoundry.client.v2.applications.SummaryApplicationRequest; import org.cloudfoundry.client.v2.applications.SummaryApplicationResponse; import org.cloudfoundry.client.v2.applications.TerminateApplicationInstanceRequest; import org.cloudfoundry.client.v2.applications.UpdateApplicationRequest; import org.cloudfoundry.client.v2.applications.UpdateApplicationResponse; import org.cloudfoundry.client.v2.applications.UploadApplicationRequest; import org.cloudfoundry.client.v2.applications.UploadApplicationResponse; import org.cloudfoundry.reactor.ConnectionContext; import org.cloudfoundry.reactor.TokenProvider; import org.cloudfoundry.reactor.client.v2.AbstractClientV2Operations; import org.cloudfoundry.reactor.util.MultipartHttpClientRequest; import org.cloudfoundry.util.FileUtils; import reactor.core.Exceptions; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.ipc.netty.http.client.HttpClientRequest; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON; /** * The Reactor-based implementation of {@link ApplicationsV2} */ public final class ReactorApplicationsV2 extends AbstractClientV2Operations implements ApplicationsV2 { /** * Creates an instance * * @param connectionContext the {@link ConnectionContext} to use when communicating with the server * @param root the root URI of the server. Typically something like {@code https://api.run.pivotal.io}. * @param tokenProvider the {@link TokenProvider} to use when communicating with the server */ public ReactorApplicationsV2(ConnectionContext connectionContext, Mono<String> root, TokenProvider tokenProvider) { super(connectionContext, root, tokenProvider); } @Override public Mono<AssociateApplicationRouteResponse> associateRoute(AssociateApplicationRouteRequest request) { return put(request, AssociateApplicationRouteResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "routes", request.getRouteId())) .checkpoint(); } @Override public Mono<CopyApplicationResponse> copy(CopyApplicationRequest request) { return post(request, CopyApplicationResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "copy_bits")) .checkpoint(); } @Override public Mono<CreateApplicationResponse> create(CreateApplicationRequest request) { return post(request, CreateApplicationResponse.class, builder -> builder.pathSegment("v2", "apps")) .checkpoint(); } @Override public Mono<Void> delete(DeleteApplicationRequest request) { return delete(request, Void.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId())) .checkpoint(); } @Override public Flux<byte[]> download(DownloadApplicationRequest request) { return get(request, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "download"), outbound -> outbound.map(HttpClientRequest::followRedirect)) .flatMapMany(response -> response.receive().asByteArray()) .checkpoint(); } @Override public Flux<byte[]> downloadDroplet(DownloadApplicationDropletRequest request) { return get(request, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "droplet", "download"), outbound -> outbound.map(HttpClientRequest::followRedirect)) .flatMapMany(response -> response.receive().asByteArray()) .checkpoint(); } @Override public Mono<ApplicationEnvironmentResponse> environment(ApplicationEnvironmentRequest request) { return get(request, ApplicationEnvironmentResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "env")) .checkpoint(); } @Override public Mono<GetApplicationResponse> get(GetApplicationRequest request) { return get(request, GetApplicationResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId())) .checkpoint(); } @Override public Mono<ApplicationInstancesResponse> instances(ApplicationInstancesRequest request) { return get(request, ApplicationInstancesResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "instances")) .checkpoint(); } @Override public Mono<ListApplicationsResponse> list(ListApplicationsRequest request) { return get(request, ListApplicationsResponse.class, builder -> builder.pathSegment("v2", "apps")) .checkpoint(); } @Override public Mono<ListApplicationRoutesResponse> listRoutes(ListApplicationRoutesRequest request) { return get(request, ListApplicationRoutesResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "routes")) .checkpoint(); } @Override public Mono<ListApplicationServiceBindingsResponse> listServiceBindings(ListApplicationServiceBindingsRequest request) { return get(request, ListApplicationServiceBindingsResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "service_bindings")) .checkpoint(); } @Override public Mono<Void> removeRoute(RemoveApplicationRouteRequest request) { return delete(request, Void.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "routes", request.getRouteId())) .checkpoint(); } @Override public Mono<Void> removeServiceBinding(RemoveApplicationServiceBindingRequest request) { return delete(request, Void.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "service_bindings", request.getServiceBindingId())) .checkpoint(); } @Override public Mono<RestageApplicationResponse> restage(RestageApplicationRequest request) { return post(request, RestageApplicationResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "restage")) .checkpoint(); } @Override public Mono<ApplicationStatisticsResponse> statistics(ApplicationStatisticsRequest request) { return get(request, ApplicationStatisticsResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "stats")) .checkpoint(); } @Override public Mono<SummaryApplicationResponse> summary(SummaryApplicationRequest request) { return get(request, SummaryApplicationResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "summary")) .checkpoint(); } @Override public Mono<Void> terminateInstance(TerminateApplicationInstanceRequest request) { return delete(request, Void.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "instances", request.getIndex())) .checkpoint(); } @Override public Mono<UpdateApplicationResponse> update(UpdateApplicationRequest request) { return put(request, UpdateApplicationResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId())) .checkpoint(); } @SuppressWarnings("unchecked") @Override public Mono<UploadApplicationResponse> upload(UploadApplicationRequest request) { return put(request, UploadApplicationResponse.class, builder -> builder.pathSegment("v2", "apps", request.getApplicationId(), "bits"), outbound -> outbound .then(r -> { if (Files.isDirectory(request.getApplication())) { return FileUtils.compress(request.getApplication()) .then(application -> upload(application, r, request) .doOnTerminate((v, t) -> { try { Files.delete(application); } catch (IOException e) { throw Exceptions.propagate(e); } })); } else { return upload(request.getApplication(), r, request); } })) .checkpoint(); } private Mono<Void> upload(Path application, MultipartHttpClientRequest r, UploadApplicationRequest request) { return r .addPart(part -> part .setContentDispositionFormData("resources") .setHeader(CONTENT_TYPE, APPLICATION_JSON) .send(request.getResources())) .addPart(part -> part .setContentDispositionFormData("application", "application.zip") .setHeader(CONTENT_TYPE, APPLICATION_ZIP) .sendFile(application)) .done(); } }