Upgrade Java SDK V1 to V2

Updated at:

OSS Java SDK V2 is a rewrite of V1 (aliyun-sdk-oss). The changes are concentrated in package names and Maven coordinates, client creation, and API invocation patterns: clients are created through a static factory and require a region, and every operation accepts a typed request object. V2 uses V4 signatures by default and provides an asynchronous client. This topic describes the breaking changes that you must handle when migrating from V1 to V2 and provides an end-to-end migration process.

Assess the scope of changes

Before you migrate, assess the scope of changes to your existing code based on the following three categories: blocking items that surface at compile time, behavior changes that still compile, and optional new capabilities. The three tables in this section are the definitive list of V1/V2 differences. The remaining sections describe only the migration actions and do not repeat the differences.

Compile-time blocking items

These changes directly cause compilation failures. The compiler can locate them one by one, so the scope of changes is relatively certain.

Item

V1

V2

Step

Maven coordinates

com.aliyun.oss:aliyun-sdk-oss

com.aliyun:alibabacloud-oss-v2

Step 1

Additional JAXB dependencies

Manually required on Java 9 or later

Not required

Step 1

Java version requirement

Java 7 or later

Java 8 or later

Migration preparation

Package name

com.aliyun.oss and its subpackages

com.aliyun.sdk.service.oss2 and its subpackages; all imports must be rewritten

Step 2

Client creation

new OSSClientBuilder().build(endpoint, ak, sk), OSSClientBuilder.create()

OSSClient.newBuilder() static factory; region and credentialsProvider are required parameters

Step 3

Client configuration

ClientBuilderConfiguration configuration object

Chained builder methods

Step 3

API input

Multiple overloads with simple parameters for the same operation

A single typed request object (such as ListBucketsRequest), with optional OperationOptions to override the behavior of a single request

Step 4

API output

OSSObject, List<Bucket>, and so on, read by using getters

Result objects such as GetObjectResult and ListBucketsResult, read by using accessor methods named after the fields, such as result.buckets() and info.name()

Step 4

Exception model

OSSException (server-side) and ClientException (client-side), both extending RuntimeException, caught separately

Catch Exception and use ServiceException.asCause(e) to determine and extract server-side error information

Step 5

Resource release

shutdown()

Implements AutoCloseable and supports try-with-resources

Step 3

The output type change is the only item that requires additional manual verification: a type mismatch causes a compile-time error, but whether fields are read correctly cannot be fully exposed at compile time. You must verify this item by item in Step 6 based on core paths.

Runtime behavior changes

These changes do not block compilation, but they change how requests are authenticated and which domain names they target. The consequences of missing a change appear only at runtime.

Item

V1

V2

Step

Signature algorithm

V1 signatures by default; to use V4 signatures, explicitly set ClientBuilderConfiguration.setSignatureVersion(SignVersion.V4) and specify region

signatureVersion defaults to v4, so region becomes a required parameter

Step 3

Region and endpoint

endpoint is required

region is required; endpoint is optional and is derived from region when not specified

Step 3

Optional new capabilities

The following two capabilities are non-breaking additions. You can adopt them as needed; migration is complete without them.

Item

V1

V2

Step

Asynchronous capability

No separate asynchronous client

OSSAsyncClient, created through OSSAsyncClient.newBuilder(); operations return CompletableFuture and are suitable for concurrently processing multiple independent operations

Step 3 (optional)

List pagination

Manually maintain a marker loop to pull pages

Paginator interfaces, such as client.listBucketsPaginator(...) returning ListBucketsIterable

Step 4 (optional)

Migration preparation

Confirm the following five items once before you start, so that you do not have to redo work midway due to missing environment or information.

  • Build environment: Confirm that the JDK version in your build and runtime environments meets the V2 requirement, which is Java 8 or later.

  • Region information: Confirm the region to which the bucket belongs. region is a required parameter in V2.

  • Credential configuration: Both V1 and V2 read long-term credentials from the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables, and also read OSS_SESSION_TOKEN in STS scenarios. The environment variable names are the same in both versions, so no deployment configuration changes are required. If your existing code uses credentials such as RAMRoleARN, ECSRAMRole, or OIDCRoleARN, V2 requires a custom credentials provider implemented together with the credentials-java library. Include this development effort when you estimate the workload.

  • Call-site checklist: Identify every place in your existing code that calls the OSS SDK, and record the interfaces and features in use as the verification checklist for Step 6.

  • Test environment: Prepare a test environment in which you can run the migrated code, and complete verification before you release it.

