Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Play Framework 3.0.5 and Apache Pekko 1.0.3 Upgrade

## Overview

This document describes the upgrade of cert-service from Play Framework 2.7.2 with Akka 2.5.22 to Play Framework 3.0.5 with Apache Pekko 1.0.3.

## Why This Upgrade

1. License Compliance: Akka changed from Apache 2.0 to Business Source License 1.1, requiring commercial licenses for production use. Apache Pekko maintains Apache 2.0 license.
2. Security: Play 2.7.2 and Akka 2.5.22 no longer receive security updates.
3. Modernization: Access to latest features and performance improvements.

## Technology Stack Changes

- Play Framework: 2.7.2 to 3.0.5
- Actor Framework: Akka 2.5.22 to Apache Pekko 1.0.3
- Scala: 2.11.12 to 2.13.12
- Java: 8 to 11
- Guice: 3.0 to 5.1.0
- SLF4J: 1.6.1 to 2.0.9
- Logback: 1.0.7 to 1.4.14
- Jackson: 2.9.9 to 2.14.3
- Netty: 4.1.44 to 4.1.93

## Key Changes

### Dependencies

All Maven POM files updated with new versions. Play Framework groupId changed from com.typesafe.play to org.playframework. Scala library exclusions added to prevent version conflicts between Scala 2.11 and 2.13.

### Source Code

Package name updates across all Java files:
- akka.actor to org.apache.pekko.actor
- akka.pattern to org.apache.pekko.pattern
- akka.routing to org.apache.pekko.routing
- akka.util to org.apache.pekko.util
- akka.testkit to org.apache.pekko.testkit
- play.libs.akka to play.libs.pekko
- scala.compat.java8.FutureConverters to scala.jdk.javaapi.FutureConverters

### Play 3.0 API Updates

- ActorStartModule: Implements play.libs.pekko.PekkoGuiceSupport
- OnRequestHandler: Updated to use Http.Request parameter
- HealthController: Added Http.Request parameter to controller methods
- Routes: Updated to include Http.Request parameter

### Configuration

application.conf files updated with Pekko namespaces:
- akka namespace to pekko namespace
- Actor system configurations migrated
- Serialization bindings updated
- Dispatcher references changed

### Scala Version Conflicts

Added exclusions to prevent Scala 2.11 transitive dependencies:
- Excluded jackson-module-scala_2.11 from cert-processor cloud-store-sdk dependency
- Excluded scala-library and scala-reflect from all-actors and es-utils dependencies
- Explicitly declared scala-library 2.13.12 dependency

## Build Instructions

Build all modules:
```
mvn clean install -Dmaven.test.skip=true
```

Build with test compilation:
```
mvn clean install -DskipTests
```

Create distribution package:
```
cd service
mvn play2:dist
```

## Migration Impact

Business Logic: No changes to business logic or functionality
API Compatibility: Maintained, as Pekko is API-compatible with Akka 2.6
Code Changes: Primarily package name updates from akka to pekko
License: Now compliant with Apache 2.0 throughout the stack

## Testing Recommendations

1. Execute full unit test suite
2. Run integration tests for actor communication
3. Perform regression testing for all features
4. Conduct performance benchmarking
5. Test under production-like load

## Known Issues

Scala 2.11/2.13 Conflict: If you encounter NoClassDefFoundError for scala.collection.GenMap or scala.Serializable, verify dependency tree to ensure no Scala 2.11 artifacts are present. Run mvn dependency:tree and add exclusions for any scala-library or scala-reflect with version 2.11.

