Repository: googleapis/sdk-platform-java (java)

Issue:
Generator should skip generation for empty services
A scenario came up with https://github.com/googleapis/google-cloud-java/pull/10802 where https://github.com/googleapis/googleapis/blob/master/google/cloud/visionai/v1/prediction.proto contains an empty service (e.g. no RPCs). 

There tests fail when trying to compile the generated library containing this proto which means the generator is generating some classes for this empty service which are not compilable. The generator instead should skip generation for an empty service. 

Pull request:
fix: Generator to skip generation for empty services.
fixes #2750
Skip parsing a service if no RPCs found for. 
In the scenario that only one service with no RPCs or all services have no RPCs, falls back to #2460

This pr also reverts a change brought by #985, and removes the relevant tests. For more context, this has been discussed  [here](https://github.com/googleapis/sdk-platform-java/issues/2750#issuecomment-2231737360).

Patch:
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..57185ea00
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,4 @@
+contact_links:
+  - name: Google Cloud Support
+    url: https://cloud.google.com/support/
+    about: If you have a support contract with Google, please use the Google Cloud Support portal.
\ No newline at end of file
diff --git a/WORKSPACE b/WORKSPACE
index 4e261d5aa..8f9e57e6c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -55,18 +55,17 @@ rules_jvm_external_setup()
 load("@com_google_protobuf//:protobuf_deps.bzl", "PROTOBUF_MAVEN_ARTIFACTS", "protobuf_deps")
 load("@rules_jvm_external//:defs.bzl", "maven_install")
 
-maven_install(
-    artifacts = PROTOBUF_MAVEN_ARTIFACTS,
-    repositories = ["https://repo.maven.apache.org/maven2/"],
-)
+load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_ARTIFACTS")
+load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS")
 
 _gapic_generator_java_version = "2.42.1-SNAPSHOT"  # {x-version-update:gapic-generator-java:current}
 
 maven_install(
     artifacts = [
         "com.google.api:gapic-generator-java:" + _gapic_generator_java_version,
-    ],
+    ] + PROTOBUF_MAVEN_ARTIFACTS + IO_GRPC_GRPC_JAVA_ARTIFACTS,
     fail_on_missing_checksum = False,
