diff --git a/api/pom.xml b/api/pom.xml index 3ff053461..f5167fd90 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT true @@ -147,12 +147,43 @@ guava-testlib test + jakarta.servlet jakarta.servlet-api + + io.grpc + grpc-netty-shaded + + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + + + io.grpc + grpc-auth + + + com.google.auth + google-auth-library-oauth2-http + + + io.grpc + grpc-testing + test + + + com.google.cloud + google-cloud-storage + + org.apache.maven.plugins @@ -213,8 +244,13 @@ false + + + + + docFX @@ -266,12 +302,12 @@ com.google.auto.service auto-service - 1.1.1 + ${auto-service.version} com.google.auto.value auto-value - 1.11.1 + ${auto-value.version} diff --git a/api/src/main/java/com/google/appengine/api/SystemEnvironmentProvider.java b/api/src/main/java/com/google/appengine/api/SystemEnvironmentProvider.java new file mode 100644 index 000000000..a27c345d0 --- /dev/null +++ b/api/src/main/java/com/google/appengine/api/SystemEnvironmentProvider.java @@ -0,0 +1,45 @@ +/* + * Copyright 2021 Google LLC + * + * 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 + * + * https://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 com.google.appengine.api; + +/** A simple wrapper around {@link System} to allow for easier testing. */ +public class SystemEnvironmentProvider implements EnvironmentProvider { + /** + * Gets the value of the specified environment variable. + * + * @param name the name of the environment variable + * @return the string value of the variable, or {@code null} if the variable is not defined + */ + @Override + public String getenv(String name) { + return System.getenv(name); + } + + /** + * Gets the value of the specified environment variable, returning a default value if the variable + * is not defined. + * + * @param name the name of the environment variable + * @param defaultValue the default value to return + * @return the string value of the variable, or the default value if the variable is not defined + */ + @Override + public String getenv(String name, String defaultValue) { + String value = System.getenv(name); + return value != null ? value : defaultValue; + } +} diff --git a/api/src/main/java/com/google/appengine/api/images/Composite.java b/api/src/main/java/com/google/appengine/api/images/Composite.java index b169b9899..bb5f1da7b 100644 --- a/api/src/main/java/com/google/appengine/api/images/Composite.java +++ b/api/src/main/java/com/google/appengine/api/images/Composite.java @@ -16,7 +16,9 @@ package com.google.appengine.api.images; +import com.google.appengine.api.images.ImagesServicePb.ImageData; import java.util.Map; +import java.util.function.Function; /** * A {@code Composite} represents a composition of an image onto a canvas. @@ -34,10 +36,13 @@ public static enum Anchor {TOP_LEFT, TOP_CENTER, TOP_RIGHT, CENTER_LEFT, /** * Adds this compositing operation to a Composite request. + * * @param request Request for this composite to be added to. * @param imageIndexMap Map of images and their indexes in the request. + * @param imageDataConverter Function to convert an Image to ImageData. */ - abstract void apply(ImagesServicePb.ImagesCompositeRequest.Builder request, - Map imageIndexMap); - + abstract void apply( + ImagesServicePb.ImagesCompositeRequest.Builder request, + Map imageIndexMap, + Function imageDataConverter); } diff --git a/api/src/main/java/com/google/appengine/api/images/CompositeImpl.java b/api/src/main/java/com/google/appengine/api/images/CompositeImpl.java index ec5de5c58..468e74555 100644 --- a/api/src/main/java/com/google/appengine/api/images/CompositeImpl.java +++ b/api/src/main/java/com/google/appengine/api/images/CompositeImpl.java @@ -19,8 +19,10 @@ import static java.util.Objects.requireNonNull; import com.google.appengine.api.images.ImagesServicePb.CompositeImageOptions; +import com.google.appengine.api.images.ImagesServicePb.ImageData; import com.google.appengine.api.images.ImagesServicePb.ImagesCompositeRequest; import java.util.Map; +import java.util.function.Function; /** * Implementation of Composite using alpha blending. @@ -68,11 +70,14 @@ final class CompositeImpl extends Composite { /** {@inheritDoc} */ @Override - void apply(ImagesCompositeRequest.Builder request, Map imageIndexMap) { + void apply( + ImagesCompositeRequest.Builder request, + Map imageIndexMap, + Function imageDataConverter) { // TODO: What is the purpose of this map? if (!imageIndexMap.containsKey(image)) { imageIndexMap.put(image, request.build().getImageCount()); - request.addImage(ImagesServiceImpl.convertImageData(image)); + request.addImage(imageDataConverter.apply(image)); } CompositeImageOptions.Builder options = CompositeImageOptions.newBuilder(); int sourceId = requireNonNull(imageIndexMap.get(image)); diff --git a/api/src/main/java/com/google/appengine/api/images/GrpcImagesClient.java b/api/src/main/java/com/google/appengine/api/images/GrpcImagesClient.java new file mode 100644 index 000000000..5bf3d22a9 --- /dev/null +++ b/api/src/main/java/com/google/appengine/api/images/GrpcImagesClient.java @@ -0,0 +1,142 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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 com.google.appengine.api.images; + +import com.google.appengine.api.EnvironmentProvider; +import com.google.appengine.api.SystemEnvironmentProvider; +import com.google.appengine.api.images.proto.ImagesServiceGrpc; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.auth.oauth2.IdTokenCredentials; +import com.google.auth.oauth2.IdTokenProvider; +import com.google.common.annotations.VisibleForTesting; +import static com.google.common.base.Strings.isNullOrEmpty; +import io.grpc.CallCredentials; +import io.grpc.ManagedChannel; +import io.grpc.auth.MoreCallCredentials; +import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; +import java.io.IOException; +import java.net.URI; +import static java.util.concurrent.TimeUnit.SECONDS; +import java.net.URISyntaxException; + +/** Client for interacting with the gRPC based Images service. */ +class GrpcImagesClient { + + private static final int MAX_MESSAGE_SIZE = 32 * 1024 * 1024; // 32MB + private final ManagedChannel channel; + private final EnvironmentProvider environmentProvider; + private final CallCredentials callCredentials; + + // private ImagesServiceGrpc.ImagesServiceBlockingStub blockingStub; + + public GrpcImagesClient() { + this(new SystemEnvironmentProvider(), getApplicationDefaultCredentials()); + } + + // Constructor for production + GrpcImagesClient(EnvironmentProvider environmentProvider, GoogleCredentials googleCredentials) { + this(environmentProvider, createOidcCredentials(environmentProvider, googleCredentials)); + } + + // Constructor for testing + @VisibleForTesting + GrpcImagesClient(EnvironmentProvider environmentProvider, CallCredentials callCredentials) { + this.environmentProvider = environmentProvider; + this.callCredentials = callCredentials; + String target = getTarget(); + this.channel = + NettyChannelBuilder.forTarget(target) + .maxInboundMessageSize(MAX_MESSAGE_SIZE) + .keepAliveTime(60, SECONDS) + .useTransportSecurity() + .build(); + } + + private static GoogleCredentials getApplicationDefaultCredentials() { + try { + return GoogleCredentials.getApplicationDefault(); + } catch (IOException e) { + throw new IllegalStateException("Failed to get Application Default Credentials", e); + } + } + + private String getTarget() { + String endpoint = + environmentProvider.getenv(ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV); + if (isNullOrEmpty(endpoint)) { + throw new IllegalStateException( + ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV + " environment variable not set."); + } + try { + URI uri = new URI(endpoint); + String host = uri.getHost(); + if (host == null) { + throw new IllegalStateException( + "Invalid URI in " + + ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV + + ": " + + endpoint); + } + return host + ":443"; + } catch (URISyntaxException e) { + throw new IllegalStateException( + "Invalid URI in " + + ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV + + ": " + + endpoint, + e); + } + } + + private static CallCredentials createOidcCredentials( + EnvironmentProvider environmentProvider, GoogleCredentials googleCredentials) { + String endpoint = + environmentProvider.getenv(ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV); + if (isNullOrEmpty(endpoint)) { + throw new IllegalStateException( + ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV + " environment variable not set."); + } + + if (!(googleCredentials instanceof IdTokenProvider idTokenProvider)) { + throw new IllegalStateException( + "The Application Default Credentials do not support OIDC ID token generation."); + } + + IdTokenCredentials idTokenCredentials = + IdTokenCredentials.newBuilder() + .setTargetAudience(endpoint) + .setIdTokenProvider(idTokenProvider) + .build(); + return MoreCallCredentials.from(idTokenCredentials); + } + + public ImagesServiceGrpc.ImagesServiceBlockingStub getBlockingStub() { + return ImagesServiceGrpc.newBlockingStub(channel).withCallCredentials(callCredentials); + } + + public ImagesServiceGrpc.ImagesServiceFutureStub getFutureStub() { + return ImagesServiceGrpc.newFutureStub(channel).withCallCredentials(callCredentials); + } + + public void shutdown() { + if (channel != null && !channel.isShutdown()) { + try { + channel.shutdown().awaitTermination(5, SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + // Handle exception + } + } + } +} diff --git a/api/src/main/java/com/google/appengine/api/images/ImagesServiceFactoryImpl.java b/api/src/main/java/com/google/appengine/api/images/ImagesServiceFactoryImpl.java index 8851452d7..98ef881fd 100644 --- a/api/src/main/java/com/google/appengine/api/images/ImagesServiceFactoryImpl.java +++ b/api/src/main/java/com/google/appengine/api/images/ImagesServiceFactoryImpl.java @@ -16,8 +16,13 @@ package com.google.appengine.api.images; +import com.google.appengine.api.EnvironmentProvider; +import com.google.appengine.api.SystemEnvironmentProvider; import com.google.appengine.api.blobstore.BlobKey; import com.google.appengine.api.blobstore.BlobstoreServiceFactory; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import java.util.Collection; /** @@ -27,9 +32,30 @@ */ final class ImagesServiceFactoryImpl implements IImagesServiceFactory { + @VisibleForTesting + static final String USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV = + "APPENGINE_USE_CUSTOM_IMAGES_GRPC_SERVICE"; + + @VisibleForTesting + static final String IMAGES_SERVICE_ENDPOINT_ENV = "APPENGINE_IMAGES_SERVICE_ENDPOINT"; + + private EnvironmentProvider environmentProvider = new SystemEnvironmentProvider(); + + private static final Supplier grpcClientSupplier = + Suppliers.memoize(() -> new GrpcImagesClient()); + + @VisibleForTesting + void setEnvironmentProvider(EnvironmentProvider environmentProvider) { + this.environmentProvider = environmentProvider; + } + @Override public ImagesService getImagesService() { - return new ImagesServiceImpl(); + GrpcImagesClient client = null; + if (Boolean.parseBoolean(environmentProvider.getenv(USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV))) { + client = grpcClientSupplier.get(); + } + return new ImagesServiceImpl(environmentProvider, client); } @Override @@ -44,6 +70,12 @@ public Image makeImageFromBlob(BlobKey blobKey) { @Override public Image makeImageFromFilename(String filename) { + if (Boolean.parseBoolean(environmentProvider.getenv(USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV))) { + if (!filename.startsWith("/gs/")) { + throw new IllegalArgumentException("Google storage filenames must be prefixed with /gs/"); + } + return new ImageImpl(new BlobKey(filename)); + } BlobKey blobKey = BlobstoreServiceFactory.getBlobstoreService().createGsBlobKey(filename); return new ImageImpl(blobKey); } diff --git a/api/src/main/java/com/google/appengine/api/images/ImagesServiceImpl.java b/api/src/main/java/com/google/appengine/api/images/ImagesServiceImpl.java index dc402dd80..23fac426f 100644 --- a/api/src/main/java/com/google/appengine/api/images/ImagesServiceImpl.java +++ b/api/src/main/java/com/google/appengine/api/images/ImagesServiceImpl.java @@ -18,7 +18,14 @@ import static java.util.Objects.requireNonNull; +import com.google.appengine.api.EnvironmentProvider; +import com.google.appengine.api.SystemEnvironmentProvider; +import com.google.appengine.api.blobstore.BlobInfo; +import com.google.appengine.api.blobstore.BlobInfoFactory; import com.google.appengine.api.blobstore.BlobKey; +import com.google.appengine.api.blobstore.BlobstoreFailureException; +import com.google.appengine.api.blobstore.BlobstoreInputStream; +import com.google.appengine.api.blobstore.BlobstoreService; import com.google.appengine.api.blobstore.BlobstoreServiceFactory; import com.google.appengine.api.images.ImagesServicePb.ImageData; import com.google.appengine.api.images.ImagesServicePb.ImagesCompositeRequest; @@ -35,23 +42,132 @@ import com.google.appengine.api.images.ImagesServicePb.ImagesTransformResponse; import com.google.appengine.api.images.ImagesServicePb.InputSettings.ORIENTATION_CORRECTION_TYPE; import com.google.appengine.api.images.ImagesServicePb.OutputSettings.MIME_TYPE; +import com.google.appengine.api.images.proto.ImagesServiceGrpc.ImagesServiceBlockingStub; import com.google.appengine.api.utils.FutureWrapper; import com.google.apphosting.api.ApiProxy; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.util.concurrent.ListenableFuture; import com.google.protobuf.ByteString; +import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.InvalidProtocolBufferException; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; import java.io.IOException; +import java.io.InputStream; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; +import java.util.logging.Logger; import org.jspecify.annotations.Nullable; -/** - * Implementation of the ImagesService interface. - * - */ +/** Implementation of the ImagesService interface. */ final class ImagesServiceImpl implements ImagesService { + private static final Logger logger = Logger.getLogger(ImagesServiceImpl.class.getName()); + static final String PACKAGE = "images"; + private static final int MAX_BLOB_SIZE = 32 * 1024 * 1024; // 32MB + + private final EnvironmentProvider environmentProvider; + private volatile GrpcImagesClient grpcClient; + + + private final BlobstoreService blobstoreService; + private final BlobstoreReference blobstoreReference; + private final BlobInfoFactory blobInfoFactory; + private final @Nullable Storage storage; + + interface BlobstoreReference { + InputStream openStream(BlobKey key) throws IOException; + } + + public ImagesServiceImpl() { + this(new SystemEnvironmentProvider(), null, null, null, null); + } + + @VisibleForTesting + ImagesServiceImpl(EnvironmentProvider environmentProvider, GrpcImagesClient grpcClient) { + this(environmentProvider, grpcClient, null, null, null); + } + + @VisibleForTesting + ImagesServiceImpl( + EnvironmentProvider environmentProvider, + GrpcImagesClient grpcClient, + BlobstoreService blobstoreService) { + this(environmentProvider, grpcClient, blobstoreService, null, null); + } + + @VisibleForTesting + ImagesServiceImpl( + EnvironmentProvider environmentProvider, + GrpcImagesClient grpcClient, + BlobstoreService blobstoreService, + BlobstoreReference blobstoreReference) { + this(environmentProvider, grpcClient, blobstoreService, blobstoreReference, null); + } + + @VisibleForTesting + ImagesServiceImpl( + EnvironmentProvider environmentProvider, + GrpcImagesClient grpcClient, + BlobstoreService blobstoreService, + BlobstoreReference blobstoreReference, + Storage storage) { + this(environmentProvider, grpcClient, blobstoreService, blobstoreReference, storage, null); + } + + @VisibleForTesting + ImagesServiceImpl( + EnvironmentProvider environmentProvider, + GrpcImagesClient grpcClient, + BlobstoreService blobstoreService, + BlobstoreReference blobstoreReference, + Storage storage, + BlobInfoFactory blobInfoFactory) { + this.environmentProvider = environmentProvider; + this.grpcClient = grpcClient; + this.blobstoreService = blobstoreService; + this.storage = storage; + this.blobInfoFactory = blobInfoFactory != null ? blobInfoFactory : new BlobInfoFactory(); + this.blobstoreReference = + blobstoreReference != null + ? blobstoreReference + : new BlobstoreReference() { + @Override + public InputStream openStream(BlobKey key) throws IOException { + return new BlobstoreInputStream(key); + } + }; + } + + // A package-private method to get the GrpcImagesClient instance, visible for testing. + @VisibleForTesting + GrpcImagesClient getGrpcClient() { + if (grpcClient == null) { + synchronized (this) { + if (grpcClient == null) { + grpcClient = new GrpcImagesClient(); + } + } + } + return grpcClient; + } + + private ImagesServiceBlockingStub getGrpcStub() { + return getGrpcClient().getBlockingStub(); + } + + @VisibleForTesting + boolean useGrpc() { + String envVar = + environmentProvider.getenv(ImagesServiceFactoryImpl.USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV); + return Boolean.parseBoolean(envVar); + } /** {@inheritDoc} */ @Override @@ -98,23 +214,39 @@ public Image applyTransform( Image image, InputSettings inputSettings, OutputSettings outputSettings) { + if (useGrpc()) { + try { + ImageData imageData = loadImageData(image, getBlobstoreService()); + ImagesTransformRequest request = + generateImagesTransformRequest(transform, imageData, inputSettings, outputSettings) + .build(); + ImagesTransformResponse response = getGrpcStub().transform(request); + image.setImageData(response.getImage().getContent().toByteArray()); + return image; + } catch (StatusRuntimeException e) { + throw convertGrpcException(e); + } + } ImagesTransformRequest.Builder request = - generateImagesTransformRequest(transform, image, inputSettings, outputSettings); + generateImagesTransformRequest( + transform, convertImageData(image), inputSettings, outputSettings); ImagesTransformResponse.Builder response = ImagesTransformResponse.newBuilder(); try { - byte[] responseBytes = ApiProxy.makeSyncCall(PACKAGE, "Transform", - request.build().toByteArray()); - response.mergeFrom(responseBytes); + byte[] responseBytes = + ApiProxy.makeSyncCall(PACKAGE, "Transform", request.build().toByteArray()); + response.mergeFrom(responseBytes, ExtensionRegistry.getEmptyRegistry()); } catch (InvalidProtocolBufferException ex) { throw new ImagesServiceFailureException("Invalid protocol buffer:", ex); } catch (ApiProxy.ApplicationException ex) { - throw convertApplicationException(request, ex); + throw convertApplicationException(ex); } image.setImageData(response.getImage().getContent().toByteArray()); return image; } + + /** {@inheritDoc} */ @Override public Future applyTransformAsync( @@ -122,17 +254,42 @@ public Future applyTransformAsync( final Image image, InputSettings inputSettings, OutputSettings outputSettings) { + if (useGrpc()) { + ImageData imageData = loadImageData(image, getBlobstoreService()); + ImagesTransformRequest request = + generateImagesTransformRequest(transform, imageData, inputSettings, outputSettings) + .build(); + ListenableFuture responseFuture = + getGrpcClient().getFutureStub().transform(request); + + return new FutureWrapper(responseFuture) { + @Override + protected Image wrap(ImagesTransformResponse response) { + image.setImageData(response.getImage().getContent().toByteArray()); + return image; + } + + @Override + protected Throwable convertException(Throwable cause) { + if (cause instanceof StatusRuntimeException statusRuntimeException) { + return convertGrpcException(statusRuntimeException); + } + return cause; + } + }; + } final ImagesTransformRequest.Builder request = - generateImagesTransformRequest(transform, image, inputSettings, outputSettings); + generateImagesTransformRequest( + transform, convertImageData(image), inputSettings, outputSettings); - Future responseBytes = ApiProxy.makeAsyncCall(PACKAGE, "Transform", - request.build().toByteArray()); - return new FutureWrapper(responseBytes){ + Future responseBytes = + ApiProxy.makeAsyncCall(PACKAGE, "Transform", request.build().toByteArray()); + return new FutureWrapper(responseBytes) { @Override - protected Image wrap(byte @Nullable[] responseBytes) throws IOException { + protected Image wrap(byte @Nullable [] responseBytes) throws IOException { ImagesTransformResponse.Builder response = - ImagesTransformResponse.newBuilder() - .mergeFrom(responseBytes); + ImagesTransformResponse.newBuilder() + .mergeFrom(responseBytes, ExtensionRegistry.getEmptyRegistry()); image.setImageData(response.getImage().getContent().toByteArray()); return image; @@ -141,13 +298,15 @@ protected Image wrap(byte @Nullable[] responseBytes) throws IOException { @Override protected Throwable convertException(Throwable cause) { if (cause instanceof ApiProxy.ApplicationException applicationException) { - return convertApplicationException(request, applicationException); + return convertApplicationException(applicationException); } return cause; } }; } + + /** {@inheritDoc} */ @Override public Image composite(Collection composites, int width, int height, long color) { @@ -200,15 +359,29 @@ public Image composite( canvas.setOutput(convertOutputSettings(settings)); request.setCanvas(canvas); - Map imageIdMap = new HashMap(); + if (useGrpc()) { + Map imageIdMap = new HashMap<>(); + BlobstoreService blobstoreService = getBlobstoreService(); + for (Composite composite : composites) { + composite.apply(request, imageIdMap, img -> loadImageData(img, blobstoreService)); + } + try { + ImagesCompositeResponse grpcResponse = getGrpcStub().composite(request.build()); + return ImagesServiceFactory.makeImage(grpcResponse.getImage().getContent().toByteArray()); + } catch (StatusRuntimeException e) { + throw convertGrpcException(e); + } + } + + Map imageIdMap = new HashMap<>(); for (Composite composite : composites) { - composite.apply(request, imageIdMap); + composite.apply(request, imageIdMap, ImagesServiceImpl::convertImageData); } try { byte[] responseBytes = ApiProxy.makeSyncCall(PACKAGE, "Composite", request.build().toByteArray()); - response.mergeFrom(responseBytes); + response.mergeFrom(responseBytes, ExtensionRegistry.getEmptyRegistry()); } catch (InvalidProtocolBufferException ex) { throw new ImagesServiceFailureException("Invalid protocol buffer:", ex); } catch (ApiProxy.ApplicationException ex) { @@ -226,13 +399,43 @@ public Image composite( /** {@inheritDoc} */ @Override public int[][] histogram(Image image) { + if (useGrpc()) { + ImagesHistogramRequest.Builder request = + ImagesHistogramRequest.newBuilder() + .setImage(loadImageData(image, BlobstoreServiceFactory.getBlobstoreService())); + ImagesHistogramResponse response; + try { + response = getGrpcStub().histogram(request.build()); + } catch (StatusRuntimeException e) { + throw convertGrpcException(e); + } + ImagesHistogram histogram = response.getHistogram(); + int[][] result = new int[3][]; + for (int i = 0; i < 3; i++) { + result[i] = new int[256]; + } + int redCount = Math.min(256, histogram.getRedCount()); + int greenCount = Math.min(256, histogram.getGreenCount()); + int blueCount = Math.min(256, histogram.getBlueCount()); + + for (int i = 0; i < redCount; i++) { + result[0][i] = histogram.getRed(i); + } + for (int i = 0; i < greenCount; i++) { + result[1][i] = histogram.getGreen(i); + } + for (int i = 0; i < blueCount; i++) { + result[2][i] = histogram.getBlue(i); + } + return result; + } ImagesHistogramRequest.Builder request = ImagesHistogramRequest.newBuilder(); ImagesHistogramResponse.Builder response = ImagesHistogramResponse.newBuilder(); request.setImage(convertImageData(image)); try { byte[] responseBytes = ApiProxy.makeSyncCall(PACKAGE, "Histogram", request.build().toByteArray()); - response.mergeFrom(responseBytes); + response.mergeFrom(responseBytes, ExtensionRegistry.getEmptyRegistry()); } catch (InvalidProtocolBufferException ex) { throw new ImagesServiceFailureException("Invalid protocol buffer:", ex); } catch (ApiProxy.ApplicationException ex) { @@ -258,11 +461,19 @@ public int[][] histogram(Image image) { /** {@inheritDoc} */ public String getServingUrl(BlobKey blobKey) { + if (useGrpc()) { + throw new UnsupportedOperationException( + "getServingUrl is not supported when using the gRPC Images Service."); + } return getServingUrl(blobKey, false); } /** {@inheritDoc} */ public String getServingUrl(BlobKey blobKey, boolean secureUrl) { + if (useGrpc()) { + throw new UnsupportedOperationException( + "getServingUrl is not supported when using the gRPC Images Service."); + } // The following check maintains the pre-existing contract for this method. if (blobKey == null) { throw new NullPointerException("blobKey cannot be null"); @@ -275,12 +486,20 @@ public String getServingUrl(BlobKey blobKey, boolean secureUrl) { /** {@inheritDoc} */ @Override public String getServingUrl(BlobKey blobKey, int imageSize, boolean crop) { + if (useGrpc()) { + throw new UnsupportedOperationException( + "getServingUrl is not supported when using the gRPC Images Service."); + } return getServingUrl(blobKey, imageSize, crop, false); } /** {@inheritDoc} */ @Override public String getServingUrl(BlobKey blobKey, int imageSize, boolean crop, boolean secureUrl) { + if (useGrpc()) { + throw new UnsupportedOperationException( + "getServingUrl is not supported when using the gRPC Images Service."); + } // The following check maintains the pre-existing contract for this method. if (blobKey == null) { throw new NullPointerException("blobKey cannot be null"); @@ -295,6 +514,10 @@ public String getServingUrl(BlobKey blobKey, int imageSize, boolean crop, boolea @Override public String getServingUrl(ServingUrlOptions options) { + if (useGrpc()) { + throw new UnsupportedOperationException( + "getServingUrl is not supported when using the gRPC Images Service."); + } ImagesGetUrlBaseRequest.Builder request = ImagesGetUrlBaseRequest.newBuilder(); ImagesGetUrlBaseResponse.Builder response = ImagesGetUrlBaseResponse.newBuilder(); @@ -316,7 +539,7 @@ public String getServingUrl(ServingUrlOptions options) { try { byte[] responseBytes = ApiProxy.makeSyncCall(PACKAGE, "GetUrlBase", request.build().toByteArray()); - response.mergeFrom(responseBytes); + response.mergeFrom(responseBytes, ExtensionRegistry.getEmptyRegistry()); } catch (InvalidProtocolBufferException ex) { throw new ImagesServiceFailureException("Invalid protocol buffer:", ex); } catch (ApiProxy.ApplicationException ex) { @@ -342,8 +565,11 @@ public String getServingUrl(ServingUrlOptions options) { /** {@inheritDoc} */ @Override public void deleteServingUrl(BlobKey blobKey) { + if (useGrpc()) { + throw new UnsupportedOperationException( + "deleteServingUrl is not supported when using the gRPC Images Service."); + } ImagesDeleteUrlBaseRequest.Builder request = ImagesDeleteUrlBaseRequest.newBuilder(); - ImagesDeleteUrlBaseResponse.Builder response = ImagesDeleteUrlBaseResponse.newBuilder(); if (blobKey == null) { throw new NullPointerException(); } @@ -351,7 +577,7 @@ public void deleteServingUrl(BlobKey blobKey) { try { byte[] responseBytes = ApiProxy.makeSyncCall(PACKAGE, "DeleteUrlBase", request.build().toByteArray()); - response.mergeFrom(responseBytes); + ImagesDeleteUrlBaseResponse unused = ImagesDeleteUrlBaseResponse.parseFrom(responseBytes, ExtensionRegistry.getEmptyRegistry()); } catch (InvalidProtocolBufferException ex) { throw new ImagesServiceFailureException("Invalid protocol buffer:", ex); } catch (ApiProxy.ApplicationException ex) { @@ -364,6 +590,71 @@ public void deleteServingUrl(BlobKey blobKey) { } } + @VisibleForTesting + ImageData loadImageData(Image image, BlobstoreService blobstoreService) { + logger.info("loadImageData image: " + image); + BlobKey blobKey = image.getBlobKey(); + logger.info("blobKey: " + blobKey); + if (blobKey != null) { + String keyString = blobKey.getKeyString(); + logger.info("keyString: " + keyString); + if (keyString.startsWith("/gs/")) { + return fetchGcsImageData(keyString); + } + + // Check if BlobInfo has GCS object name + BlobInfo blobInfo = blobInfoFactory.loadBlobInfo(blobKey); + logger.info( + "blobInfo: " + + blobInfo + + " GsObjectName: " + + (blobInfo != null ? blobInfo.getGsObjectName() : "null")); + if (blobInfo != null && blobInfo.getGsObjectName() != null) { + String gsObjectName = blobInfo.getGsObjectName(); + logger.fine("Found GCS object name for BlobKey: " + gsObjectName); + try { + return fetchGcsImageData(gsObjectName); + } catch (ImagesServiceFailureException e) { + logger.fine( + "Failed to fetch GCS blob data, falling back to BlobstoreInputStream: " + + e.getMessage()); + } + } + logger.fine("No GCS object name found for BlobKey, falling back to BlobstoreInputStream"); + + try (InputStream inputStream = blobstoreReference.openStream(blobKey)) { + return ImageData.newBuilder().setContent(ByteString.readFrom(inputStream)).build(); + } catch (IOException | BlobstoreFailureException e) { + throw new ImagesServiceFailureException("Failed to fetch blob data", e); + } + } + return convertImageData(image); + } + + private ImageData fetchGcsImageData(String keyString) { + try { + return ImageData.newBuilder() + .setContent(ByteString.copyFrom(fetchGcsContent(keyString))) + .setBlobKey(keyString) + .build(); + } catch (RuntimeException e) { + throw new ImagesServiceFailureException("Failed to fetch GCS blob data", e); + } + } + + private RuntimeException convertGrpcException(StatusRuntimeException e) { + Status.Code code = e.getStatus().getCode(); + if (code == Status.Code.UNAUTHENTICATED || code == Status.Code.PERMISSION_DENIED) { + return new ImagesServiceFailureException("Authentication failed for Images Service", e); + } else if (code == Status.Code.RESOURCE_EXHAUSTED) { + return new ImagesServiceFailureException("Image too large", e); + } else if (code == Status.Code.INVALID_ARGUMENT) { + return new IllegalArgumentException(e.getStatus().getDescription(), e); + } else { + return new ImagesServiceFailureException(e.getStatus().getDescription(), e); + } + } + static ImageData convertImageData(Image image) { ImageData.Builder builder = ImageData.newBuilder(); BlobKey blobKey = image.getBlobKey(); @@ -379,20 +670,22 @@ static ImageData convertImageData(Image image) { private ImagesTransformRequest.Builder generateImagesTransformRequest( Transform transform, - Image image, + ImageData imageData, InputSettings inputSettings, OutputSettings outputSettings) { ImagesTransformRequest.Builder request = - ImagesTransformRequest.newBuilder() - .setImage(convertImageData(image)) - .setOutput(convertOutputSettings(outputSettings)) - .setInput(convertInputSettings(inputSettings)); + ImagesTransformRequest.newBuilder() + .setImage(imageData) + .setOutput(convertOutputSettings(outputSettings)) + .setInput(convertInputSettings(inputSettings)); transform.apply(request); if (request.getTransformCount() > MAX_TRANSFORMS_PER_REQUEST) { throw new IllegalArgumentException( - "A maximum of " + MAX_TRANSFORMS_PER_REQUEST + " basic transforms " - + "can be requested in a single transform request"); + "A maximum of " + + MAX_TRANSFORMS_PER_REQUEST + + " basic transforms " + + "can be requested in a single transform request"); } return request; } @@ -414,7 +707,7 @@ private ImagesServicePb.OutputSettings convertOutputSettings(OutputSettings sett pbSettings.setQuality(settings.getQuality()); } } - default -> throw new IllegalArgumentException("Invalid output encoding requested"); + } return pbSettings.build(); } @@ -430,8 +723,7 @@ private ImagesServicePb.InputSettings convertInputSettings(InputSettings setting return pbSettings.build(); } - private RuntimeException convertApplicationException(ImagesTransformRequest.Builder request, - ApiProxy.ApplicationException ex) { + private RuntimeException convertApplicationException(ApiProxy.ApplicationException ex) { ErrorCode errorCode = ErrorCode.forNumber(ex.getApplicationError()); if (errorCode != null && errorCode != ErrorCode.UNSPECIFIED_ERROR) { return new IllegalArgumentException(ex.getErrorDetail()); @@ -439,4 +731,72 @@ private RuntimeException convertApplicationException(ImagesTransformRequest.Buil return new ImagesServiceFailureException(ex.getErrorDetail()); } } + + private BlobstoreService getBlobstoreService() { + return blobstoreService != null + ? blobstoreService + : BlobstoreServiceFactory.getBlobstoreService(); + } + + // We use double-checked locking here to allow lazy initialization and + // mock injection in tests without triggering eager class loading of heavy + // Storage clients which might fail in test environments. + private static class StorageHolder { + private static volatile Storage instance; + + static Storage get() { + if (instance == null) { + synchronized (StorageHolder.class) { + if (instance == null) { + instance = StorageOptions.getDefaultInstance().getService(); + } + } + } + return instance; + } + + @VisibleForTesting + static void setForTesting(Storage storage) { + instance = storage; + } + } + + Storage getStorage() { + return storage != null ? storage : StorageHolder.get(); + } + + @VisibleForTesting + static void setStorageForTesting(Storage storage) { + StorageHolder.setForTesting(storage); + } + + private byte[] fetchGcsContent(String keyString) { + // keyString is like "/gs/bucket/object" + // Remove "/gs/" prefix if present + String path = keyString; + if (path.startsWith("/gs/")) { + path = path.substring(4); + } else if (path.startsWith("/")) { + path = path.substring(1); + } + int index = path.indexOf('/'); + if (index == -1) { + throw new IllegalArgumentException("Invalid GCS key: " + keyString); + } + String bucket = path.substring(0, index); + String object = path.substring(index + 1); + + Blob blob = getStorage().get(BlobId.of(bucket, object)); + if (blob == null) { + throw new ImagesServiceFailureException("GCS blob not found: " + keyString); + } + if (blob.getSize() > MAX_BLOB_SIZE) { + throw new ImagesServiceFailureException( + "GCS blob is too large: " + blob.getSize() + " bytes"); + } + byte[] content = blob.getContent(); + logger.info( + "Successfully fetched GCS blob content for key: " + keyString + " size: " + content.length); + return content; + } } diff --git a/api/src/test/java/com/google/appengine/api/images/GrpcImagesClientTest.java b/api/src/test/java/com/google/appengine/api/images/GrpcImagesClientTest.java new file mode 100644 index 000000000..12be765bf --- /dev/null +++ b/api/src/test/java/com/google/appengine/api/images/GrpcImagesClientTest.java @@ -0,0 +1,94 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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 com.google.appengine.api.images; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.when; + +import com.google.appengine.api.EnvironmentProvider; +import io.grpc.CallCredentials; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@RunWith(JUnit4.class) +public class GrpcImagesClientTest { + + @Rule public final MockitoRule mocks = MockitoJUnit.rule(); + + @Mock private EnvironmentProvider mockEnvironmentProvider; + @Mock private CallCredentials mockCallCredentials; + + @Test + public void constructor_validEndpointAndCreds_success() { + when(mockEnvironmentProvider.getenv(ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV)) + .thenReturn("https://my-service.run.app"); + + GrpcImagesClient client = new GrpcImagesClient(mockEnvironmentProvider, mockCallCredentials); + assertThat(client).isNotNull(); + } + + @Test + public void constructor_endpointNotSet_throwsException() { + when(mockEnvironmentProvider.getenv(ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV)) + .thenReturn(null); + IllegalStateException e = + assertThrows( + IllegalStateException.class, + () -> new GrpcImagesClient(mockEnvironmentProvider, mockCallCredentials)); + assertThat(e) + .hasMessageThat() + .contains( + ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV + " environment variable not set"); + } + + @Test + public void constructor_invalidEndpoint_throwsException() { + when(mockEnvironmentProvider.getenv(ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV)) + .thenReturn("://my-service"); + IllegalStateException e = + assertThrows( + IllegalStateException.class, + () -> new GrpcImagesClient(mockEnvironmentProvider, mockCallCredentials)); + assertThat(e) + .hasMessageThat() + .contains("Invalid URI in " + ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV); + } + + @Test + public void constructor_endpointMissingHost_throwsException() { + when(mockEnvironmentProvider.getenv(ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV)) + .thenReturn("https://"); + IllegalStateException e = + assertThrows( + IllegalStateException.class, + () -> new GrpcImagesClient(mockEnvironmentProvider, mockCallCredentials)); + assertThat(e) + .hasMessageThat() + .contains("Invalid URI in " + ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV); + } + + @Test + public void getBlockingStub_returnsStub() { + when(mockEnvironmentProvider.getenv(ImagesServiceFactoryImpl.IMAGES_SERVICE_ENDPOINT_ENV)) + .thenReturn("https://my-service.run.app"); + GrpcImagesClient client = new GrpcImagesClient(mockEnvironmentProvider, mockCallCredentials); + assertThat(client.getBlockingStub()).isNotNull(); + } +} diff --git a/api/src/test/java/com/google/appengine/api/images/ImagesServiceFactoryImplTest.java b/api/src/test/java/com/google/appengine/api/images/ImagesServiceFactoryImplTest.java new file mode 100644 index 000000000..0ae60314c --- /dev/null +++ b/api/src/test/java/com/google/appengine/api/images/ImagesServiceFactoryImplTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2021 Google LLC + * + * 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 + * + * https://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 com.google.appengine.api.images; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.appengine.api.EnvironmentProvider; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ImagesServiceFactoryImplTest { + + private ImagesServiceFactoryImpl factory; + private EnvironmentProvider mockEnvironmentProvider; + + @Before + public void setUp() { + factory = new ImagesServiceFactoryImpl(); + mockEnvironmentProvider = mock(EnvironmentProvider.class); + factory.setEnvironmentProvider(mockEnvironmentProvider); + } + + @Test + public void makeImageFromFilename_newBehavior_trueEnv_gsPrefix() { + when(mockEnvironmentProvider.getenv( + ImagesServiceFactoryImpl.USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV)) + .thenReturn("true"); + String filename = "/gs/bucket/object"; + + // Should NOT call BlobstoreServiceFactory (which would fail in this env) + Image image = factory.makeImageFromFilename(filename); + + assertThat(image.getBlobKey()).isNotNull(); + assertThat(image.getBlobKey().getKeyString()).isEqualTo(filename); + } + + @Test + public void makeImageFromFilename_newBehavior_trueEnv_noGsPrefix_throwsException() { + when(mockEnvironmentProvider.getenv( + ImagesServiceFactoryImpl.USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV)) + .thenReturn("true"); + String filename = "not/gs/path"; + + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> factory.makeImageFromFilename(filename)); + assertThat(e).hasMessageThat().contains("must be prefixed with /gs/"); + } +} diff --git a/api/src/test/java/com/google/appengine/api/images/ImagesServiceImplTest.java b/api/src/test/java/com/google/appengine/api/images/ImagesServiceImplTest.java new file mode 100644 index 000000000..27bb5bc8b --- /dev/null +++ b/api/src/test/java/com/google/appengine/api/images/ImagesServiceImplTest.java @@ -0,0 +1,524 @@ +// Copyright 2024 Google LLC +// +// 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 +// +// https://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 com.google.appengine.api.images; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import java.util.concurrent.ExecutionException; + +import com.google.appengine.api.EnvironmentProvider; +import com.google.appengine.api.blobstore.BlobInfo; +import com.google.appengine.api.blobstore.BlobInfoFactory; +import com.google.appengine.api.blobstore.BlobKey; +import com.google.appengine.api.blobstore.BlobstoreFailureException; +import com.google.appengine.api.blobstore.BlobstoreService; +import com.google.appengine.api.images.ImagesService.OutputEncoding; +import com.google.appengine.api.images.ImagesServicePb.ImageData; +import com.google.appengine.api.images.ImagesServicePb.ImagesHistogram; +import com.google.appengine.api.images.ImagesServicePb.ImagesHistogramRequest; +import com.google.appengine.api.images.ImagesServicePb.ImagesHistogramResponse; +import com.google.appengine.api.images.ImagesServicePb.ImagesTransformRequest; +import com.google.appengine.api.images.ImagesServicePb.ImagesTransformResponse; +import com.google.appengine.api.images.proto.ImagesServiceGrpc; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.Storage; +import com.google.protobuf.ByteString; +import io.grpc.ManagedChannel; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.StreamObserver; +import io.grpc.testing.GrpcCleanupRule; +import java.io.ByteArrayInputStream; +import static java.nio.charset.StandardCharsets.UTF_8; +import java.time.Instant; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.Future; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@RunWith(JUnit4.class) +public class ImagesServiceImplTest { + + @Rule public final MockitoRule mocks = MockitoJUnit.rule(); + @Rule public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); + + @Mock private EnvironmentProvider mockEnvironmentProvider; + @Mock private GrpcImagesClient mockGrpcImagesClient; + @Mock private BlobstoreService mockBlobstoreService; + @Mock private BlobInfoFactory mockBlobInfoFactory; + @Mock private ImagesServiceImpl.BlobstoreReference mockBlobstoreReference; + @Mock private Storage mockStorage; + @Mock private Blob mockBlob; + + private ImagesServiceImpl imagesService; + private ManagedChannel channel; + private ImagesServiceGrpc.ImagesServiceBlockingStub blockingStub; + private ImagesServiceGrpc.ImagesServiceFutureStub futureStub; + + // Mock implementation of the ImagesService. + private final ImagesServiceGrpc.ImagesServiceImplBase serviceImpl = + new ImagesServiceGrpc.ImagesServiceImplBase() { + @Override + public void transform( + ImagesTransformRequest request, + StreamObserver responseObserver) { + // For now, just return a default response + ImagesTransformResponse response = + ImagesTransformResponse.newBuilder() + .setImage( + ImageData.newBuilder().setContent(ByteString.copyFromUtf8("transformed"))) + .build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + + @Override + public void composite( + ImagesServicePb.ImagesCompositeRequest request, + StreamObserver responseObserver) { + ImagesServicePb.ImagesCompositeResponse response = + ImagesServicePb.ImagesCompositeResponse.newBuilder() + .setImage( + ImageData.newBuilder().setContent(ByteString.copyFromUtf8("composited"))) + .build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + + @Override + public void histogram( + ImagesHistogramRequest request, + StreamObserver responseObserver) { + + // Fill the rest with 0s to match 256 size if needed, but the loop in impl handles it. + // Actually, proto repeated fields are just lists. The array copy loop assumes 256 size or + // less? + // "for (int i = 0; i < 256; i++) { result[0][i] = histogram.getRed(i); }" + // If the list is shorter than 256, getRed(i) might throw IndexOutOfBoundsException or + // return default? + // Protobuf list access: getRedList().get(i). + // Let's populate 256 values to be safe and correct. + ImagesHistogram.Builder histogramBuilder = ImagesHistogram.newBuilder(); + for (int i = 0; i < 256; i++) { + histogramBuilder.addRed(i).addGreen(i).addBlue(i); + } + + ImagesHistogramResponse response = + ImagesHistogramResponse.newBuilder().setHistogram(histogramBuilder).build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + }; + + @Test + public void histogram_grpcPath_success() throws Exception { + setUpGrpc(true); + Image image = ImagesServiceFactory.makeImage(new byte[] {1, 2, 3}); + + int[][] result = imagesService.histogram(image); + + assertThat(result).hasLength(3); + assertThat(result[0]).hasLength(256); + assertThat(result[1]).hasLength(256); + assertThat(result[2]).hasLength(256); + + // Check a few values based on mock + assertThat(result[0][0]).isEqualTo(0); + assertThat(result[0][255]).isEqualTo(255); + assertThat(result[1][100]).isEqualTo(100); + assertThat(result[2][50]).isEqualTo(50); + } + + // Re-write to allow dynamic behavior of mock service + private void setupGrpcService(ImagesServiceGrpc.ImagesServiceImplBase serviceImplementation) + throws Exception { + String serverName = InProcessServerBuilder.generateName(); + grpcCleanup.register( + InProcessServerBuilder.forName(serverName) + .directExecutor() + .addService(serviceImplementation) + .build() + .start()); + channel = + grpcCleanup.register(InProcessChannelBuilder.forName(serverName).directExecutor().build()); + blockingStub = ImagesServiceGrpc.newBlockingStub(channel); + futureStub = ImagesServiceGrpc.newFutureStub(channel); + when(mockGrpcImagesClient.getBlockingStub()).thenReturn(blockingStub); + when(mockGrpcImagesClient.getFutureStub()).thenReturn(futureStub); + imagesService = + new ImagesServiceImpl( + mockEnvironmentProvider, mockGrpcImagesClient, null, mockBlobstoreReference); + + when(mockEnvironmentProvider.getenv( + ImagesServiceFactoryImpl.USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV)) + .thenReturn("true"); + } + + @Test + public void applyTransformAsync_grpcPath_authError_test() throws Exception { + ImagesServiceGrpc.ImagesServiceImplBase errorService = + new ImagesServiceGrpc.ImagesServiceImplBase() { + @Override + public void transform( + ImagesTransformRequest request, + StreamObserver responseObserver) { + responseObserver.onError(new StatusRuntimeException(Status.UNAUTHENTICATED)); + } + }; + setupGrpcService(errorService); + + Image originalImage = ImagesServiceFactory.makeImage(new byte[] {1, 2, 3}); + Transform transform = new Crop(0, 0, 1, 1); + OutputSettings settings = new OutputSettings(OutputEncoding.PNG); + + ExecutionException e = + assertThrows( + ExecutionException.class, + () -> imagesService.applyTransformAsync(transform, originalImage, settings).get()); + assertThat(e).hasCauseThat().isInstanceOf(ImagesServiceFailureException.class); + assertThat(e).hasCauseThat().hasMessageThat().contains("Authentication failed"); + } + + @Test + public void applyTransformAsync_grpcPath_tooLargeError_test() throws Exception { + ImagesServiceGrpc.ImagesServiceImplBase errorService = + new ImagesServiceGrpc.ImagesServiceImplBase() { + @Override + public void transform( + ImagesTransformRequest request, + StreamObserver responseObserver) { + responseObserver.onError(new StatusRuntimeException(Status.RESOURCE_EXHAUSTED)); + } + }; + setupGrpcService(errorService); + + Image originalImage = ImagesServiceFactory.makeImage(new byte[] {1, 2, 3}); + Transform transform = new Crop(0, 0, 1, 1); + OutputSettings settings = new OutputSettings(OutputEncoding.PNG); + + ExecutionException e = + assertThrows( + ExecutionException.class, + () -> imagesService.applyTransformAsync(transform, originalImage, settings).get()); + assertThat(e).hasCauseThat().isInstanceOf(ImagesServiceFailureException.class); + assertThat(e).hasCauseThat().hasMessageThat().contains("Image too large"); + } + + @Test + public void applyTransformAsync_grpcPath_invalidArgument_test() throws Exception { + ImagesServiceGrpc.ImagesServiceImplBase errorService = + new ImagesServiceGrpc.ImagesServiceImplBase() { + @Override + public void transform( + ImagesTransformRequest request, + StreamObserver responseObserver) { + responseObserver.onError( + new StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Bad arg"))); + } + }; + setupGrpcService(errorService); + + Image originalImage = ImagesServiceFactory.makeImage(new byte[] {1, 2, 3}); + Transform transform = new Crop(0, 0, 1, 1); + OutputSettings settings = new OutputSettings(OutputEncoding.PNG); + + ExecutionException e = + assertThrows( + ExecutionException.class, + () -> imagesService.applyTransformAsync(transform, originalImage, settings).get()); + assertThat(e).hasCauseThat().isInstanceOf(IllegalArgumentException.class); + assertThat(e).hasCauseThat().hasMessageThat().contains("Bad arg"); + } + + @Test + public void composite_grpcPath_success() throws Exception { + setUpGrpc(true); + Image image1 = ImagesServiceFactory.makeImage(new byte[] {1}); + Image image2 = ImagesServiceFactory.makeImage(new byte[] {2}); + Composite composite1 = + ImagesServiceFactory.makeComposite(image1, 0, 0, 1.0f, Composite.Anchor.TOP_LEFT); + Composite composite2 = + ImagesServiceFactory.makeComposite(image2, 10, 10, 0.5f, Composite.Anchor.CENTER_CENTER); + List composites = Arrays.asList(composite1, composite2); + OutputSettings settings = new OutputSettings(OutputEncoding.PNG); + + Image result = imagesService.composite(composites, 100, 100, 0L, settings); + + assertThat(result).isNotNull(); + assertThat(result.getImageData()) + .isEqualTo(ByteString.copyFromUtf8("composited").toByteArray()); + } + + @Test + public void loadImageData_withImageContent() throws Exception { + setUpGrpc(false); + byte[] content = new byte[] {1, 2, 3}; + Image image = ImagesServiceFactory.makeImage(content); + + ImageData imageData = imagesService.loadImageData(image, mockBlobstoreService); + + assertThat(imageData.hasBlobKey()).isFalse(); + assertThat(imageData.getContent().toByteArray()).isEqualTo(content); + } + + @Test + public void loadImageData_withGsBlobKey() throws Exception { + setUpGrpc(false); + String gsKey = "/gs/bucket/object"; + BlobKey blobKey = new BlobKey(gsKey); + Image image = ImagesServiceFactory.makeImageFromBlob(blobKey); + byte[] content = new byte[] {1, 2, 3}; + + when(mockStorage.get(BlobId.of("bucket", "object"))).thenReturn(mockBlob); + when(mockBlob.getContent()).thenReturn(content); + + ImageData imageData = imagesService.loadImageData(image, mockBlobstoreService); + + assertThat(imageData.getBlobKey()).isEqualTo(gsKey); + assertThat(imageData.getContent().toByteArray()).isEqualTo(content); + } + + @Test + public void loadImageData_withBlobInfoFallback_success() throws Exception { + setUpGrpc(false); + BlobKey blobKey = new BlobKey("some_opaque_key"); + Image image = ImagesServiceFactory.makeImageFromBlob(blobKey); + BlobInfo blobInfo = + new BlobInfo( + blobKey, "type", Date.from(Instant.now()), "file", 0, "hash", "/gs/bucket/object"); + when(mockBlobInfoFactory.loadBlobInfo(blobKey)).thenReturn(blobInfo); + when(mockStorage.get(BlobId.of("bucket", "object"))).thenReturn(mockBlob); + when(mockBlob.getContent()).thenReturn("blobContent".getBytes(UTF_8)); + ImageData imageData = imagesService.loadImageData(image, mockBlobstoreService); + assertThat(imageData.getBlobKey()).isEqualTo("/gs/bucket/object"); + assertThat(imageData.getContent().isEmpty()).isFalse(); + assertThat(imageData.getContent().toStringUtf8()).isEqualTo("blobContent"); + } + + @Test + public void loadImageData_withLegacyBlobKey_success() throws Exception { + setUpGrpc(false); + + BlobKey blobKey = new BlobKey("legacy-blob-key"); + byte[] fetchedData = new byte[] {4, 5, 6}; + + when(mockBlobstoreReference.openStream(eq(blobKey))) + .thenReturn(new ByteArrayInputStream(fetchedData)); + + Image image = ImagesServiceFactory.makeImageFromBlob(blobKey); + + ImageData imageData = imagesService.loadImageData(image, mockBlobstoreService); + + assertThat(imageData.hasBlobKey()).isFalse(); + assertThat(imageData.getContent().toByteArray()).isEqualTo(fetchedData); + } + + @Test + public void loadImageData_withLegacyBlobKey_failure() throws Exception { + setUpGrpc(false); + String legacyKey = "legacy-blob-key"; + BlobKey blobKey = new BlobKey(legacyKey); + Image image = ImagesServiceFactory.makeImageFromBlob(blobKey); + + when(mockBlobstoreReference.openStream(eq(blobKey))) + .thenThrow(new BlobstoreFailureException("Fetch failed")); + + ImagesServiceFailureException e = + assertThrows( + ImagesServiceFailureException.class, + () -> imagesService.loadImageData(image, mockBlobstoreService)); + assertThat(e).hasMessageThat().contains("Failed to fetch blob data"); + assertThat(e).hasCauseThat().isInstanceOf(BlobstoreFailureException.class); + } + + public void setUpGrpc(boolean useGrpc) throws Exception { + ImagesServiceImpl.setStorageForTesting(mockStorage); + when(mockEnvironmentProvider.getenv( + ImagesServiceFactoryImpl.USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV)) + .thenReturn(Boolean.toString(useGrpc)); + + if (useGrpc) { + String serverName = InProcessServerBuilder.generateName(); + grpcCleanup.register( + InProcessServerBuilder.forName(serverName) + .directExecutor() + .addService(serviceImpl) + .build() + .start()); + channel = + grpcCleanup.register( + InProcessChannelBuilder.forName(serverName).directExecutor().build()); + blockingStub = ImagesServiceGrpc.newBlockingStub(channel); + futureStub = ImagesServiceGrpc.newFutureStub(channel); + when(mockGrpcImagesClient.getBlockingStub()).thenReturn(blockingStub); + when(mockGrpcImagesClient.getFutureStub()).thenReturn(futureStub); + + imagesService = + new ImagesServiceImpl( + mockEnvironmentProvider, + mockGrpcImagesClient, + mockBlobstoreService, + mockBlobstoreReference, + mockStorage, + mockBlobInfoFactory); + } else { + imagesService = + new ImagesServiceImpl( + mockEnvironmentProvider, + null, + null, + mockBlobstoreReference, + mockStorage, + mockBlobInfoFactory); + } + } + + @Test + public void useGrpc_envVarSetTrue_returnsTrue() throws Exception { + setUpGrpc(true); + assertThat(imagesService.useGrpc()).isTrue(); + } + + @Test + public void useGrpc_envVarSetFalse_returnsFalse() throws Exception { + setUpGrpc(false); + assertThat(imagesService.useGrpc()).isFalse(); + } + + @Test + public void useGrpc_envVarNotSet_returnsFalse() { + when(mockEnvironmentProvider.getenv( + ImagesServiceFactoryImpl.USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV)) + .thenReturn(null); + imagesService = + new ImagesServiceImpl( + mockEnvironmentProvider, null, null, mockBlobstoreReference, null, mockBlobInfoFactory); + assertThat(imagesService.useGrpc()).isFalse(); + } + + @Test + public void useGrpc_envVarInvalid_returnsFalse() { + when(mockEnvironmentProvider.getenv( + ImagesServiceFactoryImpl.USE_CUSTOM_IMAGES_GRPC_SERVICE_ENV)) + .thenReturn("yes"); + imagesService = + new ImagesServiceImpl( + mockEnvironmentProvider, null, null, mockBlobstoreReference, null, mockBlobInfoFactory); + assertThat(imagesService.useGrpc()).isFalse(); + } + + @Test + public void applyTransformAsync_grpcPath_success() throws Exception { + setUpGrpc(true); + Image originalImage = ImagesServiceFactory.makeImage(new byte[] {1, 2, 3}); + Transform transform = new Crop(0, 0, 1, 1); + OutputSettings settings = new OutputSettings(OutputEncoding.PNG); + + Future future = imagesService.applyTransformAsync(transform, originalImage, settings); + Image transformedImage = future.get(); + + assertThat(transformedImage).isNotNull(); + assertThat(transformedImage.getImageData()) + .isEqualTo(ByteString.copyFromUtf8("transformed").toByteArray()); + } + + @Test + public void applyTransformAsync_grpcPath_withLegacyBlobKey_success() throws Exception { + setUpGrpc(true); + String legacyKey = "legacy-blob-key"; + BlobKey blobKey = new BlobKey(legacyKey); + Image originalImage = ImagesServiceFactory.makeImageFromBlob(blobKey); + Transform transform = new Crop(0, 0, 1, 1); + OutputSettings settings = new OutputSettings(OutputEncoding.PNG); + byte[] fetchedData = new byte[] {4, 5, 6}; + + when(mockBlobstoreReference.openStream(eq(blobKey))) + .thenReturn(new ByteArrayInputStream(fetchedData)); + + Future future = imagesService.applyTransformAsync(transform, originalImage, settings); + Image transformedImage = future.get(); + + assertThat(transformedImage).isNotNull(); + assertThat(transformedImage.getImageData()) + .isEqualTo(ByteString.copyFromUtf8("transformed").toByteArray()); + } + + @Test + // Suppressing deprecation warnings as we are testing deprecated methods or options, + // and UnnecessaryJavacSuppressWarnings to avoid warnings in different compiler versions. + @SuppressWarnings({"deprecation", "UnnecessaryJavacSuppressWarnings"}) + public void getServingUrl_grpcPath_throwsUnsupportedOperationException() throws Exception { + setUpGrpc(true); + // Use a valid blob key format to avoid other validations if they were to run (though they + // shouldn't) + String gsKey = "/gs/bucket/object"; + BlobKey blobKey = new BlobKey(gsKey); + + // Test getServingUrl(BlobKey) + UnsupportedOperationException e1 = + assertThrows( + UnsupportedOperationException.class, () -> imagesService.getServingUrl(blobKey)); + assertThat(e1).hasMessageThat().contains("getServingUrl is not supported"); + + // Test getServingUrl(BlobKey, boolean) + UnsupportedOperationException e2 = + assertThrows( + UnsupportedOperationException.class, () -> imagesService.getServingUrl(blobKey, true)); + assertThat(e2).hasMessageThat().contains("getServingUrl is not supported"); + + // Test getServingUrl(BlobKey, int, boolean) + UnsupportedOperationException e3 = + assertThrows( + UnsupportedOperationException.class, + () -> imagesService.getServingUrl(blobKey, 32, true)); + assertThat(e3).hasMessageThat().contains("getServingUrl is not supported"); + + // Test getServingUrl(BlobKey, int, boolean, boolean) + UnsupportedOperationException e4 = + assertThrows( + UnsupportedOperationException.class, + () -> imagesService.getServingUrl(blobKey, 32, true, true)); + assertThat(e4).hasMessageThat().contains("getServingUrl is not supported"); + + // Test getServingUrl(ServingUrlOptions) + ServingUrlOptions options = ServingUrlOptions.Builder.withBlobKey(blobKey); + UnsupportedOperationException e5 = + assertThrows( + UnsupportedOperationException.class, () -> imagesService.getServingUrl(options)); + assertThat(e5).hasMessageThat().contains("getServingUrl is not supported"); + } + + @Test + public void deleteServingUrl_grpcPath_throwsUnsupportedOperationException() throws Exception { + setUpGrpc(true); + BlobKey blobKey = new BlobKey("blob-key"); + + UnsupportedOperationException e = + assertThrows( + UnsupportedOperationException.class, () -> imagesService.deleteServingUrl(blobKey)); + assertThat(e).hasMessageThat().contains("deleteServingUrl is not supported"); + } +} diff --git a/api_dev/pom.xml b/api_dev/pom.xml index 6194e10d5..a8bdb6ee3 100644 --- a/api_dev/pom.xml +++ b/api_dev/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/api_dev/src/test/java/com/google/appengine/api/images/CompositeImplTest.java b/api_dev/src/test/java/com/google/appengine/api/images/CompositeImplTest.java index 917706152..3c36eb7e7 100644 --- a/api_dev/src/test/java/com/google/appengine/api/images/CompositeImplTest.java +++ b/api_dev/src/test/java/com/google/appengine/api/images/CompositeImplTest.java @@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import com.google.appengine.api.images.ImagesServicePb.ImageData; +import com.google.protobuf.ByteString; import java.util.HashMap; import java.util.Map; import org.junit.Before; @@ -58,7 +60,10 @@ public void testApply_singleImage() throws Exception { float opacity = 0.8f; Composite.Anchor anchor = Composite.Anchor.CENTER_LEFT; Composite composite = new CompositeImpl(image, xOffset, yOffset, opacity, anchor); - composite.apply(request, imageIndexMap); + composite.apply( + request, + imageIndexMap, + img -> ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); assertThat(request.getOptionsCount()).isEqualTo(1); assertThat(request.getOptions(0).getXOffset()).isEqualTo(xOffset); assertThat(request.getOptions(0).getYOffset()).isEqualTo(yOffset); @@ -81,9 +86,15 @@ public void testApply_singleImageMultipleOptions() throws Exception { float otherOpacity = 0.1f; Composite.Anchor otherAnchor = Composite.Anchor.BOTTOM_RIGHT; Composite composite = new CompositeImpl(image, xOffset, yOffset, opacity, anchor); - composite.apply(request, imageIndexMap); + composite.apply( + request, + imageIndexMap, + img -> ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); composite = new CompositeImpl(image, otherXOffset, otherYOffset, otherOpacity, otherAnchor); - composite.apply(request, imageIndexMap); + composite.apply( + request, + imageIndexMap, + img -> ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); assertThat(request.getOptionsCount()).isEqualTo(2); assertThat(request.getOptions(0).getXOffset()).isEqualTo(xOffset); assertThat(request.getOptions(0).getYOffset()).isEqualTo(yOffset); @@ -116,10 +127,16 @@ public void testApply_identicalImagesMultipleOptions() throws Exception { float otherOpacity = 0.3f; Composite.Anchor otherAnchor = Composite.Anchor.BOTTOM_RIGHT; Composite composite = new CompositeImpl(image, xOffset, yOffset, opacity, anchor); - composite.apply(request, imageIndexMap); + composite.apply( + request, + imageIndexMap, + img -> ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); composite = new CompositeImpl(otherImage, otherXOffset, otherYOffset, otherOpacity, otherAnchor); - composite.apply(request, imageIndexMap); + composite.apply( + request, + imageIndexMap, + img -> ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); assertThat(request.getOptionsCount()).isEqualTo(2); assertThat(request.getOptions(0).getXOffset()).isEqualTo(xOffset); assertThat(request.getOptions(0).getYOffset()).isEqualTo(yOffset); @@ -152,10 +169,16 @@ public void testApply_multipleImagesSingleOptions() throws Exception { float otherOpacity = 0.1111f; Composite.Anchor otherAnchor = Composite.Anchor.BOTTOM_RIGHT; Composite composite = new CompositeImpl(image, xOffset, yOffset, opacity, anchor); - composite.apply(request, imageIndexMap); + composite.apply( + request, + imageIndexMap, + img -> ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); composite = new CompositeImpl(otherImage, otherXOffset, otherYOffset, otherOpacity, otherAnchor); - composite.apply(request, imageIndexMap); + composite.apply( + request, + imageIndexMap, + img -> ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); assertThat(request.getOptionsCount()).isEqualTo(2); assertThat(request.getOptions(0).getXOffset()).isEqualTo(xOffset); assertThat(request.getOptions(0).getYOffset()).isEqualTo(yOffset); @@ -200,21 +223,41 @@ public void testApply_multipleImagesLotsOfOptions() throws Exception { float opacity4 = 0.9f; Composite.Anchor anchor4 = Composite.Anchor.TOP_CENTER; new CompositeImpl(new ImageImpl(imageData), xOffset, yOffset, opacity, anchor) - .apply(request, imageIndexMap); + .apply( + request, + imageIndexMap, + img -> + ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); new CompositeImpl( new ImageImpl(otherImageData), otherXOffset, otherYOffset, otherOpacity, otherAnchor) - .apply(request, imageIndexMap); + .apply( + request, + imageIndexMap, + img -> + ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); new CompositeImpl( new ImageImpl(imageData), yetAnotherXOffset, yetAnotherYOffset, yetAnotherOpacity, yetAnotherAnchor) - .apply(request, imageIndexMap); + .apply( + request, + imageIndexMap, + img -> + ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); new CompositeImpl(new ImageImpl(otherImageData), xOffset3, yOffset3, opacity3, anchor3) - .apply(request, imageIndexMap); + .apply( + request, + imageIndexMap, + img -> + ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); new CompositeImpl(new ImageImpl(otherImageData), xOffset4, yOffset4, opacity4, anchor4) - .apply(request, imageIndexMap); + .apply( + request, + imageIndexMap, + img -> + ImageData.newBuilder().setContent(ByteString.copyFrom(img.getImageData())).build()); assertThat(request.getOptionsCount()).isEqualTo(5); assertThat(request.getOptions(0).getXOffset()).isEqualTo(xOffset); assertThat(request.getOptions(0).getYOffset()).isEqualTo(yOffset); diff --git a/api_dev/src/test/java/com/google/appengine/api/images/ImagesServiceImplTest.java b/api_dev/src/test/java/com/google/appengine/api/images/ImagesServiceImplTest.java index 7dac4425a..dbb6107b7 100644 --- a/api_dev/src/test/java/com/google/appengine/api/images/ImagesServiceImplTest.java +++ b/api_dev/src/test/java/com/google/appengine/api/images/ImagesServiceImplTest.java @@ -61,6 +61,7 @@ import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.function.Function; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -698,8 +699,10 @@ static class FakeComposite extends Composite { @Override void apply( - ImagesServicePb.ImagesCompositeRequest.Builder request, Map imageIndexMap) { - delegate.apply(request, imageIndexMap); + ImagesServicePb.ImagesCompositeRequest.Builder request, + Map imageIndexMap, + Function imageDataConverter) { + delegate.apply(request, imageIndexMap, imageDataConverter); applications++; } } diff --git a/api_legacy/pom.xml b/api_legacy/pom.xml index 4b7991a3e..2aaebbf14 100644 --- a/api_legacy/pom.xml +++ b/api_legacy/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/appengine-api-1.0-sdk/pom.xml b/appengine-api-1.0-sdk/pom.xml index b1a41a361..09ad1b6e0 100644 --- a/appengine-api-1.0-sdk/pom.xml +++ b/appengine-api-1.0-sdk/pom.xml @@ -20,7 +20,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar AppEngine :: appengine-api-1.0-sdk @@ -75,6 +75,10 @@ com.google.api.services.datastore com.google.appengine.repackaged.com.google.api.services.datastore + + com.google.api.services.storage + com.google.appengine.repackaged.com.google.api.services.storage + com.google.datastore.v1 @@ -104,6 +108,10 @@ com.google.appengine.api.search.proto.SearchServicePb com.google.appengine.repackaged.com.google.appengine.api.search.proto.SearchServicePb + + com.google.auth + com.google.appengine.repackaged.com.google.auth + com.google.borg.borgcron com.google.appengine.repackaged.com.google.cron @@ -272,6 +280,34 @@ io.grpc com.google.appengine.repackaged.io.grpc + + com.google.cloud + com.google.appengine.repackaged.com.google.cloud + + + com.google.api.gax + com.google.appengine.repackaged.com.google.api.gax + + + com.google.api.core + com.google.appengine.repackaged.com.google.api.core + + + com.google.api.pathtemplate + com.google.appengine.repackaged.com.google.api.pathtemplate + + + com.google.api.resourcenames + com.google.appengine.repackaged.com.google.api.resourcenames + + + org.threeten.bp + com.google.appengine.repackaged.org.threeten.bp + + + io.opentelemetry + com.google.appengine.repackaged.io.opentelemetry + @@ -292,6 +328,7 @@ com/google/appengine/api/blobstore/jakarta/* com/google/appengine/api/capabilities/* com/google/appengine/api/images/* + com/google/appengine/api/images/proto/* com/google/appengine/api/internal/* com/google/appengine/api/log/* com/google/appengine/api/mail/* @@ -470,6 +507,8 @@ com.google.http-client:google-http-client-jackson:* com.google.http-client:google-http-client-protobuf:* com.google.http-client:google-http-client:* + com.google.http-client:google-http-client-appengine:* + com.google.apis:google-api-services-storage:* com.google.oauth-client:google-oauth-client:* com.google.protobuf:protobuf-java:* commons-codec:commons-codec:* @@ -483,8 +522,31 @@ io.opencensus:opencensus-contrib-http-util:* io.grpc:grpc-api:* + io.grpc:grpc-auth:* + io.grpc:grpc-stub:* + io.grpc:grpc-protobuf:* + io.grpc:grpc-protobuf-lite:* + io.grpc:grpc-core:* + io.grpc:grpc-netty-shaded:* + io.grpc:grpc-util:* + io.grpc:grpc-context:* + com.google.auth:google-auth-library-oauth2-http:* + com.google.auth:google-auth-library-credentials:* + com.google.cloud:google-cloud-storage:* + com.google.cloud:google-cloud-core:* + com.google.cloud:google-cloud-core-http:* + com.google.api:gax:* + com.google.api:gax-httpjson:* + com.google.api:api-common:* + org.threeten:threetenbp:* + io.opentelemetry:opentelemetry-api:* + io.opentelemetry:opentelemetry-context:* + + + + diff --git a/appengine-api-stubs/pom.xml b/appengine-api-stubs/pom.xml index 3ef7c9ef1..30e450889 100644 --- a/appengine-api-stubs/pom.xml +++ b/appengine-api-stubs/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/appengine_init/pom.xml b/appengine_init/pom.xml index 3d103aa8a..faa701f56 100644 --- a/appengine_init/pom.xml +++ b/appengine_init/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/appengine_jsr107/pom.xml b/appengine_jsr107/pom.xml index 02a08add2..faa2cb851 100644 --- a/appengine_jsr107/pom.xml +++ b/appengine_jsr107/pom.xml @@ -26,7 +26,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT diff --git a/appengine_resources/pom.xml b/appengine_resources/pom.xml index 155d26b1a..c4750b500 100644 --- a/appengine_resources/pom.xml +++ b/appengine_resources/pom.xml @@ -21,7 +21,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar AppEngine :: appengine-resources diff --git a/appengine_testing/pom.xml b/appengine_testing/pom.xml index 347315d87..6fee41001 100644 --- a/appengine_testing/pom.xml +++ b/appengine_testing/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/appengine_testing_tests/pom.xml b/appengine_testing_tests/pom.xml index 172f99c1c..99556f251 100644 --- a/appengine_testing_tests/pom.xml +++ b/appengine_testing_tests/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/applications/guestbook/pom.xml b/applications/guestbook/pom.xml index d51fc6a72..3929e6ab6 100644 --- a/applications/guestbook/pom.xml +++ b/applications/guestbook/pom.xml @@ -24,7 +24,7 @@ com.google.appengine applications - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos guestbook diff --git a/applications/guestbook_jakarta/pom.xml b/applications/guestbook_jakarta/pom.xml index 2b703d47c..9b900929b 100644 --- a/applications/guestbook_jakarta/pom.xml +++ b/applications/guestbook_jakarta/pom.xml @@ -24,7 +24,7 @@ com.google.appengine applications - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos guestbook_jakarta diff --git a/applications/jaxrs/pom.xml b/applications/jaxrs/pom.xml index 8cbe50262..98ce4b41f 100644 --- a/applications/jaxrs/pom.xml +++ b/applications/jaxrs/pom.xml @@ -24,7 +24,7 @@ com.google.appengine applications - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos jaxrs diff --git a/applications/pom.xml b/applications/pom.xml index 55a7e2f56..04abe4b79 100644 --- a/applications/pom.xml +++ b/applications/pom.xml @@ -24,7 +24,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT pom diff --git a/applications/proberapp/pom.xml b/applications/proberapp/pom.xml index d8ed33fb4..92fcd5855 100644 --- a/applications/proberapp/pom.xml +++ b/applications/proberapp/pom.xml @@ -29,7 +29,7 @@ com.google.appengine applications - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT diff --git a/applications/proberapp_jakarta/pom.xml b/applications/proberapp_jakarta/pom.xml index ee693bbc9..bbd281194 100644 --- a/applications/proberapp_jakarta/pom.xml +++ b/applications/proberapp_jakarta/pom.xml @@ -29,7 +29,7 @@ com.google.appengine applications - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT diff --git a/applications/servletasyncapp/pom.xml b/applications/servletasyncapp/pom.xml index ebd5c0ee0..7b68e2771 100644 --- a/applications/servletasyncapp/pom.xml +++ b/applications/servletasyncapp/pom.xml @@ -24,7 +24,7 @@ com.google.appengine applications - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos servletasyncapp diff --git a/applications/servletasyncappjakarta/pom.xml b/applications/servletasyncappjakarta/pom.xml index f286d15b7..4afcba6d5 100644 --- a/applications/servletasyncappjakarta/pom.xml +++ b/applications/servletasyncappjakarta/pom.xml @@ -24,7 +24,7 @@ com.google.appengine applications - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos servletasyncappjakarta diff --git a/applications/springboot/pom.xml b/applications/springboot/pom.xml index d05f44bf6..1c4bc0597 100644 --- a/applications/springboot/pom.xml +++ b/applications/springboot/pom.xml @@ -24,7 +24,7 @@ com.google.appengine applications - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/devappservertests/pom.xml b/e2etests/devappservertests/pom.xml index fcea82825..86bb4c6d9 100644 --- a/e2etests/devappservertests/pom.xml +++ b/e2etests/devappservertests/pom.xml @@ -22,7 +22,7 @@ com.google.appengine e2etests - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/e2etests/pom.xml b/e2etests/pom.xml index 6dfe5e552..d6db47161 100644 --- a/e2etests/pom.xml +++ b/e2etests/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT AppEngine :: e2e tests https://github.com/GoogleCloudPlatform/appengine-java-standard/ diff --git a/e2etests/stagingtests/pom.xml b/e2etests/stagingtests/pom.xml index 53c3ba28e..e546b8873 100644 --- a/e2etests/stagingtests/pom.xml +++ b/e2etests/stagingtests/pom.xml @@ -22,7 +22,7 @@ com.google.appengine e2etests - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/e2etests/testlocalapps/allinone/pom.xml b/e2etests/testlocalapps/allinone/pom.xml index 1c162995e..f1d69be46 100644 --- a/e2etests/testlocalapps/allinone/pom.xml +++ b/e2etests/testlocalapps/allinone/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/allinone_jakarta/pom.xml b/e2etests/testlocalapps/allinone_jakarta/pom.xml index e23cadbe0..87f1021ec 100644 --- a/e2etests/testlocalapps/allinone_jakarta/pom.xml +++ b/e2etests/testlocalapps/allinone_jakarta/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/badcron/pom.xml b/e2etests/testlocalapps/badcron/pom.xml index 41704184c..638404af7 100644 --- a/e2etests/testlocalapps/badcron/pom.xml +++ b/e2etests/testlocalapps/badcron/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/bundle_standard/pom.xml b/e2etests/testlocalapps/bundle_standard/pom.xml index 3b1341b94..41db18461 100644 --- a/e2etests/testlocalapps/bundle_standard/pom.xml +++ b/e2etests/testlocalapps/bundle_standard/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/bundle_standard_with_container_initializer/pom.xml b/e2etests/testlocalapps/bundle_standard_with_container_initializer/pom.xml index 6caeb47b1..5936f0b82 100644 --- a/e2etests/testlocalapps/bundle_standard_with_container_initializer/pom.xml +++ b/e2etests/testlocalapps/bundle_standard_with_container_initializer/pom.xml @@ -25,7 +25,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/bundle_standard_with_container_initializer_jakarta/pom.xml b/e2etests/testlocalapps/bundle_standard_with_container_initializer_jakarta/pom.xml index bc4e474c3..ff2459e5d 100644 --- a/e2etests/testlocalapps/bundle_standard_with_container_initializer_jakarta/pom.xml +++ b/e2etests/testlocalapps/bundle_standard_with_container_initializer_jakarta/pom.xml @@ -25,7 +25,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/bundle_standard_with_no_jsp/pom.xml b/e2etests/testlocalapps/bundle_standard_with_no_jsp/pom.xml index 855b403a4..ca566bd66 100644 --- a/e2etests/testlocalapps/bundle_standard_with_no_jsp/pom.xml +++ b/e2etests/testlocalapps/bundle_standard_with_no_jsp/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/bundle_standard_with_weblistener_memcache/pom.xml b/e2etests/testlocalapps/bundle_standard_with_weblistener_memcache/pom.xml index 171aebd9c..05485655d 100644 --- a/e2etests/testlocalapps/bundle_standard_with_weblistener_memcache/pom.xml +++ b/e2etests/testlocalapps/bundle_standard_with_weblistener_memcache/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/cron-bad-job-age-limit/pom.xml b/e2etests/testlocalapps/cron-bad-job-age-limit/pom.xml index 5f65c90e6..66736fee6 100644 --- a/e2etests/testlocalapps/cron-bad-job-age-limit/pom.xml +++ b/e2etests/testlocalapps/cron-bad-job-age-limit/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/cron-good-retry-parameters/pom.xml b/e2etests/testlocalapps/cron-good-retry-parameters/pom.xml index 08a48401a..3af9f9788 100644 --- a/e2etests/testlocalapps/cron-good-retry-parameters/pom.xml +++ b/e2etests/testlocalapps/cron-good-retry-parameters/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/cron-negative-max-backoff/pom.xml b/e2etests/testlocalapps/cron-negative-max-backoff/pom.xml index f222ce6ee..2abfefdc1 100644 --- a/e2etests/testlocalapps/cron-negative-max-backoff/pom.xml +++ b/e2etests/testlocalapps/cron-negative-max-backoff/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/cron-negative-retry-limit/pom.xml b/e2etests/testlocalapps/cron-negative-retry-limit/pom.xml index 55d991a71..29bf2314d 100644 --- a/e2etests/testlocalapps/cron-negative-retry-limit/pom.xml +++ b/e2etests/testlocalapps/cron-negative-retry-limit/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/cron-two-max-doublings/pom.xml b/e2etests/testlocalapps/cron-two-max-doublings/pom.xml index fd8237d69..0e465a04e 100644 --- a/e2etests/testlocalapps/cron-two-max-doublings/pom.xml +++ b/e2etests/testlocalapps/cron-two-max-doublings/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/http-headers/pom.xml b/e2etests/testlocalapps/http-headers/pom.xml index 022874f69..9dcb5621f 100644 --- a/e2etests/testlocalapps/http-headers/pom.xml +++ b/e2etests/testlocalapps/http-headers/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/java8-jar/pom.xml b/e2etests/testlocalapps/java8-jar/pom.xml index fa04276b0..7a24610d7 100644 --- a/e2etests/testlocalapps/java8-jar/pom.xml +++ b/e2etests/testlocalapps/java8-jar/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/java8-no-webxml/pom.xml b/e2etests/testlocalapps/java8-no-webxml/pom.xml index 31508028a..eebe02d7a 100644 --- a/e2etests/testlocalapps/java8-no-webxml/pom.xml +++ b/e2etests/testlocalapps/java8-no-webxml/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/pom.xml b/e2etests/testlocalapps/pom.xml index 292152bc7..353d71192 100644 --- a/e2etests/testlocalapps/pom.xml +++ b/e2etests/testlocalapps/pom.xml @@ -24,7 +24,7 @@ com.google.appengine e2etests - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT pom diff --git a/e2etests/testlocalapps/sample-badaeweb/pom.xml b/e2etests/testlocalapps/sample-badaeweb/pom.xml index 11e90cc8b..393f3c3dc 100644 --- a/e2etests/testlocalapps/sample-badaeweb/pom.xml +++ b/e2etests/testlocalapps/sample-badaeweb/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-baddispatch-yaml/pom.xml b/e2etests/testlocalapps/sample-baddispatch-yaml/pom.xml index e5b0650ba..a82ec2b33 100644 --- a/e2etests/testlocalapps/sample-baddispatch-yaml/pom.xml +++ b/e2etests/testlocalapps/sample-baddispatch-yaml/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-baddispatch/pom.xml b/e2etests/testlocalapps/sample-baddispatch/pom.xml index f3702ed43..4eec32b5d 100644 --- a/e2etests/testlocalapps/sample-baddispatch/pom.xml +++ b/e2etests/testlocalapps/sample-baddispatch/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-badindexes/pom.xml b/e2etests/testlocalapps/sample-badindexes/pom.xml index 6f9a1c151..d50c9f914 100644 --- a/e2etests/testlocalapps/sample-badindexes/pom.xml +++ b/e2etests/testlocalapps/sample-badindexes/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-badweb/pom.xml b/e2etests/testlocalapps/sample-badweb/pom.xml index e9779e3f3..9a9bdd4cd 100644 --- a/e2etests/testlocalapps/sample-badweb/pom.xml +++ b/e2etests/testlocalapps/sample-badweb/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-default-auto-ids/pom.xml b/e2etests/testlocalapps/sample-default-auto-ids/pom.xml index 609ec5757..dadf441aa 100644 --- a/e2etests/testlocalapps/sample-default-auto-ids/pom.xml +++ b/e2etests/testlocalapps/sample-default-auto-ids/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-error-in-tag-file/pom.xml b/e2etests/testlocalapps/sample-error-in-tag-file/pom.xml index 5910a3cc2..aaddc3734 100644 --- a/e2etests/testlocalapps/sample-error-in-tag-file/pom.xml +++ b/e2etests/testlocalapps/sample-error-in-tag-file/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-java11/pom.xml b/e2etests/testlocalapps/sample-java11/pom.xml index 9d56a9f97..d1dfd5357 100644 --- a/e2etests/testlocalapps/sample-java11/pom.xml +++ b/e2etests/testlocalapps/sample-java11/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-java17/pom.xml b/e2etests/testlocalapps/sample-java17/pom.xml index 0a62889a9..fc702f9a7 100644 --- a/e2etests/testlocalapps/sample-java17/pom.xml +++ b/e2etests/testlocalapps/sample-java17/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-jsptaglibrary/pom.xml b/e2etests/testlocalapps/sample-jsptaglibrary/pom.xml index 266d64c38..8fe402b43 100644 --- a/e2etests/testlocalapps/sample-jsptaglibrary/pom.xml +++ b/e2etests/testlocalapps/sample-jsptaglibrary/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-jspx/pom.xml b/e2etests/testlocalapps/sample-jspx/pom.xml index bfb8dbcf2..6590022cf 100644 --- a/e2etests/testlocalapps/sample-jspx/pom.xml +++ b/e2etests/testlocalapps/sample-jspx/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-legacy-auto-ids/pom.xml b/e2etests/testlocalapps/sample-legacy-auto-ids/pom.xml index b53dabf1c..631e8c8f6 100644 --- a/e2etests/testlocalapps/sample-legacy-auto-ids/pom.xml +++ b/e2etests/testlocalapps/sample-legacy-auto-ids/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-missingappid/pom.xml b/e2etests/testlocalapps/sample-missingappid/pom.xml index 62ada0fa8..06f887b65 100644 --- a/e2etests/testlocalapps/sample-missingappid/pom.xml +++ b/e2etests/testlocalapps/sample-missingappid/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-nojsps/pom.xml b/e2etests/testlocalapps/sample-nojsps/pom.xml index ab963fa62..2273bdf44 100644 --- a/e2etests/testlocalapps/sample-nojsps/pom.xml +++ b/e2etests/testlocalapps/sample-nojsps/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-unspecified-auto-ids/pom.xml b/e2etests/testlocalapps/sample-unspecified-auto-ids/pom.xml index 4d298cbec..d3ca6b880 100644 --- a/e2etests/testlocalapps/sample-unspecified-auto-ids/pom.xml +++ b/e2etests/testlocalapps/sample-unspecified-auto-ids/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sample-with-classes/pom.xml b/e2etests/testlocalapps/sample-with-classes/pom.xml index c5482cca0..3a94287b2 100644 --- a/e2etests/testlocalapps/sample-with-classes/pom.xml +++ b/e2etests/testlocalapps/sample-with-classes/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sampleapp-automatic-module/pom.xml b/e2etests/testlocalapps/sampleapp-automatic-module/pom.xml index c3904d53f..55a9d0ca8 100644 --- a/e2etests/testlocalapps/sampleapp-automatic-module/pom.xml +++ b/e2etests/testlocalapps/sampleapp-automatic-module/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sampleapp-backends/pom.xml b/e2etests/testlocalapps/sampleapp-backends/pom.xml index 0f5332890..36f12a11e 100644 --- a/e2etests/testlocalapps/sampleapp-backends/pom.xml +++ b/e2etests/testlocalapps/sampleapp-backends/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war AppEngine :: sampleapp-backends diff --git a/e2etests/testlocalapps/sampleapp-basic-module/pom.xml b/e2etests/testlocalapps/sampleapp-basic-module/pom.xml index 5fd954aef..d6368a9f2 100644 --- a/e2etests/testlocalapps/sampleapp-basic-module/pom.xml +++ b/e2etests/testlocalapps/sampleapp-basic-module/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sampleapp-manual-module/pom.xml b/e2etests/testlocalapps/sampleapp-manual-module/pom.xml index 449704500..f75bc53af 100644 --- a/e2etests/testlocalapps/sampleapp-manual-module/pom.xml +++ b/e2etests/testlocalapps/sampleapp-manual-module/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sampleapp-runtime/pom.xml b/e2etests/testlocalapps/sampleapp-runtime/pom.xml index bb82a50c3..0cef0c28f 100644 --- a/e2etests/testlocalapps/sampleapp-runtime/pom.xml +++ b/e2etests/testlocalapps/sampleapp-runtime/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/sampleapp/pom.xml b/e2etests/testlocalapps/sampleapp/pom.xml index e333a1667..b254d504c 100644 --- a/e2etests/testlocalapps/sampleapp/pom.xml +++ b/e2etests/testlocalapps/sampleapp/pom.xml @@ -25,7 +25,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT AppEngine :: sampleapp https://github.com/GoogleCloudPlatform/appengine-java-standard/ diff --git a/e2etests/testlocalapps/stage-sampleapp/pom.xml b/e2etests/testlocalapps/stage-sampleapp/pom.xml index 251630139..07d72ee49 100644 --- a/e2etests/testlocalapps/stage-sampleapp/pom.xml +++ b/e2etests/testlocalapps/stage-sampleapp/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/stage-with-staging-options/pom.xml b/e2etests/testlocalapps/stage-with-staging-options/pom.xml index 3be7d6512..7b790187a 100644 --- a/e2etests/testlocalapps/stage-with-staging-options/pom.xml +++ b/e2etests/testlocalapps/stage-with-staging-options/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war diff --git a/e2etests/testlocalapps/xmlorder/pom.xml b/e2etests/testlocalapps/xmlorder/pom.xml index e7db085a5..d3dd4e10e 100644 --- a/e2etests/testlocalapps/xmlorder/pom.xml +++ b/e2etests/testlocalapps/xmlorder/pom.xml @@ -24,7 +24,7 @@ com.google.appengine testlocalapps - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT war AppEngine :: xmlorder diff --git a/external/geronimo_javamail/pom.xml b/external/geronimo_javamail/pom.xml index 6a09eca53..1843bb0f1 100644 --- a/external/geronimo_javamail/pom.xml +++ b/external/geronimo_javamail/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT ../../pom.xml diff --git a/jetty121_assembly/pom.xml b/jetty121_assembly/pom.xml index c94b8dc51..6fbf98a21 100644 --- a/jetty121_assembly/pom.xml +++ b/jetty121_assembly/pom.xml @@ -20,7 +20,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT 4.0.0 jetty121-assembly diff --git a/lib/pom.xml b/lib/pom.xml index e9a9868bd..3551be09f 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -24,7 +24,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT pom diff --git a/lib/tools_api/pom.xml b/lib/tools_api/pom.xml index 20d530421..5a16095bc 100644 --- a/lib/tools_api/pom.xml +++ b/lib/tools_api/pom.xml @@ -23,7 +23,7 @@ com.google.appengine lib-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/lib/xml_validator/pom.xml b/lib/xml_validator/pom.xml index 890937be2..619f66227 100644 --- a/lib/xml_validator/pom.xml +++ b/lib/xml_validator/pom.xml @@ -22,7 +22,7 @@ com.google.appengine lib-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar AppEngine :: libxmlvalidator diff --git a/lib/xml_validator_test/pom.xml b/lib/xml_validator_test/pom.xml index 111ff96cc..400be5470 100644 --- a/lib/xml_validator_test/pom.xml +++ b/lib/xml_validator_test/pom.xml @@ -22,7 +22,7 @@ com.google.appengine lib-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar AppEngine :: libxmlvalidator_test diff --git a/local_runtime_shared_jetty12/pom.xml b/local_runtime_shared_jetty12/pom.xml index 1a4fdfe6e..216773a20 100644 --- a/local_runtime_shared_jetty12/pom.xml +++ b/local_runtime_shared_jetty12/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar AppEngine :: appengine-local-runtime-shared Jakarta diff --git a/pom.xml b/pom.xml index 3ffe60a3a..2d211f26c 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT pom AppEngine :: Parent project https://github.com/GoogleCloudPlatform/appengine-java-standard/ @@ -66,6 +66,9 @@ 17 17 UTF-8 + 1.1.1 + 1.11.1 + 1.81.0 9.4.58.v20250814 12.0.36 12.1.10 @@ -278,6 +281,32 @@ + + io.grpc + grpc-netty-shaded + ${grpc.version} + + + io.grpc + grpc-protobuf + ${grpc.version} + + + io.grpc + grpc-stub + ${grpc.version} + + + io.grpc + grpc-auth + ${grpc.version} + + + io.grpc + grpc-testing + ${grpc.version} + test + com.google.appengine appengine-init @@ -443,25 +472,25 @@ com.google.auto.service auto-service - 1.1.1 + ${auto-service.version} provided com.google.auto.service auto-service-annotations - 1.1.1 + ${auto-service.version} provided com.google.auto.value auto-value - 1.11.1 + ${auto-value.version} provided com.google.auto.value auto-value-annotations - 1.11.1 + ${auto-value.version} com.contrastsecurity @@ -716,7 +745,21 @@ commons-lang3 3.20.0 - + + javax.annotation + javax.annotation-api + 1.3.2 + + + com.google.auth + google-auth-library-oauth2-http + 1.25.0 + + + com.google.cloud + google-cloud-storage + 2.63.0 + diff --git a/protobuf/api/images_service_rpc.proto b/protobuf/api/images_service_rpc.proto new file mode 100644 index 000000000..ea475f0dc --- /dev/null +++ b/protobuf/api/images_service_rpc.proto @@ -0,0 +1,57 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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. +// Copyright 2008 Google Inc. All Rights Reserved. + +syntax = "proto2"; + +// Some generic_services option(s) added automatically. +// See: http://go/proto2-generic-services-default +package apphosting; + +import "images_service.proto"; + +option java_package = "com.google.appengine.api.images.proto"; +option java_outer_classname = "ImagesServiceRPC"; +option java_multiple_files = true; + +service ImagesService { + // Perform transformations on the given image and return the result. + rpc Transform(java.apphosting.ImagesTransformRequest) + returns (java.apphosting.ImagesTransformResponse) {} + + // Composite images onto a canvas and return the result. + rpc Composite(java.apphosting.ImagesCompositeRequest) + returns (java.apphosting.ImagesCompositeResponse) {} + + // Calculate the histogram of an image and return it. + rpc Histogram(java.apphosting.ImagesHistogramRequest) + returns (java.apphosting.ImagesHistogramResponse) {} + + // Obtains a URL that can serve the image dynamically at different sizes. + // The URL format will look like the following: + // + // http://lh3.ggpht.com/SomeEncryptedString + // + // to serve this image at different sizes just append the size option: + // + // http://lh3.ggpht.com/SomeEncryptedString=s128 (serves a 128 sized image) + // + // The allowed thumbnail sizes are any integer in the range [0, 1600]. + rpc GetUrlBase(java.apphosting.ImagesGetUrlBaseRequest) + returns (java.apphosting.ImagesGetUrlBaseResponse) {} + + // Delete a URL that was created with GetUrlBase + rpc DeleteUrlBase(java.apphosting.ImagesDeleteUrlBaseRequest) + returns (java.apphosting.ImagesDeleteUrlBaseResponse) {} +} diff --git a/protobuf/pom.xml b/protobuf/pom.xml index 810a4a772..436d025e1 100644 --- a/protobuf/pom.xml +++ b/protobuf/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar @@ -36,6 +36,18 @@ com.google.protobuf protobuf-java + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + + + javax.annotation + javax.annotation-api + @@ -44,12 +56,21 @@ com.github.os72 protoc-jar-maven-plugin - - . - - - ./api - + + . + + + ./api + + + + java + + + grpc-java + io.grpc:protoc-gen-grpc-java:${grpc.version} + + diff --git a/quickstartgenerator_jetty121_ee11/pom.xml b/quickstartgenerator_jetty121_ee11/pom.xml index 872890959..286a544d5 100644 --- a/quickstartgenerator_jetty121_ee11/pom.xml +++ b/quickstartgenerator_jetty121_ee11/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/quickstartgenerator_jetty121_ee8/pom.xml b/quickstartgenerator_jetty121_ee8/pom.xml index c3540e422..7df4a818d 100644 --- a/quickstartgenerator_jetty121_ee8/pom.xml +++ b/quickstartgenerator_jetty121_ee8/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/remoteapi/pom.xml b/remoteapi/pom.xml index 17cbbaf2e..382082754 100644 --- a/remoteapi/pom.xml +++ b/remoteapi/pom.xml @@ -20,7 +20,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar AppEngine :: appengine-remote-api diff --git a/runtime/annotationscanningwebapp/pom.xml b/runtime/annotationscanningwebapp/pom.xml index fe112481a..322fc3ffb 100644 --- a/runtime/annotationscanningwebapp/pom.xml +++ b/runtime/annotationscanningwebapp/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos annotationscanningwebapp diff --git a/runtime/annotationscanningwebappjakarta/pom.xml b/runtime/annotationscanningwebappjakarta/pom.xml index 429ae3678..803b128dd 100644 --- a/runtime/annotationscanningwebappjakarta/pom.xml +++ b/runtime/annotationscanningwebappjakarta/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos annotationscanningwebappjakarta diff --git a/runtime/deployment/pom.xml b/runtime/deployment/pom.xml index 72e98e318..a356c9539 100644 --- a/runtime/deployment/pom.xml +++ b/runtime/deployment/pom.xml @@ -22,7 +22,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT pom diff --git a/runtime/failinitfilterwebapp/pom.xml b/runtime/failinitfilterwebapp/pom.xml index 868dc1f45..1199945ca 100644 --- a/runtime/failinitfilterwebapp/pom.xml +++ b/runtime/failinitfilterwebapp/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos failinitfilterwebapp diff --git a/runtime/failinitfilterwebappjakarta/pom.xml b/runtime/failinitfilterwebappjakarta/pom.xml index e625e907d..2b216ac6b 100644 --- a/runtime/failinitfilterwebappjakarta/pom.xml +++ b/runtime/failinitfilterwebappjakarta/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos failinitfilterwebappjakarta diff --git a/runtime/impl/pom.xml b/runtime/impl/pom.xml index 2ec433bfc..b9b4a457e 100644 --- a/runtime/impl/pom.xml +++ b/runtime/impl/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime/local_jetty121/pom.xml b/runtime/local_jetty121/pom.xml index c23ec04bc..6df2b6418 100644 --- a/runtime/local_jetty121/pom.xml +++ b/runtime/local_jetty121/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime/local_jetty121_ee11/pom.xml b/runtime/local_jetty121_ee11/pom.xml index 6f95a3eb8..a14e2e027 100644 --- a/runtime/local_jetty121_ee11/pom.xml +++ b/runtime/local_jetty121_ee11/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime/main/pom.xml b/runtime/main/pom.xml index 17cbd6e5d..69b6d2648 100644 --- a/runtime/main/pom.xml +++ b/runtime/main/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime/nogaeapiswebapp/pom.xml b/runtime/nogaeapiswebapp/pom.xml index 8f71f8676..1b228d693 100644 --- a/runtime/nogaeapiswebapp/pom.xml +++ b/runtime/nogaeapiswebapp/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos nogaeapiswebapp diff --git a/runtime/nogaeapiswebappjakarta/pom.xml b/runtime/nogaeapiswebappjakarta/pom.xml index 37ffb16fe..ab2edde13 100644 --- a/runtime/nogaeapiswebappjakarta/pom.xml +++ b/runtime/nogaeapiswebappjakarta/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT com.google.appengine.demos nogaeapiswebappjakarta diff --git a/runtime/pom.xml b/runtime/pom.xml index 364e71ab5..05ead1bb5 100644 --- a/runtime/pom.xml +++ b/runtime/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT AppEngine :: runtime projects https://github.com/GoogleCloudPlatform/appengine-java-standard/ diff --git a/runtime/runtime_impl_jetty12/pom.xml b/runtime/runtime_impl_jetty12/pom.xml index 9d61d4afa..a3940efd6 100644 --- a/runtime/runtime_impl_jetty12/pom.xml +++ b/runtime/runtime_impl_jetty12/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime/runtime_impl_jetty121/pom.xml b/runtime/runtime_impl_jetty121/pom.xml index cd96cbb44..368acd3aa 100644 --- a/runtime/runtime_impl_jetty121/pom.xml +++ b/runtime/runtime_impl_jetty121/pom.xml @@ -23,7 +23,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime/test/pom.xml b/runtime/test/pom.xml index a1195f517..d4cf3f715 100644 --- a/runtime/test/pom.xml +++ b/runtime/test/pom.xml @@ -22,7 +22,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime/testapps/pom.xml b/runtime/testapps/pom.xml index 1ef403f5b..10759fa30 100644 --- a/runtime/testapps/pom.xml +++ b/runtime/testapps/pom.xml @@ -22,7 +22,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime/util/pom.xml b/runtime/util/pom.xml index 9f632d1ef..8215fb0a5 100644 --- a/runtime/util/pom.xml +++ b/runtime/util/pom.xml @@ -22,7 +22,7 @@ com.google.appengine runtime-parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime_shared/pom.xml b/runtime_shared/pom.xml index aa13a7ae9..f8b120467 100644 --- a/runtime_shared/pom.xml +++ b/runtime_shared/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime_shared_jetty12/pom.xml b/runtime_shared_jetty12/pom.xml index e2ba85c3c..c4b3207a7 100644 --- a/runtime_shared_jetty12/pom.xml +++ b/runtime_shared_jetty12/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime_shared_jetty121_ee11/pom.xml b/runtime_shared_jetty121_ee11/pom.xml index 962603ff6..2c76d8635 100644 --- a/runtime_shared_jetty121_ee11/pom.xml +++ b/runtime_shared_jetty121_ee11/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime_shared_jetty121_ee8/pom.xml b/runtime_shared_jetty121_ee8/pom.xml index 9af947be3..2cd5d2b52 100644 --- a/runtime_shared_jetty121_ee8/pom.xml +++ b/runtime_shared_jetty121_ee8/pom.xml @@ -24,7 +24,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/runtime_shared_jetty12_ee10/pom.xml b/runtime_shared_jetty12_ee10/pom.xml index a5935cd93..126e48b5f 100644 --- a/runtime_shared_jetty12_ee10/pom.xml +++ b/runtime_shared_jetty12_ee10/pom.xml @@ -22,7 +22,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/sdk_assembly/pom.xml b/sdk_assembly/pom.xml index 7ca92d80f..8fb948ea0 100644 --- a/sdk_assembly/pom.xml +++ b/sdk_assembly/pom.xml @@ -20,7 +20,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT 4.0.0 appengine-java-sdk diff --git a/sessiondata/pom.xml b/sessiondata/pom.xml index 7e7a5d55d..7e0e1aa26 100644 --- a/sessiondata/pom.xml +++ b/sessiondata/pom.xml @@ -23,7 +23,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/shared_sdk/pom.xml b/shared_sdk/pom.xml index bf2d2d68f..46883c2ff 100644 --- a/shared_sdk/pom.xml +++ b/shared_sdk/pom.xml @@ -21,7 +21,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/shared_sdk_jetty12/pom.xml b/shared_sdk_jetty12/pom.xml index 5709e1891..3949c6890 100644 --- a/shared_sdk_jetty12/pom.xml +++ b/shared_sdk_jetty12/pom.xml @@ -21,7 +21,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/shared_sdk_jetty121/pom.xml b/shared_sdk_jetty121/pom.xml index 8d8f5b3a0..1f158849a 100644 --- a/shared_sdk_jetty121/pom.xml +++ b/shared_sdk_jetty121/pom.xml @@ -21,7 +21,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT jar diff --git a/utils/pom.xml b/utils/pom.xml index 0fdfba46b..0fb3a6e6a 100644 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -21,7 +21,7 @@ com.google.appengine parent - 5.0.5-SNAPSHOT + 5.0.5-beta.1-SNAPSHOT true