Jackson Module: The distribution must include jackson-module-scala_2.13 only. The jackson-module-scala_2.11 dependency is excluded from cert-processor to prevent ServiceLoader conflicts.
21 changes: 13 additions & 8 deletions all-actors/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,33 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.1</version>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_${scala.major.version}</artifactId>
<version>${akka.x.version}</version>
<groupId>org.apache.pekko</groupId>
<artifactId>pekko-actor_${scala.major.version}</artifactId>
<version>${pekko.version}</version>
</dependency>
<dependency>
<groupId>org.sunbird.incredible</groupId>
<artifactId>cert-processor</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-testkit_${scala.major.version}</artifactId>
<version>2.5.16</version>
<groupId>org.apache.pekko</groupId>
<artifactId>pekko-testkit_${scala.major.version}</artifactId>
<version>${pekko.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
Expand Down
2 changes: 1 addition & 1 deletion all-actors/src/main/java/org/sunbird/BaseActor.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.sunbird;

import akka.actor.UntypedAbstractActor;
import org.apache.pekko.actor.UntypedAbstractActor;
import org.sunbird.incredible.processor.JsonKey;
import org.sunbird.message.IResponseMessage;
import org.sunbird.message.Localizer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ private BaseStorageService getStorageService() {
storageEndpoint = Option.apply(certVar.getCloudStorageEndpoint());
storageRegion = Option.apply("");
storageConfig = new StorageConfig(certVar.getCloudStorageType(), storageKey, storageSecret,storageEndpoint,storageRegion);
logger.info(null, "CertificateGeneratorActor:getStorageService:storage object formed: {}" ,storageConfig.toString());
logger.info(null, "CertificateGeneratorActor:getStorageService:storage object formed");
return StorageServiceFactory.getStorageService(storageConfig);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.sunbird.cert.actor;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.javadsl.TestKit;
import org.apache.pekko.actor.ActorRef;
import org.apache.pekko.actor.ActorSystem;
import org.apache.pekko.actor.Props;
import org.apache.pekko.testkit.javadsl.TestKit;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
Expand All @@ -27,7 +27,6 @@
import org.sunbird.CertMapper;
import org.sunbird.incredible.processor.views.SvgGenerator;
import org.sunbird.cloud.storage.BaseStorageService;
import org.sunbird.cloud.storage.factory.StorageConfig;
import org.sunbird.cloud.storage.factory.StorageServiceFactory;
import org.sunbird.message.Localizer;
import org.sunbird.message.ResponseCode;
Expand Down Expand Up @@ -121,12 +120,11 @@ private void mock(Request request) throws Exception {
StoreConfig azureStoreConfig = PowerMockito.mock(StoreConfig.class);
PowerMockito.when(azureStoreConfig.getAccount()).thenReturn("Mockito.anyString()");
PowerMockito.when(azureStoreConfig.getKey()).thenReturn("Mockito.anyString()");
StorageConfig storageConfig = PowerMockito.mock(StorageConfig.class);
PowerMockito.when(storeParams.getType()).thenReturn("Mockito.anyString()");
PowerMockito.whenNew(StorageConfig.class).withArguments(Mockito.anyString(),Mockito.anyString(),Mockito.anyString(),Option.apply(Mockito.anyString()),Option.apply(Mockito.anyString())).thenReturn(storageConfig);
// Avoid mocking StorageConfig directly to prevent scala.Serializable compilation issues
BaseStorageService storageService = PowerMockito.mock(BaseStorageService.class);
PowerMockito.mockStatic(StorageServiceFactory.class);
PowerMockito.when(StorageServiceFactory.getStorageService(Mockito.any(StorageConfig.class))).thenReturn(storageService);
// Use withAnyArguments to avoid scala.Serializable compilation issues with StorageConfig parameter
PowerMockito.when(StorageServiceFactory.class, "getStorageService", Mockito.any()).thenReturn(storageService);
PowerMockito.when(certStore.save(Mockito.any(File.class), Mockito.anyString())).thenReturn("Mockito.anyString()");
PowerMockito.when(certStore.getPublicLink(Mockito.any(File.class), Mockito.anyString())).thenReturn("Mockito.anyString()");
File file2 = PowerMockito.mock(File.class);
Expand Down
4 changes: 4 additions & 0 deletions cert-processor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_2.11</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
Expand Down
20 changes: 11 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,26 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<jackson.version>2.9.9</jackson.version>
<akka.x.version>2.5.22</akka.x.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<jackson.version>2.14.3</jackson.version>
<pekko.version>1.0.3</pekko.version>
<junit.version>4.12</junit.version>

<version.compiler.plugin>2.3.1</version.compiler.plugin>
<scoverage.plugin.version>1.1.1</scoverage.plugin.version>
<slf4j.version>1.6.1</slf4j.version>
<logback.version>1.0.7</logback.version>
<slf4j.version>2.0.9</slf4j.version>
<logback.version>1.4.14</logback.version>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<play2.version>2.7.2</play2.version>
<scala.major.version>2.11</scala.major.version>
<scala.version>2.11.12</scala.version>
<play2.version>3.0.5</play2.version>
<scala.major.version>2.13</scala.major.version>
<scala.version>2.13.12</scala.version>
<powermock.version>1.7.4</powermock.version>
<httpClient.version>4.5.1</httpClient.version>
<jacoco.maven.plugin.version>0.8.5</jacoco.maven.plugin.version>
<guice.version>5.1.0</guice.version>
<netty.version>4.1.93.Final</netty.version>
</properties>

<modules>
Expand Down
2 changes: 1 addition & 1 deletion service/app/controllers/BaseController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;

import akka.actor.ActorRef;
import org.apache.pekko.actor.ActorRef;

import com.fasterxml.jackson.databind.JsonNode;

Expand Down
12 changes: 6 additions & 6 deletions service/app/controllers/RequestHandler.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package controllers;

import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.pattern.Patterns;
import akka.util.Timeout;
import org.apache.pekko.actor.ActorRef;
import org.apache.pekko.actor.ActorSelection;
import org.apache.pekko.pattern.Patterns;
import org.apache.pekko.util.Timeout;
import org.apache.commons.lang3.StringUtils;
import org.sunbird.incredible.processor.JsonKey;
import org.sunbird.BaseException;
Expand All @@ -18,7 +18,7 @@
import play.libs.Json;
import play.mvc.Result;
import play.mvc.Results;
import scala.compat.java8.FutureConverters;
import scala.jdk.javaapi.FutureConverters;
import scala.concurrent.Future;

import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -52,7 +52,7 @@ public CompletionStage<Result> handleRequest(Request request,Object actorRef, St
} else {
future = Patterns.ask((ActorSelection) actorRef, request, t);
}
return FutureConverters.toJava(future).thenApplyAsync(fn);
return FutureConverters.asJava(future).thenApplyAsync(fn);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.util.Map;
import java.util.concurrent.CompletionStage;

import akka.actor.ActorRef;
import org.apache.pekko.actor.ActorRef;
import org.sunbird.incredible.processor.JsonKey;
import org.sunbird.cert.actor.operation.CertActorOperation;

Expand Down
10 changes: 5 additions & 5 deletions service/app/controllers/health/HealthController.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package controllers.health;

import akka.actor.ActorRef;
import org.apache.pekko.actor.ActorRef;
import controllers.BaseController;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
Expand Down Expand Up @@ -47,14 +47,14 @@ public class HealthController extends BaseController {
*
* @return a CompletableFuture of success response
*/
public CompletionStage<Result> getHealth() throws BaseException {
public CompletionStage<Result> getHealth(Http.Request httpRequest) throws BaseException {
try {
handleSigTerm();
logger.info("complete health method called.");
CompletionStage<Result> response = handleRequest(healthActorRef, request(), null, HEALTH_ACTOR_OPERATION_NAME);
CompletionStage<Result> response = handleRequest(healthActorRef, httpRequest, null, HEALTH_ACTOR_OPERATION_NAME);
return response;
} catch (Exception e) {
return CompletableFuture.completedFuture(RequestHandler.handleFailureResponse(e,request()));
return CompletableFuture.completedFuture(RequestHandler.handleFailureResponse(e,httpRequest));
}
}

Expand All @@ -74,7 +74,7 @@ public CompletionStage<Result> getServiceHealth(String health, Http.Request http
? cf.thenApplyAsync(Results::ok)
: cf.thenApplyAsync(Results::badRequest);
} catch (Exception e) {
return CompletableFuture.completedFuture(RequestHandler.handleFailureResponse(e,request()));
return CompletableFuture.completedFuture(RequestHandler.handleFailureResponse(e,httpRequest));
}
}

Expand Down
8 changes: 4 additions & 4 deletions service/app/utils/module/ActorStartModule.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package utils.module;

import akka.routing.FromConfig;
import akka.routing.RouterConfig;
import org.apache.pekko.routing.FromConfig;
import org.apache.pekko.routing.RouterConfig;
import com.google.inject.AbstractModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.libs.akka.AkkaGuiceSupport;
import play.libs.pekko.PekkoGuiceSupport;

public class ActorStartModule extends AbstractModule implements AkkaGuiceSupport {
public class ActorStartModule extends AbstractModule implements PekkoGuiceSupport {

Logger logger = LoggerFactory.getLogger(ActorStartModule.class);

Expand Down
8 changes: 3 additions & 5 deletions service/app/utils/module/OnRequestHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import play.http.ActionCreator;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Http.Context;
import play.mvc.Result;
/**
* This class will be called on each request.
Expand All @@ -28,15 +27,14 @@ public class OnRequestHandler implements ActionCreator {
public Action createAction(Http.Request request, Method method) {
return new Action.Simple() {
@Override
public CompletionStage<Result> call(Context context) {
Optional<String> requestIdHeader = request.getHeaders().get(JsonKey.X_REQUEST_ID);
public CompletionStage<Result> call(Http.Request req) {
Optional<String> requestIdHeader = req.getHeaders().get(JsonKey.X_REQUEST_ID);
String reqId = requestIdHeader.orElseGet(() -> UUID.randomUUID().toString());
MDC.clear();
MDC.put(JsonKey.REQUEST_MESSAGE_ID, reqId);
request.getHeaders().addHeader(JsonKey.REQUEST_MESSAGE_ID, reqId);
CompletionStage<Result> result = null;
logger.debug("On request method called");
result = delegate.call(context);
result = delegate.call(req);
return result.thenApply(res -> res.withHeader("Access-Control-Allow-Origin", "*"));
}
};
Expand Down
2 changes: 1 addition & 1 deletion service/app/utils/module/SignalHandler.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package utils.module;

import akka.actor.ActorSystem;
import org.apache.pekko.actor.ActorSystem;
import org.apache.commons.lang3.StringUtils;
import org.sunbird.LoggerUtil;
import play.api.Application;
Expand Down
Loading