Migration path: V1 and V2 have different package names, so they can coexist in the same project without class conflicts. Therefore, two migration paths are supported:

  • One-time full replacement (default): suitable for projects where OSS call sites are concentrated and regression verification can be completed in one pass. In Step 1, remove the V1 dependency directly, and the subsequent steps cover all call sites in the project.

  • Module-by-module canary migration: suitable for projects where call sites are scattered and cannot be regressed all at once. In Step 1, keep both the V1 and V2 dependencies temporarily and rewrite only the code in the current migration scope. Remove the V1 dependency after all modules are migrated.

Complete migration steps

Step 1: Update the Maven dependency

Replace the dependency coordinates in pom.xml. The V1 dependency is as follows:

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.18.4</version>
</dependency>

For the V2 dependency, replace <version> with the latest version number that you find on Maven Repository:

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>alibabacloud-oss-v2</artifactId>
    <version><!-- Enter the latest version number --></version>
</dependency>

V1 additionally declares JAXB-related dependencies for Java 9 or later (javax.xml.bind:jaxb-api, javax.activation:activation, and org.glassfish.jaxb:jaxb-runtime). V2 no longer needs them, so you can remove them as well.

Step 2: Change package names and imports

Replace imports of com.aliyun.oss and its subpackages with the corresponding types under com.aliyun.sdk.service.oss2. The following table lists the mappings for commonly used packages:

V1 package or type

V2 package or type

com.aliyun.oss.OSS, com.aliyun.oss.OSSClient

com.aliyun.sdk.service.oss2.OSSClient

com.aliyun.oss.OSSClientBuilder

com.aliyun.sdk.service.oss2.OSSClientBuilder; clients are created uniformly through the OSSClient.newBuilder() static factory, so the Step 3 example only needs to import OSSClient

com.aliyun.oss.common.auth.* (such as DefaultCredentialProvider)

com.aliyun.sdk.service.oss2.credentials.*: to pass an AccessKey explicitly, use StaticCredentialsProvider; to read from environment variables, use EnvironmentVariableCredentialsProvider

com.aliyun.oss.model.*

com.aliyun.sdk.service.oss2.models.*

com.aliyun.oss.OSSException, com.aliyun.oss.ClientException

com.aliyun.sdk.service.oss2.exceptions.ServiceException

com.aliyun.oss.ClientBuilderConfiguration

Client configuration is provided directly as chained methods on OSSClient.newBuilder(); there is no separate configuration class

V2 groups its model classes in the models package. When you replace imports in bulk, import com.aliyun.sdk.service.oss2.models.* first, and then add the missing types one by one based on the compilation errors.

Step 3: Change the client creation code

Determine the region and endpoint

region is required in all scenarios and must be the region in which the bucket resides. Whether to keep endpoint depends on the access scenario. The default path is to set only region and not pass endpoint, so that the access domain name is derived from region.

Access scenario

**Is endpoint required?**

Switch method to set

Default public access (recommended)

No; derived from region

None

Custom domain name

Yes; specify the custom domain name

useCName

Internal endpoint

Yes; specify the internal endpoint

useInternalEndpoint

Transfer acceleration endpoint

Yes; specify the transfer acceleration endpoint

useAccelerateEndpoint

Replace the client creation code

V1 requires you to construct a credentials provider and a client configuration object, and to declare V4 signatures explicitly. The following V1 example uses the OSSClientBuilder.create() chained form. Existing code that uses new OSSClientBuilder().build(endpoint, ak, sk) also converges to the V2 form after migration. In the example code, replace <region-id> with the region in which the bucket resides, such as cn-hangzhou, and replace <endpoint> with the access domain name of the bucket. V1 requires this parameter.

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;

