google-http-client-jackson2 cannot be excluded even when a JsonFactory is configured via FirebaseOptions

Open
#1,231 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
48/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
java
Domain
api, backend

Research direction

Start with ApiClientUtils.java and the FirebaseMessagingClientImpl.fromApp path shown in the stack trace, then reproduce the Maven exclusion and first-send failure using the FirebaseOptions configuration in the issue. Determine whether the configured JsonFactory is bypassed during request-factory or credentials initialization. Done means Jackson2 is no longer required for this configuration, or the hard requirement is documented.

Written by the indexing model from the issue text.

Description

Describe your environment
  • Operating System version: Amazon Linux 2023 (AWS ECS Fargate), also reproduced on macOS 15
  • Firebase SDK version: 9.7.1 (the code in question is unchanged on v9.10.0)
  • Library version: com.google.firebase:firebase-admin:9.7.1, Java 25, Spring Boot 4.1
  • Firebase Product: messaging (FCM only — no Firestore, Storage, Realtime Database or FirebaseAuth)
Describe the problem

google-http-client-jackson2 is a hard runtime requirement, and FirebaseOptions.setJsonFactory(...) is not sufficient to avoid it. Excluding the artifact still throws ClassNotFoundException on the first FCM send, which forces ~580 KB of Jackson 2 into deployments whose Firebase usage is otherwise entirely Gson-based.

Steps to reproduce:
  1. Configure FirebaseOptions with an explicit transport and JSON factory (see code below).
  2. Exclude com.google.http-client:google-http-client-jackson2 from the build.
  3. Start the application — startup succeeds, both FirebaseApp instances initialize fine.
  4. Send a message via FirebaseMessaging.sendEachForMulticast(...) — fails:
java.lang.ClassNotFoundException: com.google.api.client.json.jackson2.JacksonFactory
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
    at com.google.firebase.messaging.FirebaseMessagingClientImpl.fromApp(FirebaseMessagingClientImpl.java:193)
    at com.google.firebase.messaging.FirebaseMessaging$7.get(FirebaseMessaging.java:637)
    at com.google.firebase.messaging.FirebaseMessaging$7.get(FirebaseMessaging.java:634)
    at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers.java:201)
    at com.google.firebase.messaging.FirebaseMessaging.getMessagingClient(FirebaseMessaging.java:519)
    at com.google.firebase.messaging.FirebaseMessaging.sendOpForSendResponse(FirebaseMessaging.java:253)

The failure only surfaces on the first send, because getMessagingClient() is lazy and memoized — so a startup smoke test does not catch it.

The configured factory does appear to be honoured for messaging itself; something in the request-factory / credentials initialization path loads JacksonFactory regardless of configuration.

Relevant Code:
FirebaseOptions.builder()
    .setHttpTransport(new NetHttpTransport())
    .setJsonFactory(GsonFactory.getDefaultInstance())
    .setCredentials(GoogleCredentials.fromStream(serviceAccountJson))
    .build();

We use FCM only, and prune the parts of the SDK we do not need:

<dependency>
    <groupId>com.google.firebase</groupId>
    <artifactId>firebase-admin</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.google.cloud</groupId>
            <artifactId>*</artifactId>
        </exclusion>
        <exclusion>
            <groupId>io.netty</groupId>
            <artifactId>*</artifactId>
        </exclusion>
    </exclusions>
</dependency>
Why this matters

After that pruning, everything remaining is Gson-based. Credentials come from GoogleCredentials, and google-auth-library-oauth2-http uses GsonFactory (OAuth2Utils.JSON_FACTORY), which brings google-http-client-gson + gson 2.13.2 (283 KB) in at compile scope. Despite that, we must also ship:

+- com.google.http-client:google-http-client-jackson2:jar:runtime
|  \- com.fasterxml.jackson.core:jackson-core:jar:2.21.4:runtime   <- 580 KB, sole consumer

Jackson 2 is roughly twice the size of the Gson stack it duplicates, and nothing else on our classpath requires it.

This is amplified on Spring Boot 4.x, which moved to Jackson 3 (tools.jackson). Applications now carry both Jackson generations side by side, with Jackson 2 present solely to satisfy firebase-admin.

The blocker cited in the code may be stale
public static JsonFactory getDefaultJsonFactory() {
    // Force using the Jackson2 parser for this project for now. Eventually we should switch
    // to Gson, but there are some issues that are preventing this migration at the moment.
    // See https://github.com/googleapis/google-api-java-client/issues/1779 for details.
    return JacksonFactory.getDefaultInstance();
}

googleapis/google-api-java-client#1779 was closed on 2021-04-29 as triage rather than fixed, splitting into three sub-bugs. Per a 2023-03-03 comment on that issue from a Gson maintainer:

  • floating-point serialization — fixed
  • parser leniency — addressed by Gson's strict-parsing API
  • exception handling — tracked in googleapis/google-http-java-client#1353, which is a documentation issue

ApiClientUtils.java does not appear to have been revisited on this point since (subsequent commits touch transports and copyright headers).

Ask

Any of these would help:

  1. Make the Jackson2 dependency genuinely optional when a JsonFactory is supplied via FirebaseOptions.
  2. Switch the default to GsonFactory, if the 2021 blockers no longer apply.
  3. Failing both, document that google-http-client-jackson2 is a hard requirement — that alone would save others the investigation.
Dominant language
Java
Stars
620
Forks
305
Avg merge
3h 23m
Merged PRs (30d)
1

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from firebase/firebase-admin-java

All issues in firebase/firebase-admin-java

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.