+    override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS,
     repositories = [
         "m2Local",
         "https://repo.maven.apache.org/maven2/",
@@ -98,6 +97,10 @@ load("@io_grpc_grpc_java//:repositories.bzl", "grpc_java_repositories")
 
 grpc_java_repositories()
 
+load("@envoy_api//bazel:repositories.bzl", "api_dependencies")
+
+api_dependencies()
+
 _disco_to_proto3_converter_commit = "ce8d8732120cdfb5bf4847c3238b5be8acde87e3"
 
 http_archive(
diff --git a/api-common-java/pom.xml b/api-common-java/pom.xml
index 784a17617..cce43fb88 100644
--- a/api-common-java/pom.xml
+++ b/api-common-java/pom.xml
@@ -89,7 +89,7 @@
         <dependency>
             <groupId>com.google.truth</groupId>
             <artifactId>truth</artifactId>
-            <version>1.4.3</version>
+            <version>1.4.4</version>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/gapic-generator-java-pom-parent/pom.xml b/gapic-generator-java-pom-parent/pom.xml
index 01e56475b..de2d47c16 100644
--- a/gapic-generator-java-pom-parent/pom.xml
+++ b/gapic-generator-java-pom-parent/pom.xml
@@ -26,7 +26,7 @@
     <!-- External dependencies, especially gRPC and Protobuf version, should be
         consistent across modules in this repository -->
     <javax.annotation-api.version>1.3.2</javax.annotation-api.version>
-    <grpc.version>1.62.2</grpc.version>
+    <grpc.version>1.65.0</grpc.version>
     <google.auth.version>1.24.0</google.auth.version>
     <google.http-client.version>1.44.2</google.http-client.version>
     <gson.version>2.11.0</gson.version>
@@ -34,7 +34,7 @@
     <protobuf.version>3.25.3</protobuf.version>
     <opentelemetry.version>1.40.0</opentelemetry.version>
     <maven.compiler.release>8</maven.compiler.release>
-    <errorprone.version>2.28.0</errorprone.version>
+    <errorprone.version>2.29.2</errorprone.version>
     <j2objc-annotations.version>3.0.0</j2objc-annotations.version>
     <threetenbp.version>1.6.9</threetenbp.version>
     <junit.version>5.10.3</junit.version>
@@ -115,7 +115,7 @@
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-surefire-plugin</artifactId>
-              <version>3.3.0</version>
+              <version>3.3.1</version>
               <configuration>
                 <!-- Excludes integration tests and smoke tests when unit tests are run -->
                 <excludes>
@@ -131,7 +131,7 @@
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-failsafe-plugin</artifactId>
-              <version>3.3.0</version>
+              <version>3.3.1</version>
               <executions>
                 <execution>
                   <goals>
diff --git a/gapic-generator-java/pom.xml b/gapic-generator-java/pom.xml
index 0afd5c0ef..c755dc843 100644
--- a/gapic-generator-java/pom.xml
+++ b/gapic-generator-java/pom.xml
@@ -317,7 +317,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
-        <version>3.3.0</version>
+        <version>3.3.1</version>
         <configuration>
           <additionalClasspathElements>
             <additionalClasspathElement>
@@ -467,7 +467,7 @@
     <dependency>
       <groupId>com.google.truth</groupId>
       <artifactId>truth</artifactId>
-      <version>1.4.3</version>
+      <version>1.4.4</version>
       <scope>test</scope>
     </dependency>
     <dependency>
diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientHeaderSampleComposer.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientHeaderSampleComposer.java
index b979f8774..6403890b9 100644
--- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientHeaderSampleComposer.java
+++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientHeaderSampleComposer.java
@@ -52,10 +52,6 @@ public class ServiceClientHeaderSampleComposer {
       TypeNode clientType,
       Map<String, ResourceName> resourceNames,
       Map<String, Message> messageTypes) {
-    if (service.methods().isEmpty()) {
-      return ServiceClientMethodSampleComposer.composeEmptyServiceSample(clientType, service);
-    }
-
     // Use the first pure unary RPC method's sample code as showcase, if no such method exists, use
     // the first method in the service's methods list.
     Method method =
diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java
index e7c6bd896..9a2da7474 100644
--- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java
+++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java
@@ -435,6 +435,17 @@ public class Parser {
       Transport transport) {
 
     return fileDescriptor.getServices().stream()
+        .filter(
+            serviceDescriptor -> {
+              List<MethodDescriptor> methodsList = serviceDescriptor.getMethods();
+              if (methodsList.isEmpty()) {
+                LOGGER.warning(
+                    String.format(
+                        "Service %s has no RPC methods and will not be generated",
+                        serviceDescriptor.getName()));
+              }
+              return !methodsList.isEmpty();
+            })
         .map(
             s -> {
               // Workaround for a missing default_host and oauth_scopes annotation from a service
diff --git a/gax-java/dependencies.properties b/gax-java/dependencies.properties
index 7eef4d4b8..b8a8d4ab5 100644
--- a/gax-java/dependencies.properties
+++ b/gax-java/dependencies.properties
@@ -28,7 +28,7 @@ version.gax_httpjson=2.50.1-SNAPSHOT
 
 version.com_google_protobuf=3.25.3
 version.google_java_format=1.15.0
-version.io_grpc=1.62.2
+version.io_grpc=1.65.0
 
 # Maven artifacts.
 # Note, the actual name of each property matters (bazel build scripts depend on it).
@@ -44,26 +44,26 @@ maven.io_opencensus_opencensus_api=io.opencensus:opencensus-api:0.31.1
 maven.io_opencensus_opencensus_contrib_grpc_metrics=io.opencensus:opencensus-contrib-grpc-metrics:0.31.1
 maven.io_opencensus_opencensus_contrib_http_util=io.opencensus:opencensus-contrib-http-util:0.31.1
 maven.io_netty_netty_tcnative_boringssl_static=io.netty:netty-tcnative-boringssl-static:2.0.65.Final
-maven.io_netty_netty_handler=io.netty:netty-handler:4.1.111.Final
-maven.io_netty_netty_common=io.netty:netty-common:4.1.111.Final
-maven.io_netty_netty_codec_socks=io.netty:netty-codec-socks:4.1.111.Final
-maven.io_netty_netty_codec_http2=io.netty:netty-codec-http2:4.1.111.Final
-maven.io_netty_netty_codec_http=io.netty:netty-codec-http:4.1.111.Final
-maven.io_netty_netty_codec=io.netty:netty-codec:4.1.111.Final
-maven.io_netty_netty_buffer=io.netty:netty-buffer:4.1.111.Final
-maven.io_netty_netty_resolver=io.netty:netty-resolver:4.1.111.Final
-maven.io_netty_netty_transport=io.netty:netty-transport:4.1.111.Final
-maven.io_netty_netty_handler_proxy=io.netty:netty-handler-proxy:4.1.111.Final
-maven.io_netty_netty_transport_native_unix_common=io.netty:netty-transport-native-unix-common:4.1.111.Final
+maven.io_netty_netty_handler=io.netty:netty-handler:4.1.112.Final
+maven.io_netty_netty_common=io.netty:netty-common:4.1.112.Final
+maven.io_netty_netty_codec_socks=io.netty:netty-codec-socks:4.1.112.Final
+maven.io_netty_netty_codec_http2=io.netty:netty-codec-http2:4.1.112.Final
+maven.io_netty_netty_codec_http=io.netty:netty-codec-http:4.1.112.Final
+maven.io_netty_netty_codec=io.netty:netty-codec:4.1.112.Final
+maven.io_netty_netty_buffer=io.netty:netty-buffer:4.1.112.Final
+maven.io_netty_netty_resolver=io.netty:netty-resolver:4.1.112.Final
+maven.io_netty_netty_transport=io.netty:netty-transport:4.1.112.Final
+maven.io_netty_netty_handler_proxy=io.netty:netty-handler-proxy:4.1.112.Final
+maven.io_netty_netty_transport_native_unix_common=io.netty:netty-transport-native-unix-common:4.1.112.Final
 maven.io_perfmark_perfmark_api=io.perfmark:perfmark-api:0.27.0
 maven.org_apache_tomcat_annotations_api=org.apache.tomcat:annotations-api:6.0.53
 maven.com_google_code_gson_gson=com.google.code.gson:gson:2.11.0
 maven.com_google_guava_guava=com.google.guava:guava:32.1.3-jre
 maven.com_google_guava_failureaccess=com.google.guava:failureaccess:1.0.2
-maven.org_apache_commons_commons_lang3=org.apache.commons:commons-lang3:3.14.0
+maven.org_apache_commons_commons_lang3=org.apache.commons:commons-lang3:3.15.0
 maven.com_google_android_annotations=com.google.android:annotations:4.1.1.4
 maven.com_google_code_findbugs_jsr305=com.google.code.findbugs:jsr305:3.0.2
-maven.com_google_errorprone_error_prone_annotations=com.google.errorprone:error_prone_annotations:2.28.0
+maven.com_google_errorprone_error_prone_annotations=com.google.errorprone:error_prone_annotations:2.29.2
 maven.com_google_j2objc_j2objc_annotations=com.google.j2objc:j2objc-annotations:2.8
 maven.com_google_auto_value_auto_value=com.google.auto.value:auto-value:1.11.0
 maven.com_google_auto_value_auto_value_annotations=com.google.auto.value:auto-value-annotations:1.11.0
@@ -82,7 +82,7 @@ maven.junit_junit=junit:junit:4.13.2
 maven.org_mockito_mockito_core=org.mockito:mockito-core:4.11.0
 maven.org_mockito_mockito_junit_jupiter=org.mockito:mockito-junit-jupiter:4.11.0
 maven.org_hamcrest_hamcrest_core=org.hamcrest:hamcrest-core:1.3
-maven.com_google_truth_truth=com.google.truth:truth:1.4.3
+maven.com_google_truth_truth=com.google.truth:truth:1.4.4
 maven.com_googlecode_java_diff_utils_diffutils=com.googlecode.java-diff-utils:diffutils:1.3.0
 maven.net_bytebuddy_byte_buddy=net.bytebuddy:byte-buddy:1.14.18
 maven.org_objenesis_objenesis=org.objenesis:objenesis:2.6
diff --git a/gax-java/pom.xml b/gax-java/pom.xml
index 60f3d29d6..426c905c5 100644
--- a/gax-java/pom.xml
+++ b/gax-java/pom.xml
@@ -192,7 +192,7 @@
     <dependency>
       <groupId>com.google.truth</groupId>
       <artifactId>truth</artifactId>
-      <version>1.4.3</version>
+      <version>1.4.4</version>
       <exclusions>
         <exclusion>
           <groupId>org.checkerframework</groupId>
diff --git a/java-common-protos/pom.xml b/java-common-protos/pom.xml
index 31b4bf66f..03d80d1bd 100644
--- a/java-common-protos/pom.xml
+++ b/java-common-protos/pom.xml
@@ -119,7 +119,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-project-info-reports-plugin</artifactId>
-        <version>3.6.1</version>
+        <version>3.6.2</version>
         <reportSets>
           <reportSet>
             <reports>
diff --git a/java-core/pom.xml b/java-core/pom.xml
index 75439f1cd..a44c1a05e 100644
--- a/java-core/pom.xml
+++ b/java-core/pom.xml
@@ -41,7 +41,7 @@
       <dependency>
         <groupId>com.google.truth</groupId>
         <artifactId>truth</artifactId>
-        <version>1.4.3</version>
+        <version>1.4.4</version>
         <scope>test</scope>
         <exclusions>
           <exclusion>
diff --git a/java-iam/pom.xml b/java-iam/pom.xml
index 7b8fd2ed2..4286ca2f3 100644
--- a/java-iam/pom.xml
+++ b/java-iam/pom.xml
@@ -156,7 +156,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-project-info-reports-plugin</artifactId>
-        <version>3.6.1</version>
+        <version>3.6.2</version>
         <reportSets>
           <reportSet>
             <reports>
diff --git a/java-shared-dependencies/dependency-analyzer/pom.xml b/java-shared-dependencies/dependency-analyzer/pom.xml
index 7a8cb11f3..178f0832d 100644
--- a/java-shared-dependencies/dependency-analyzer/pom.xml
+++ b/java-shared-dependencies/dependency-analyzer/pom.xml
@@ -41,7 +41,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
-        <version>3.3.0</version>
+        <version>3.3.1</version>
         <configuration>
           <environmentVariables>
             <!--this environment variable is used to set token when construct
@@ -74,7 +74,7 @@
     <dependency>
       <groupId>com.google.truth</groupId>
       <artifactId>truth</artifactId>
-      <version>1.4.3</version>
+      <version>1.4.4</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
diff --git a/java-shared-dependencies/third-party-dependencies/pom.xml b/java-shared-dependencies/third-party-dependencies/pom.xml
index fcc95df6c..768222b38 100644
--- a/java-shared-dependencies/third-party-dependencies/pom.xml
+++ b/java-shared-dependencies/third-party-dependencies/pom.xml
@@ -28,19 +28,19 @@
     <opencensus.version>0.31.1</opencensus.version>
     <findbugs.version>3.0.2</findbugs.version>
     <jackson.version>2.17.2</jackson.version>
-    <errorprone.version>2.28.0</errorprone.version>
-    <codec.version>1.17.0</codec.version>
+    <errorprone.version>2.29.2</errorprone.version>
+    <codec.version>1.17.1</codec.version>
     <httpcomponents.httpcore.version>4.4.16</httpcomponents.httpcore.version>
     <httpcomponents.httpclient.version>4.5.14</httpcomponents.httpclient.version>
     <!-- ensure checker-qual version matches what Guava uses -->
     <checker-qual.version>3.45.0</checker-qual.version>
     <perfmark-api.version>0.27.0</perfmark-api.version>
     <j2objc-annotations.version>3.0.0</j2objc-annotations.version>
-    <google.cloud.opentelemetry.version>0.30.0</google.cloud.opentelemetry.version>
+    <google.cloud.opentelemetry.version>0.31.0</google.cloud.opentelemetry.version>
     <opentelemetry-grpc-instrumentation.version>2.1.0-alpha</opentelemetry-grpc-instrumentation.version>
     <flogger.version>0.8</flogger.version>
     <arrow.version>15.0.2</arrow.version>
-    <dev.cel.version>0.5.2</dev.cel.version>
+    <dev.cel.version>0.6.0</dev.cel.version>
     <com.google.crypto.tink.version>1.14.0</com.google.crypto.tink.version>
   </properties>
 
diff --git a/java-shared-dependencies/unmanaged-dependency-check/pom.xml b/java-shared-dependencies/unmanaged-dependency-check/pom.xml
index f745e5271..e7451d7f5 100644
--- a/java-shared-dependencies/unmanaged-dependency-check/pom.xml
+++ b/java-shared-dependencies/unmanaged-dependency-check/pom.xml
@@ -67,7 +67,7 @@
     <dependency>
       <groupId>com.google.truth</groupId>
       <artifactId>truth</artifactId>
-      <version>1.4.3</version>
+      <version>1.4.4</version>
       <scope>test</scope>
     </dependency>
     <dependency>
diff --git a/library_generation/README.md b/library_generation/README.md
index 7ffe6a4a5..8bc47bad7 100644
--- a/library_generation/README.md
+++ b/library_generation/README.md
@@ -105,29 +105,31 @@ The library level parameters define how to generate a (multi-versions) GAPIC
 library.
 They are shared by all GAPICs of a library.
 
-| Name                  | Required | Notes                                                                              |
-|:----------------------|:--------:|:-----------------------------------------------------------------------------------|
-| api_shortname         |   Yes    |                                                                                    |
-| api_description       |   Yes    |                                                                                    |
-| name_pretty           |   Yes    |                                                                                    |
-| product_docs          |   Yes    |                                                                                    |
-| library_type          |    No    | `GAPIC_AUTO` if not specified                                                      |
-| release_level         |    No    | `preview` if not specified                                                         |
-| api_id                |    No    | `{api_shortname}.googleapis.com` if not specified                                  |
-| api_reference         |    No    |                                                                                    |
-| codeowner_team        |    No    |                                                                                    |
-| client_documentation  |    No    |                                                                                    |
-| distribution_name     |    No    | `{group_id}:google-{cloud_prefix}{library_name}` if not specified                  |
-| excluded_poms         |    No    |                                                                                    |
-| excluded_dependencies |    No    |                                                                                    |
-| googleapis_commitish  |    No    | use repository level `googleapis_commitish` if not specified.                      |
-| group_id              |    No    | `com.google.cloud` if not specified                                                |
-| issue_tracker         |    No    |                                                                                    |
-| library_name          |    No    | `api_shortname` is not specified. This value should be unique among all libraries. |
-| rest_documentation    |    No    |                                                                                    |
-| rpc_documentation     |    No    |                                                                                    |
-| cloud_api             |    No    | `true` if not specified                                                            |
-| requires-billing      |    No    | `true` if not specified                                                            |
+| Name                  | Required | Notes                                                                                                                                     |
+|:----------------------|:--------:|:------------------------------------------------------------------------------------------------------------------------------------------|
+| api_shortname         |   Yes    |                                                                                                                                           |
+| api_description       |   Yes    |                                                                                                                                           |
+| name_pretty           |   Yes    |                                                                                                                                           |
+| product_docs          |   Yes    |                                                                                                                                           |
+| library_type          |    No    | `GAPIC_AUTO` if not specified                                                                                                             |
+| release_level         |    No    | `preview` if not specified                                                                                                                |
+| api_id                |    No    | `{api_shortname}.googleapis.com` if not specified                                                                                         |
+| api_reference         |    No    |                                                                                                                                           |
+| codeowner_team        |    No    |                                                                                                                                           |
+| client_documentation  |    No    |                                                                                                                                           |
+| distribution_name     |    No    | `{group_id}:google-{cloud_prefix}{library_name}` if not specified                                                                         |
+| excluded_poms         |    No    |                                                                                                                                           |
+| excluded_dependencies |    No    |                                                                                                                                           |
+| googleapis_commitish  |    No    | use repository level `googleapis_commitish` if not specified.                                                                             |
+| group_id              |    No    | `com.google.cloud` if not specified                                                                                                       |
+| issue_tracker         |    No    |                                                                                                                                           |
+| library_name          |    No    | `api_shortname` is not specified. This value should be unique among all libraries.                                                        |
+| rest_documentation    |    No    |                                                                                                                                           |
+| rpc_documentation     |    No    |                                                                                                                                           |
+| cloud_api             |    No    | `true` if not specified                                                                                                                   |
+| requires-billing      |    No    | `true` if not specified                                                                                                                   |
+| transport             |    No    | must be one of `grpc`, `rest` or `both`. This value would only be used for generating .repo-metadata.json and relevant sections in README |
+
 
 Note that `cloud_prefix` is `cloud-` if `cloud_api` is `true`; empty otherwise.
 
diff --git a/library_generation/generate_composed_library.py b/library_generation/generate_composed_library.py
index dc094c0b1..5b503450a 100755
--- a/library_generation/generate_composed_library.py
+++ b/library_generation/generate_composed_library.py
@@ -72,13 +72,16 @@ def generate_composed_library(
         gapic_inputs = parse_build_file(build_file_folder, gapic.proto_path)
         # generate postprocessing prerequisite files (.repo-metadata.json, .OwlBot-hermetic.yaml,
         # owlbot.py) here because transport is parsed from BUILD.bazel,
-        # which lives in a versioned proto_path.
+        # which lives in a versioned proto_path. The value of transport will be
+        # overriden by the config object if specified. Note that this override
+        # does not affect library generation but instead used only for
+        # generating postprocessing files such as README.
         util.generate_postprocessing_prerequisite_files(
             config=config,
             library=library,
             proto_path=util.remove_version_from(gapic.proto_path),
-            transport=gapic_inputs.transport,
             library_path=library_path,
+            transport=library.get_transport(gapic_inputs),
         )
         temp_destination_path = f"java-{gapic.proto_path.replace('/','-')}"
         effective_arguments = __construct_effective_arg(
diff --git a/library_generation/model/generation_config.py b/library_generation/model/generation_config.py
index 8a2b0829c..87242fd1a 100644
--- a/library_generation/model/generation_config.py
+++ b/library_generation/model/generation_config.py
@@ -143,6 +143,7 @@ def from_yaml(path_to_yaml: str) -> GenerationConfig:
             ),
             recommended_package=__optional(library, "recommended_package", None),
             min_java_version=__optional(library, "min_java_version", None),
+            transport=__optional(library, "transport", None),
         )
         parsed_libraries.append(new_library)
 
diff --git a/library_generation/model/library_config.py b/library_generation/model/library_config.py
index 9855e4e83..f7992f47a 100644
--- a/library_generation/model/library_config.py
+++ b/library_generation/model/library_config.py
@@ -15,6 +15,7 @@
 from hashlib import sha256
 from typing import Optional
 from library_generation.model.gapic_config import GapicConfig
+from library_generation.model.gapic_inputs import GapicInputs
 
 
 MAVEN_COORDINATE_SEPARATOR = ":"
@@ -52,6 +53,7 @@ class LibraryConfig:
         extra_versioned_modules: Optional[str] = None,
         recommended_package: Optional[str] = None,
         min_java_version: Optional[int] = None,
+        transport: Optional[str] = None,
     ):
         self.api_shortname = api_shortname
         self.api_description = api_description
@@ -78,6 +80,7 @@ class LibraryConfig:
         self.recommended_package = recommended_package
         self.min_java_version = min_java_version
         self.distribution_name = self.__get_distribution_name(distribution_name)
+        self.transport = self.__validate_transport(transport)
 
     def get_library_name(self) -> str:
         """
@@ -101,6 +104,15 @@ class LibraryConfig:
         """
         return self.get_maven_coordinate().split(MAVEN_COORDINATE_SEPARATOR)[-1]
 
+    def get_transport(self, gapic_inputs: GapicInputs) -> str:
+        """
+        Returns the transport of the library. If directly set in library config, return it.
+        Otherwise, return the transport inferred from gapic_inputs. This value is only
+        used for postprocessing - the generation still infers the transport from BUILD
+        files.
+        """
+        return self.transport if self.transport is not None else gapic_inputs.transport
+
     def __get_distribution_name(self, distribution_name: Optional[str]) -> str:
         LibraryConfig.__check_distribution_name(distribution_name)
         if distribution_name:
@@ -109,6 +121,13 @@ class LibraryConfig:
         library_name = self.get_library_name()
         return f"{self.group_id}:google-{cloud_prefix}{library_name}"
 
+    def __validate_transport(self, transport: str):
+        if transport not in [None, "grpc", "rest", "both"]:
+            raise ValueError(
+                "allowed values for library.transport: grpc, rest, both and None"
+            )
+        return transport
+
     @staticmethod
     def __check_distribution_name(distribution_name: str) -> None:
         if not distribution_name:
@@ -144,6 +163,7 @@ class LibraryConfig:
             and self.extra_versioned_modules == other.extra_versioned_modules
             and self.recommended_package == other.recommended_package
             and self.min_java_version == other.min_java_version
+            and self.transport == other.transport
         )
 
     def __hash__(self):
@@ -175,6 +195,7 @@ class LibraryConfig:
                     self.extra_versioned_modules,
                     self.recommended_package,
                     self.min_java_version,
+                    self.transport,
                 ]
                 + [config.proto_path for config in self.gapic_configs]
             ).encode("utf-8")
diff --git a/library_generation/model/repo_config.py b/library_generation/model/repo_config.py
index 520c50582..58ed3fa3b 100644
--- a/library_generation/model/repo_config.py
+++ b/library_generation/model/repo_config.py
@@ -57,6 +57,12 @@ class RepoConfig:
         return self.library_versions.get(artifact_id, NEW_CLIENT_VERSION)
 
     def __parse_versions(self) -> dict[str, str]:
+        """
+        For a given versions.txt file (defined in self.versions_file)
+        creates a map of artifact-id to its version
+
+        :return: a map "artifact-id -> version"
+        """
         library_versions = dict()
         with open(self.versions_file) as f:
             for line in f.readlines():
diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml
index 5feee4c5c..2549eb199 100644
--- a/showcase/gapic-showcase/pom.xml
+++ b/showcase/gapic-showcase/pom.xml
@@ -149,7 +149,7 @@
     <dependency>
       <groupId>com.google.truth</groupId>
       <artifactId>truth</artifactId>
-      <version>1.4.3</version>
+      <version>1.4.4</version>
       <exclusions>
         <exclusion>
           <groupId>junit</groupId>
diff --git a/showcase/gapic-showcase/src/main/resources/META-INF/native-image/com.google.showcase.v1beta1/reflect-config.json b/showcase/gapic-showcase/src/main/resources/META-INF/native-image/com.google.showcase.v1beta1/reflect-config.json
index 27722fa93..3d3a14c9b 100644
--- a/showcase/gapic-showcase/src/main/resources/META-INF/native-image/com.google.showcase.v1beta1/reflect-config.json
+++ b/showcase/gapic-showcase/src/main/resources/META-INF/native-image/com.google.showcase.v1beta1/reflect-config.json
@@ -440,6 +440,24 @@
     "allDeclaredClasses": true,
     "allPublicClasses": true
   },
+  {
+    "name": "com.google.api.TypeReference",
+    "queryAllDeclaredConstructors": true,
+    "queryAllPublicConstructors": true,
+    "queryAllDeclaredMethods": true,
+    "allPublicMethods": true,
+    "allDeclaredClasses": true,
+    "allPublicClasses": true
+  },
+  {
+    "name": "com.google.api.TypeReference$Builder",
+    "queryAllDeclaredConstructors": true,
+    "queryAllPublicConstructors": true,
+    "queryAllDeclaredMethods": true,
+    "allPublicMethods": true,
+    "allDeclaredClasses": true,
+    "allPublicClasses": true
+  },
   {
     "name": "com.google.cloud.location.GetLocationRequest",
     "queryAllDeclaredConstructors": true,
diff --git a/showcase/pom.xml b/showcase/pom.xml
index 5e7ebb40d..a47a554e3 100644
--- a/showcase/pom.xml
+++ b/showcase/pom.xml
@@ -108,7 +108,7 @@
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-surefire-plugin</artifactId>
-            <version>3.3.0</version>
+            <version>3.3.1</version>
             <configuration>
               <reportNameSuffix>sponge_log</reportNameSuffix>
               <skipTests>${skipUnitTests}</skipTests>


Test patch:
diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/ServiceClientClassComposerTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/ServiceClientClassComposerTest.java
index 8efc43118..a6d057fe8 100644
--- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/ServiceClientClassComposerTest.java
+++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/ServiceClientClassComposerTest.java
@@ -40,20 +40,6 @@ class ServiceClientClassComposerTest {
     Assert.assertCodeEquals(goldenFilePath, visitor.write());
   }
 
-  @Test
-  void generateServiceClassesEmpty() {
-    GapicContext context = GrpcRestTestProtoLoader.instance().parseShowcaseEcho();
-    Service echoProtoService = context.services().get(1);
-    GapicClass clazz = ServiceClientClassComposer.instance().generate(context, echoProtoService);
-
-    JavaWriterVisitor visitor = new JavaWriterVisitor();
-    clazz.classDefinition().accept(visitor);
-    GoldenFileWriter.saveCodegenToFile(this.getClass(), "EchoEmpty.golden", visitor.write());
-    Path goldenFilePath =
-        Paths.get(GoldenFileWriter.getGoldenDir(this.getClass()), "EchoEmpty.golden");
-    Assert.assertCodeEquals(goldenFilePath, visitor.write());
-  }
-
   @Test
   void generateServiceClassesWicked() {
     GapicContext context = GrpcRestTestProtoLoader.instance().parseShowcaseWicked();
diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoEmpty.golden b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoEmpty.golden
deleted file mode 100644
index b423bbbb9..000000000
--- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpcrest/goldens/EchoEmpty.golden
+++ /dev/null
@@ -1,154 +0,0 @@
-package com.google.showcase.grpcrest.v1beta1;
-
-import com.google.api.core.BetaApi;
-import com.google.api.gax.core.BackgroundResource;
-import com.google.showcase.grpcrest.v1beta1.stub.EchoEmpyStub;
-import com.google.showcase.grpcrest.v1beta1.stub.EchoEmpyStubSettings;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import javax.annotation.Generated;
-
-// AUTO-GENERATED DOCUMENTATION AND CLASS.
-/**
- * This class provides the ability to make remote calls to the backing service through method calls
- * that map to API methods. Sample code to get started:
- *
- * <pre>{@code
- * // This snippet has been automatically generated and should be regarded as a code template only.
- * // It will require modifications to work:
- * // - It may require correct/in-range values for request initialization.
- * // - It may require specifying regional endpoints when creating the service client as shown in
- * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
- * try (EchoEmpyClient echoEmpyClient = EchoEmpyClient.create()) {}
- * }</pre>
- *
- * <p>Note: close() needs to be called on the EchoEmpyClient object to clean up resources such as
- * threads. In the example above, try-with-resources is used, which automatically calls close().
- *
- * <table>
- *    <caption>Methods</caption>
- *    <tr>
- *      <th>Method</th>
- *      <th>Description</th>
- *      <th>Method Variants</th>
- *    </tr>
- *  </table>
- *
- * <p>See the individual methods for example code.
- *
- * <p>Many parameters require resource names to be formatted in a particular way. To assist with
- * these names, this class includes a format method for each type of name, and additionally a parse
- * method to extract the individual identifiers contained within names that are returned.
- *
- * <p>This class can be customized by passing in a custom instance of EchoEmpySettings to create().
- * For example:
- *
- * <p>To customize credentials:
- *
- * <pre>{@code
- * // This snippet has been automatically generated and should be regarded as a code template only.
- * // It will require modifications to work:
- * // - It may require correct/in-range values for request initialization.
- * // - It may require specifying regional endpoints when creating the service client as shown in
- * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
- * EchoEmpySettings echoEmpySettings =
- *     EchoEmpySettings.newBuilder()
- *         .setCredentialsProvider(FixedCredentialsProvider.create(myCredentials))
- *         .build();
- * EchoEmpyClient echoEmpyClient = EchoEmpyClient.create(echoEmpySettings);
- * }</pre>
- *
- * <p>To customize the endpoint:
- *
- * <pre>{@code
- * // This snippet has been automatically generated and should be regarded as a code template only.
- * // It will require modifications to work:
- * // - It may require correct/in-range values for request initialization.
- * // - It may require specifying regional endpoints when creating the service client as shown in
- * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
- * EchoEmpySettings echoEmpySettings =
- *     EchoEmpySettings.newBuilder().setEndpoint(myEndpoint).build();
- * EchoEmpyClient echoEmpyClient = EchoEmpyClient.create(echoEmpySettings);
- * }</pre>
- *
- * <p>Please refer to the GitHub repository's samples for more quickstart code snippets.
- */
-@BetaApi
-@Generated("by gapic-generator-java")
-public class EchoEmpyClient implements BackgroundResource {
-  private final EchoEmpySettings settings;
-  private final EchoEmpyStub stub;
-
-  /** Constructs an instance of EchoEmpyClient with default settings. */
-  public static final EchoEmpyClient create() throws IOException {
-    return create(EchoEmpySettings.newBuilder().build());
-  }
-
-  /**
-   * Constructs an instance of EchoEmpyClient, using the given settings. The channels are created
-   * based on the settings passed in, or defaults for any settings that are not set.
-   */
-  public static final EchoEmpyClient create(EchoEmpySettings settings) throws IOException {
-    return new EchoEmpyClient(settings);
-  }
-
-  /**
-   * Constructs an instance of EchoEmpyClient, using the given stub for making calls. This is for
-   * advanced usage - prefer using create(EchoEmpySettings).
-   */
-  public static final EchoEmpyClient create(EchoEmpyStub stub) {
-    return new EchoEmpyClient(stub);
-  }
-
-  /**
-   * Constructs an instance of EchoEmpyClient, using the given settings. This is protected so that
-   * it is easy to make a subclass, but otherwise, the static factory methods should be preferred.
-   */
-  protected EchoEmpyClient(EchoEmpySettings settings) throws IOException {
-    this.settings = settings;
-    this.stub = ((EchoEmpyStubSettings) settings.getStubSettings()).createStub();
-  }
-
-  protected EchoEmpyClient(EchoEmpyStub stub) {
-    this.settings = null;
-    this.stub = stub;
-  }
-
-  public final EchoEmpySettings getSettings() {
-    return settings;
-  }
-
-  public EchoEmpyStub getStub() {
-    return stub;
-  }
-
-  @Override
-  public final void close() {
-    stub.close();
-  }
-
-  @Override
-  public void shutdown() {
-    stub.shutdown();
-  }
-
-  @Override
-  public boolean isShutdown() {
-    return stub.isShutdown();
-  }
-
-  @Override
-  public boolean isTerminated() {
-    return stub.isTerminated();
-  }
-
-  @Override
-  public void shutdownNow() {
-    stub.shutdownNow();
-  }
-
-  @Override
-  public boolean awaitTermination(long duration, TimeUnit unit) throws InterruptedException {
-    return stub.awaitTermination(duration, unit);
-  }
-}
diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java
index 93c4eb359..2776fec68 100644
--- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java
+++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java
@@ -692,6 +692,19 @@ class ParserTest {
     assertNull(parsedBookshopService.apiVersion());
   }
 
+  @Test
+  void parseServiceWithNoMethodsTest() {
+    FileDescriptor fileDescriptor =
+        com.google.api.service.without.methods.test.ServiceWithNoMethodsOuterClass.getDescriptor();
+    Map<String, Message> messageTypes = Parser.parseMessages(fileDescriptor);
+    Map<String, ResourceName> resourceNames = Parser.parseResourceNames(fileDescriptor);
+    List<com.google.api.generator.gapic.model.Service> services =
+        Parser.parseService(
+            fileDescriptor, messageTypes, resourceNames, Optional.empty(), new HashSet<>());
+    assertEquals(1, services.size());
+    assertEquals("EchoWithMethods", services.get(0).overriddenName());
+  }
+
   private void assertMethodArgumentEquals(
       String name, TypeNode type, List<TypeNode> nestedFields, MethodArgument argument) {
     assertEquals(name, argument.name());
diff --git a/gapic-generator-java/src/test/proto/echo_grpcrest.proto b/gapic-generator-java/src/test/proto/echo_grpcrest.proto
index ea4a9392f..e02b4e35d 100644
--- a/gapic-generator-java/src/test/proto/echo_grpcrest.proto
+++ b/gapic-generator-java/src/test/proto/echo_grpcrest.proto
@@ -163,11 +163,6 @@ service Echo {
   }
 }
 
-// Generator should not fail when encounter a service without methods
-service EchoEmpy {
-  option (google.api.default_host) = "localhost:7469";
-}
-
 // A severity enum used to test enum capabilities in GAPIC surfaces
 enum Severity {
   UNNECESSARY = 0;
diff --git a/gapic-generator-java/src/test/proto/service_with_no_methods.proto b/gapic-generator-java/src/test/proto/service_with_no_methods.proto
new file mode 100644
index 000000000..906ade5dc
--- /dev/null
+++ b/gapic-generator-java/src/test/proto/service_with_no_methods.proto
@@ -0,0 +1,131 @@
+// Copyright 2018 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.
+
+syntax = "proto3";
+
+import "google/api/annotations.proto";
+import "google/api/client.proto";
+import "google/api/field_behavior.proto";
+import "google/api/field_info.proto";
+import "google/api/resource.proto";
+import "google/longrunning/operations.proto";
+import "google/protobuf/duration.proto";
+import "google/protobuf/timestamp.proto";
+import "google/rpc/status.proto";
+
+package google.api.service.without.methods.test.v1;
+
+option java_package = "com.google.api.service.without.methods.test";
+option java_multiple_files = true;
+option java_outer_classname = "ServiceWithNoMethodsOuterClass";
+
+option (google.api.resource_definition) = {
+  type: "showcase.googleapis.com/AnythingGoes"
+  pattern: "*"
+};
+// This proto is used to test scenarios where a service does not have any rpc methods
+
+// This service is used as control group when it is not empty.
+service EchoWithMethods {
+  // This service is meant to only run locally on the port 7469 (keypad digits
+  // for "show").
+  option (google.api.default_host) = "localhost:7469";
+  option (google.api.oauth_scopes) =
+      "https://www.googleapis.com/auth/cloud-platform";
+
+  // This method simply echos the request. This method is showcases unary rpcs.
+  rpc EchoWithMethod(EchoRequest) returns (EchoResponse) {
+    option (google.api.http) = {
+      post: "/v1beta1/echo:echo"
+      body: "*"
+    };
+    option (google.api.method_signature) = "content";
+    option (google.api.method_signature) = "error";
+    option (google.api.method_signature) = "content,severity";
+    option (google.api.method_signature) = "name";
+    option (google.api.method_signature) = "parent";
+    option (google.api.method_signature) = "";
+  }
+}
+
+// This service is to test when no method specified.
+service EchoWithoutMethods {
+  // This service is meant to only run locally on the port 7469 (keypad digits
+  // for "show").
+  option (google.api.default_host) = "localhost:7469";
+  option (google.api.oauth_scopes) =
+    "https://www.googleapis.com/auth/cloud-platform";
+}
+
+// A severity enum used to test enum capabilities in GAPIC surfaces
+enum Severity {
+  UNNECESSARY = 0;
+  NECESSARY = 1;
+  URGENT = 2;
+  CRITICAL = 3;
+}
+
+message Foobar {
+  option (google.api.resource) = {
+    type: "showcase.googleapis.com/Foobar"
+    pattern: "projects/{project}/foobars/{foobar}"
+    pattern: "projects/{project}/chocolate/variants/{variant}/foobars/{foobar}"
+    pattern: "foobars/{foobar}"
+    pattern: "bar_foos/{bar_foo}/foobars/{foobar}"
+    pattern: "*"
+  };
+
+  string name = 1;
+  string info = 2;
+}
+
+// The request message used for the Echo, Collect and Chat methods.
+// If content or opt are set in this message then the request will succeed.
+// If status is set in this message
+// then the status will be returned as an error.
+message EchoRequest {
+  string name = 5 [
+    (google.api.resource_reference).type = "showcase.googleapis.com/Foobar",
+    (google.api.field_behavior) = REQUIRED
+  ];
+
+  string parent = 6 [
+    (google.api.resource_reference).child_type =
+        "showcase.googleapis.com/AnythingGoes",
+    (google.api.field_behavior) = REQUIRED
+  ];
+
+  oneof response {
+    // The content to be echoed by the server.
+    string content = 1;
+
+    // The error to be thrown by the server.
+    google.rpc.Status error = 2;
+  }
+
+  // The severity to be echoed by the server.
+  Severity severity = 3;
+
+  Foobar foobar = 4;
+}
+
+// The response message for the Echo methods.
+message EchoResponse {
+  // The content specified in the request.
+  string content = 1;
+
+  // The severity specified in the request.
+  Severity severity = 2;
+}
+
diff --git a/library_generation/test/resources/goldens/.repo-metadata-custom-transport-golden.json b/library_generation/test/resources/goldens/.repo-metadata-custom-transport-golden.json
new file mode 100644
index 000000000..2cccd4b88
--- /dev/null
+++ b/library_generation/test/resources/goldens/.repo-metadata-custom-transport-golden.json
@@ -0,0 +1,16 @@
+{
+  "api_shortname": "secretmanager",
+  "name_pretty": "Secret Management",
+  "product_documentation": "https://cloud.google.com/solutions/secrets-management/",
+  "api_description": "allows you to encrypt, store, manage, and audit infrastructure and application-level secrets.",
+  "client_documentation": "https://cloud.google.com/java/docs/reference/google-cloud-secretmanager/latest/overview",
+  "release_level": "preview",
+  "transport": "http",
+  "language": "java",
+  "repo": "googleapis/google-cloud-java",
+  "repo_short": "java-secretmanager",
+  "distribution_name": "com.google.cloud:google-cloud-secretmanager",
+  "api_id": "secretmanager.googleapis.com",
+  "library_type": "GAPIC_AUTO",
+  "requires_billing": true
+}
\ No newline at end of file
diff --git a/library_generation/test/utilities_unit_tests.py b/library_generation/test/utilities_unit_tests.py
index c38016fcb..7d9f09ec2 100644
--- a/library_generation/test/utilities_unit_tests.py
+++ b/library_generation/test/utilities_unit_tests.py
@@ -23,6 +23,7 @@ import contextlib
 from pathlib import Path
 from library_generation.utils import utilities as util
 from library_generation.model.gapic_config import GapicConfig
+from library_generation.model.gapic_inputs import GapicInputs
 from library_generation.model.generation_config import GenerationConfig
 from library_generation.model.library_config import LibraryConfig
 from library_generation.test.test_utils import FileComparator
@@ -57,6 +58,14 @@ common_protos = LibraryConfig(
     api_description="example description",
     gapic_configs=list(),
 )
+test_library_with_custom_transport = LibraryConfig(
+    api_shortname="secretmanager",
+    name_pretty="Secret Management",
+    product_documentation="https://cloud.google.com/solutions/secrets-management/",
+    api_description="allows you to encrypt, store, manage, and audit infrastructure and application-level secrets.",
+    gapic_configs=list(),
+    transport="rest",
+)
 
 
 class UtilitiesTest(unittest.TestCase):
@@ -225,6 +234,40 @@ class UtilitiesTest(unittest.TestCase):
         )
         self.__remove_postprocessing_prerequisite_files(path=library_path)
 
+    def test_generate_postprocessing_prerequisite_files__custom_transport_set_in_config__success(
+        self,
+    ):
+        """
+        This test generates files for `test_library_with_custom_transport`, which
+        has an explicit value for transport declared (http). This is expected to
+        override the value obtained in BUILD.bazel via gapic_inputs.parse(). For
+        testing purposes, we test with a default GapicInputs object, whose transport
+        is set to "grpc".
+        """
+        library_path = self.__setup_postprocessing_prerequisite_files(
+            combination=2, library=test_library_with_custom_transport
+        )
+
+        file_comparator.compare_files(
+            f"{library_path}/.repo-metadata.json",
+            f"{library_path}/.repo-metadata-custom-transport-golden.json",
+        )
+        self.__remove_postprocessing_prerequisite_files(path=library_path)
+
+    def test_create__library_invalid_transport__fails(
+        self,
+    ):
+
+        with self.assertRaises(ValueError):
+            test_library_with_invalid_transport = LibraryConfig(
+                api_shortname="secretmanager",
+                name_pretty="Secret Management",
+                product_documentation="https://cloud.google.com/solutions/secrets-management/",
+                api_description="allows you to encrypt, store, manage, and audit infrastructure and application-level secrets.",
+                gapic_configs=list(),
+                transport="http",
+            )
+
     def test_prepare_repo_monorepo_success(self):
         gen_config = self.__get_a_gen_config(2)
         repo_config = util.prepare_repo(
@@ -276,7 +319,8 @@ class UtilitiesTest(unittest.TestCase):
         library.library_type = library_type
         config = self.__get_a_gen_config(combination, library_type=library_type)
         proto_path = "google/cloud/baremetalsolution/v2"
-        transport = "grpc"
+        gapic_inputs = GapicInputs()  # defaults to transport=grpc
+        transport = library.get_transport(gapic_inputs)
         util.generate_postprocessing_prerequisite_files(
             config=config,
             library=library,