String accessKeyId = System.getenv("OSS_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("OSS_ACCESS_KEY_SECRET");
String region = "<region-id>";
String endpoint = "<endpoint>";

DefaultCredentialProvider provider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);

ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
// V1 requires V4 signatures to be declared explicitly
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);

OSS ossClient = OSSClientBuilder.create()
        .credentialsProvider(provider)
        .clientConfiguration(clientBuilderConfiguration)
        .region(region)
        .endpoint(endpoint)
        .build();

// Perform OSS operations...

ossClient.shutdown();

V2 creates the client through a static factory. region is required and V4 signatures are used by default. Specify endpoint only in the scenarios listed in the preceding table. OSSClient implements AutoCloseable, so try-with-resources releases resources automatically:

import com.aliyun.sdk.service.oss2.OSSClient;
import com.aliyun.sdk.service.oss2.credentials.CredentialsProvider;
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;

CredentialsProvider provider = new EnvironmentVariableCredentialsProvider();

try (OSSClient client = OSSClient.newBuilder()
        .credentialsProvider(provider)
        .region("<region-id>")
        .build()) {

    // Perform OSS operations...

}

Replace credentials provider type names according to the mappings in Step 2. The V2 example above uses EnvironmentVariableCredentialsProvider to read credentials directly from environment variables. The credential source is the same as the V1 example, which reads environment variables first and then wraps them in DefaultCredentialProvider. To pass an AccessKey explicitly from code, use StaticCredentialsProvider instead.

Creating and destroying a client is expensive. Reuse a single instance in high-frequency scenarios.

Important

If you reuse a V2 client with the singleton pattern, you cannot rely on try-with-resources. You must call close() explicitly before the application exits. Otherwise, resources leak.

Migrate client configurations and the retry policy

V1 sets parameters such as timeouts, retries, and proxies on a ClientConfiguration or ClientBuilderConfiguration object. V2 raises these parameters to chained methods on OSSClient.newBuilder(), such as connectTimeout, readWriteTimeout, retryMaxAttempts, retryer, proxyHost, useCName, useInternalEndpoint, useAccelerateEndpoint, disableSsl, and usePathStyle. For the full list of configurations, see the client configuration content of the V2 SDK documentation.

Retries are an optional tuning item. V2 uses StandardRetryer by default, with a maximum of 3 attempts, a maximum backoff time of 20 seconds, a base delay of 0.2 seconds, and the FullJitter backoff algorithm. To adjust the policy, build a custom retryer by using StandardRetryer.newBuilder() and pass it to retryer(...). To disable retries, pass NopRetryer.

Optional: Switch to the asynchronous client

If your workload needs to execute multiple independent operations concurrently, switch to the asynchronous client. Operations return CompletableFuture:

import com.aliyun.sdk.service.oss2.OSSAsyncClient;
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
import com.aliyun.sdk.service.oss2.models.*;

import java.util.concurrent.CompletableFuture;

try (OSSAsyncClient client = OSSAsyncClient.newBuilder()
        .credentialsProvider(new EnvironmentVariableCredentialsProvider())
        .region("<region-id>")
        .build()) {

    CompletableFuture<ListBucketsResult> future = client.listBucketsAsync(
            ListBucketsRequest.newBuilder().build());

    future.thenAccept(result -> {
        for (BucketSummary info : result.buckets()) {
            System.out.printf("bucket: name:%s, region:%s%n", info.name(), info.region());
        }
    });

    future.join();
}

Step 4: Change the API input and output forms

V2 consolidates the input of every operation into a request object and the output into a result object. This section uses listing buckets to demonstrate the general migration pattern. Migrate other operations in the same way.

V1:

List<Bucket> buckets = ossClient.listBuckets();
for (Bucket bucket : buckets) {
    System.out.println(bucket.getName());
}

V2:

import com.aliyun.sdk.service.oss2.models.*;

ListBucketsResult result = client.listBuckets(
        ListBucketsRequest.newBuilder().build());

for (BucketSummary info : result.buckets()) {
    System.out.printf("bucket: name:%s, region:%s, storageClass:%s%n",
            info.name(), info.region(), info.storageClass());
}

