How to migrate from the v1 SDK to the v2 SDK.
The Java v1 SDK was deprecated on 1 January 2024.
Differences between v1 and v2 SDKs
See the following links for more information on differences between the v1 and v2 SDKs.
Migration guides
You can follow one of these approaches to migrate from Java v1 to v2.
Placeholder values
Example code in this guide uses the following placeholder values. You should replace these placeholders with the following values:
| Placeholder | Replace with | From |
|---|---|---|
{projectKey} | project_key | your API Client |
{clientID} | client_id | your API Client |
{clientSecret} | secret | your API Client |
{scope} | scope | your API Client |
{region} | your Region | Hosts |
Manually migrate to v2 SDK
The following example code demonstrates how to perform an action using the Java v1 SDK, and the best practice for replicating this action with the Java v2 SDK.
Client configuration and creation
SphereClientFactory has been replaced by ApiRootBuilder.ApiRoot has been instantiated, you can create requests directly from it.final SphereClient sphereClient = SphereClientFactory
.of()
.createClient("{projectKey}", "{clientID}", "{clientSecret}");final ProjectApiRoot projectRoot = ApiRootBuilder
.of()
.defaultClient(
ClientCredentials
.of()
.withClientId("{clientID}")
.withClientSecret("{clientSecret}")
.build(),
ServiceRegion.GCP_EUROPE_WEST1
)
.build("{projectKey}");Timeout setting
executeBlocking() to set the timeout.PagedQueryResult<Category> response = blockingClient()
.executeBlocking(CategoryQuery.of(), 45, TimeUnit.SECONDS);CategoryPagedQueryResponse response = projectClient()
.categories()
.get()
.executeBlocking(Duration.ofSeconds(45))
.getBody();Exception handling
SphereClient throws the underlying exception when it occurs. In the v2 SDK, the ProjectRoot throws an CompletionException instead and the actual exception is wrapped inside it.ClientBuilder.withErrorMiddleware(ErrorMiddleware.ExceptionMode) method allows you to configure the middleware to unwrap the causing exception.final ProjectApiRoot projectRoot = ApiRootBuilder
.of()
.defaultClient(
ClientCredentials
.of()
.withClientId("{clientID}")
.withClientSecret("{clientSecret}")
.build(),
ServiceRegion.GCP_EUROPE_WEST1
)
.withErrorMiddleware(
ErrorMiddleware.ExceptionMode.UNWRAP_COMPLETION_EXCEPTION
)
.build("{projectKey}");Headers
HttpRequest class and in the v2 SDK the ApiHttpRequest class.ApiHttpRequest can be directly instantiated and it can set the type of method (GET or POST), the URI, the headers, and the body.// Not in SDK v1
final CartPagedQueryResponse carts = projectClient()
.carts()
.get()
.addHeader("foo", "bar")
.executeBlocking()
.getBody();Implement retry
The retry mechanisms are significantly different between the v1 SDK and the v2 SDK.
SphereClient, and then the request, in this case, a PagedQueryResultByProjectKeyRequestBuilder can be created with a built-in retry mechanism using the RetryMiddleware. It is also possible to set up additional parameters to the request, like the logger InternalLoggerFactory.final int maxAttempts = 5;
final List<RetryRule> retryRules = singletonList(
RetryRule.of(
RetryPredicate.ofMatchingStatusCodes(
BAD_GATEWAY_502,
SERVICE_UNAVAILABLE_503,
GATEWAY_TIMEOUT_504
),
RetryAction.ofExponentialBackoff(maxAttempts, 100, 2000)
)
);
final SphereClient client = RetrySphereClientDecorator.of(
sphereClient(),
retryRules
);
final PagedQueryResult<Category> categoryPagedQueryResult = client
.execute(CategoryQuery.of())
.toCompletableFuture()
.get();final ByProjectKeyRequestBuilder projectClient = ApiRootBuilder
.of()
.defaultClient(
ServiceRegion.GCP_EUROPE_WEST1.getApiUrl(),
ClientCredentials
.of()
.withClientId("{clientId}")
.withClientSecret("{clientSecret}")
.build(),
ServiceRegion.GCP_EUROPE_WEST1.getOAuthTokenUrl()
)
.withRetryMiddleware(5, Arrays.asList(502, 503, 504))
.buildForProject("{projectKey}");
final CategoryPagedQueryResponse body = projectClient
.categories()
.get()
.executeBlocking()
.getBody();Use DraftBuilders
In the v2 SDK there are not inheritances for DraftBuilder classes, but the differences are minor.
LocalizedString name = LocalizedString.ofEnglish("name");
LocalizedString slug = LocalizedString.ofEnglish("slug");
LocalizedString metaDescription = LocalizedString.ofEnglish("metaDescription");
LocalizedString metaTitle = LocalizedString.ofEnglish("metaTitle");
LocalizedString metaKeywords = LocalizedString.ofEnglish("metaKeywords");
CategoryDraft categoryDraft = CategoryDraftBuilder
.of(name, slug)
.metaDescription(metaDescription)
.metaTitle(metaTitle)
.metaKeywords(metaKeywords)
.externalId("externalId")
.build();CategoryDraft categoryDraft = CategoryDraft
.builder()
.name(LocalizedString.ofEnglish("name"))
.slug(LocalizedString.ofEnglish("slug"))
.externalId("externalId")
.metaTitle(LocalizedString.ofEnglish("metaTitle"))
.metaDescription(LocalizedString.ofEnglish("metaDescription"))
.metaKeywords(LocalizedString.ofEnglish("metaKeywords"))
.build();Create resources
post() method.LocalizedString name = LocalizedString.ofEnglish("name");
LocalizedString slug = LocalizedString.ofEnglish("slug");
CategoryDraft categoryDraft = CategoryDraftBuilder.of(name, slug).build();
Category category = blockingClient()
.executeBlocking(CategoryCreateCommand.of(categoryDraft));CategoryDraft categoryDraft = CategoryDraft
.builder()
.name(LocalizedString.ofEnglish("name"))
.slug(LocalizedString.ofEnglish("slug"))
.build();
Category category = projectClient()
.categories()
.post(categoryDraft)
.executeBlocking()
.getBody();Create resources from JSON
JsonUtils class replaces the SphereJsonUtils class.final CategoryDraft categoryDraft = SphereJsonUtils.readObjectFromResource(
"category.json",
CategoryDraft.class
);
final Category category = blockingClient()
.executeBlocking(CategoryCreateCommand.of(categoryDraft));final CategoryDraft categoryDraft = JsonUtils.fromJsonString(
TestUtils.stringFromResource("category.json"),
CategoryDraft.class
);
final Category category = projectClient()
.categories()
.post(categoryDraft)
.executeBlocking()
.getBody();Update resources
UpdateBuilder is used to create the type of update action to apply in the post() method.LocalizedString newName = LocalizedString.ofEnglish("new name");
CategoryUpdateCommand command = CategoryUpdateCommand.of(
category,
Collections.singletonList(ChangeName.of(newName))
);
Category updatedCategory = blockingClient().executeBlocking(command);CategoryUpdate categoryUpdate = CategoryUpdate
.builder()
.version(category.getVersion())
.plusActions(actionBuilder ->
actionBuilder
.changeNameBuilder()
.name(LocalizedString.ofEnglish("new name"))
)
.build();
Category updatedCategory = projectClient()
.categories()
.withId(category.getId())
.post(categoryUpdate)
.executeBlocking()
.getBody();Get a resource by ID
.withId() before .get(), after which additional filters or requests (such as .withExpand()) can be included before the request is executed.Category loadedCategory = blockingClient()
.executeBlocking(
CategoryByIdGet.of(category.getId()).withExpansionPaths(m -> m.parent())
);Category queriedCategory = projectClient()
.categories()
.withId(category.getId())
.get()
.withExpand("parent")
.executeBlocking()
.getBody();Query resources
ResourcePagedQueryResponse (as opposed to PagedQueryResult in the v1 SDK). ResourcePagedQueryResponse can apply the limit, count, total, offset, and result to the query.PagedQueryResult<Category> pagedQueryResult = blockingClient()
.executeBlocking(CategoryQuery.of().byId("id123"));CategoryPagedQueryResponse response = projectClient()
.categories()
.get()
.withWhere("id = :id", "id", "id123")
.executeBlocking()
.getBody();
// Using query DSL builder
CategoryPagedQueryResponse response = projectClient()
.categories()
.get()
.withWhere(CategoryQueryBuilderDsl.of().id().is("id123"))
.executeBlocking()
.getBody();Access Attribute values
AttributeAccessor with the correct type.final String attributeValue = product
.getMasterData()
.getStaged()
.getMasterVariant()
.getAttribute("test")
.getValueAsString();final String attributeValue = product
.getMasterData()
.getStaged()
.getMasterVariant()
.getAttribute("products")
.withAttribute(AttributeAccessor::asString);AttributeAccessor in the v2 SDK, see the Attribute tests.Compatibility layer for Java v1 SDK
When you use the compatibility layer, the v1 SDK adapts its internal logic, so you don't need to change your whole business logic. Use this flexibility to gradually migrate to the v2 SDK.
Use Requests from the Java v1 SDK
import com.commercetools.api.defaultconfig.ApiRootBuilder;
import com.commercetools.api.models.project.Project;
import io.sphere.sdk.projects.queries.ProjectGet;
import io.vrap.rmf.base.client.ApiHttpClient;
import io.vrap.rmf.base.client.oauth2.ClientCredentials;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
public class CompatClientUsageTest {
@Test
public void compatClient() {
final ApiHttpClient apiHttpClient = ApiRootBuilder.of()
.defaultClient(ClientCredentials.of()
.withClientId(CommercetoolsTestUtils.getClientId())
.withClientSecret(CommercetoolsTestUtils.getClientSecret())
.build())
.buildClient();
final String projectKey = CommercetoolsTestUtils.getProjectKey();
CompatClient client = CompatClient.of(apiHttpClient, projectKey);
Project project = client.executeBlocking(ProjectGet.of(), Project.class).getBody();
Assertions.assertThat(project).isInstanceOf(Project.class);
Assertions.assertThat(project.getKey()).isEqualTo(projectKey);
}
}
Create a SphereClient with the Java v2 SDK
public class CompatSphereClientUsageTest {
@Test
public void sphereCompatClient()
throws ExecutionException, InterruptedException {
SphereClientConfig clientConfig = SphereClientConfig.of(
CommercetoolsTestUtils.getProjectKey(),
CommercetoolsTestUtils.getClientId(),
CommercetoolsTestUtils.getClientSecret()
);
SphereClient client = CompatSphereClient.of(clientConfig);
Project project = client
.execute(ProjectGet.of())
.toCompletableFuture()
.get();
Assertions.assertThat(project).isInstanceOf(Project.class);
}
}