Note the following three differences when you rewrite the code. First, input: even if an operation takes no parameters, you must construct a request object by using XxxRequest.newBuilder().build(). Second, output: read result fields by using accessor methods that are named after the fields (result.buckets(), info.name()) instead of the V1 getXxx() getters. Third, pagination: V1 requires you to maintain a marker loop to pull pages manually, whereas V2 provides paginator interfaces. For example, client.listBucketsPaginator(...) returns ListBucketsIterable, which you can iterate over directly to get all pages:

import com.aliyun.sdk.service.oss2.paginator.ListBucketsIterable;

ListBucketsIterable paginator = client.listBucketsPaginator(
        ListBucketsRequest.newBuilder().build());

for (ListBucketsResult page : paginator) {
    for (BucketSummary info : page.buckets()) {
        System.out.println(info.name());
    }
}

Step 5: Change the exception handling logic

V1 requires you to catch server-side and client-side exceptions separately. Both extend RuntimeException:

try {
    ossClient.putObject(...);
} catch (OSSException oe) {
    // The request reached the server but was rejected: insufficient permissions, invalid parameters, resource not found, and so on
    System.out.println("Error Code: " + oe.getErrorCode());
    System.out.println("Request ID: " + oe.getRequestId());
} catch (ClientException ce) {
    // Client-side issues: network unavailable, request timeout, certificate validation failure, and so on
    System.out.println("Error Message: " + ce.getMessage());
} finally {
    ossClient.shutdown();
}

In V2, catch the error uniformly with catch (Exception e), use ServiceException.asCause(e) to determine whether it is a server-side error, and if so read information such as requestId() and errorCode(); otherwise handle it as a client-side error:

import com.aliyun.sdk.service.oss2.exceptions.ServiceException;

try {
    client.putObject(...);
} catch (Exception e) {
    ServiceException se = ServiceException.asCause(e);
    if (se != null) {
        System.out.printf("ServiceException: requestId:%s, errorCode:%s%n",
                se.requestId(), se.errorCode());
    } else {
        System.out.printf("error:%n%s", e);
    }
}

Existing error code branches based on OSSException.getErrorCode() (such as SignatureDoesNotMatch, AccessDenied, and NoSuchKey) can be moved directly to ServiceException.errorCode(). The error code values remain the same. When you use the asynchronous client, exceptions are obtained through the CompletableFuture.exceptionally() callback, and server-side information is also extracted through ServiceException.asCause(e).

Step 6: Test and verify

Verify the migration results in the order shown in the following table. Proceed to the next item only after the current item meets its pass criteria.

Verification item

Method

Pass criteria

Compilation verification

Compile the project and search for remaining V1 code patterns

The build succeeds, and no com.aliyun.oss imports or V1 call patterns remain

Core-path field verification

In the test environment, cover the call-site checklist recorded during migration preparation, and focus on whether result fields are read correctly in core paths such as upload, download, list, and delete

Result fields are read correctly in all paths.

Note

The return type change is a risk that compilation cannot fully expose. Verify it item by item manually.

Authentication and domain verification

Confirm that every client sets region; if you use a custom domain name, an internal endpoint, or a transfer acceleration endpoint, verify the corresponding endpoint and switch parameters

Requests pass authentication with V4 signatures, and the domain and switch settings match the scenario decision table in Step 3

Asynchronous path verification

If you introduced the asynchronous client, trigger both the normal completion path and the exception path

CompletableFuture executes both the normal completion and exception callbacks as expected

Canary observation

Perform a canary release and observe the error code distribution and the number of retries

Confirm that no anomalies occur, and then switch all traffic

Rollback

If a problem is found during the canary period, you can roll back by using the coexistence capability of the two versions: restore the V1 dependency in pom.xml, and revert the imports, client creation, API calls, and exception handling code that were rewritten in the current batch. The rollback scope matches the scope of the current batch.

References

  • For the installation, client configuration, access credentials, and sample code of V2, see OSS Java SDK V2.

  • For the installation, client configuration, and exception handling of V1, see OSS Java SDK V1.