Skip to content

Commit

Permalink
#27540 Replacing direct usage of CompletableFuture and other native J…
Browse files Browse the repository at this point in the history
…ava concurrency mechanisms with ManagedExecutor
  • Loading branch information
jgambarios committed Feb 8, 2024
1 parent 9fa4d33 commit efef5a5
Show file tree
Hide file tree
Showing 24 changed files with 933 additions and 383 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.dotcms.api.client;

import com.dotcms.api.client.files.traversal.exception.TraversalTaskException;
import com.dotcms.security.Encryptor;
import com.dotcms.security.HashBuilder;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.jboss.logging.Logger;

/**
* This is a service class for calculating file hashes. It is responsible for providing a method to
* calculate the SHA-256 hash of a given file. This class is marked as
* {@link javax.enterprise.context.ApplicationScoped}, meaning that a single instance is shared
* across the entire application.
*/
@ApplicationScoped
public class FileHashCalculatorService implements Serializable {

@Inject
Logger logger;

/**
* Calculates the SHA-256 hash of the content of the supplied file represented by the provided
* {@link Path}. The function will throw a {@link TraversalTaskException} if the SHA-256
* algorithm is not found or there is an error reading the file.
*
* @param path the path to the file whose content hash needs to be calculated
* @return the SHA-256 hash as a UNIX-formatted string
* @throws TraversalTaskException if there is an error calculating the hash
*/
public String sha256toUnixHash(final Path path) {

try {

final HashBuilder sha256Builder = Encryptor.Hashing.sha256();
final byte[] buffer = new byte[4096];
int countBytes;

try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(path))) {

countBytes = inputStream.read(buffer);
while (countBytes > 0) {

sha256Builder.append(buffer, countBytes);
countBytes = inputStream.read(buffer);
}
}

return sha256Builder.buildUnixHash();
} catch (NoSuchAlgorithmException | IOException e) {
var errorMessage = String.format("Error calculating sha256 for file [%s]", path);
logger.error(errorMessage, e);
throw new TraversalTaskException(errorMessage, e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import javax.enterprise.context.Dependent;
import javax.enterprise.context.control.ActivateRequestContext;
import javax.inject.Inject;
import org.eclipse.microprofile.context.ManagedExecutor;
import org.jboss.logging.Logger;

@DefaultBean
Expand All @@ -41,6 +42,9 @@ public class PushServiceImpl implements PushService {
@Inject
RemoteTraversalService remoteTraversalService;

@Inject
ManagedExecutor executor;

/**
* Traverses the local folders and retrieves the hierarchical tree representation of their
* contents with the push related information for each file and folder. Each folder is
Expand Down Expand Up @@ -251,17 +255,16 @@ public void processTreeNodes(OutputOptionMixin output,
);

var isRetry = retryAttempts > 0;
CompletableFuture<List<Exception>> pushTreeFuture = CompletableFuture.supplyAsync(
CompletableFuture<List<Exception>> pushTreeFuture = executor.supplyAsync(
() -> remoteTraversalService.pushTreeNode(
PushTraverseParams.builder().from(traverseParams)
.progressBar(progressBar)
.logger(logger)
.isRetry(isRetry).build()
)
);
progressBar.setFuture(pushTreeFuture);

CompletableFuture<Void> animationFuture = CompletableFuture.runAsync(
CompletableFuture<Void> animationFuture = executor.runAsync(
progressBar
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.dotcms.api.client.files.traversal;

import com.dotcms.api.client.files.traversal.data.Pusher;
import com.dotcms.api.traversal.TreeNode;
import com.dotcms.cli.command.PushContext;
import com.dotcms.cli.common.ConsoleProgressBar;
Expand All @@ -10,7 +9,6 @@
import javax.annotation.Nullable;
import org.immutables.value.Value;
import org.immutables.value.Value.Default;
import org.jboss.logging.Logger;

/**
* Just a class to compile all the params shared by various Traverse APIs
Expand All @@ -29,12 +27,6 @@ public interface AbstractPushTraverseParams extends Serializable {
@Default
default int maxRetryAttempts(){return 0;}

@Nullable
Logger logger();

@Nullable
Pusher pusher();

@Nullable
ConsoleProgressBar progressBar();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import static com.dotcms.common.AssetsUtils.parseLocalPath;

import com.dotcms.api.client.FileHashCalculatorService;
import com.dotcms.api.client.files.traversal.data.Downloader;
import com.dotcms.api.client.files.traversal.data.Retriever;
import com.dotcms.api.client.files.traversal.task.LocalFolderTraversalTask;
import com.dotcms.api.client.files.traversal.task.LocalFolderTraversalTaskParams;
import com.dotcms.api.client.files.traversal.task.PullTreeNodeTask;
import com.dotcms.api.client.files.traversal.task.PullTreeNodeTaskParams;
import com.dotcms.api.traversal.TreeNode;
import com.dotcms.cli.common.ConsoleProgressBar;
import com.dotcms.common.AssetsUtils;
Expand All @@ -14,11 +17,11 @@
import java.io.File;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.control.ActivateRequestContext;
import javax.inject.Inject;
import javax.ws.rs.NotFoundException;
import org.eclipse.microprofile.context.ManagedExecutor;
import org.jboss.logging.Logger;

/**
Expand All @@ -39,6 +42,12 @@ public class LocalTraversalServiceImpl implements LocalTraversalService {
@Inject
protected Downloader downloader;

@Inject
ManagedExecutor executor;

@Inject
FileHashCalculatorService fileHashCalculatorService;

/**
* Traverses the file system directory at the specified path and builds a hierarchical tree
* representation of its contents. The folders and contents are compared to the remote server in
Expand Down Expand Up @@ -84,16 +93,25 @@ public TraverseResult traverseLocalFolder(final LocalTraverseParams params) {
logger.debug(String.format("Language [%s] doesn't exist on remote server.", localPath.language()));
}

var forkJoinPool = ForkJoinPool.commonPool();
var task = new LocalFolderTraversalTask(
logger,
executor,
retriever,
fileHashCalculatorService
);

var task = new LocalFolderTraversalTask(LocalTraverseParams.builder()
.from(params)
.logger(logger)
.retriever(retriever)
task.setTraversalParams(LocalFolderTraversalTaskParams.builder()
.siteExists(siteExists)
.sourcePath(params.sourcePath())
.workspace(params.workspace())
.removeAssets(params.removeAssets())
.removeFolders(params.removeFolders())
.ignoreEmptyFolders(params.ignoreEmptyFolders())
.failFast(params.failFast())
.build()
);
var result = forkJoinPool.invoke(task);

var result = task.compute();
return TraverseResult.builder()
.exceptions(result.getLeft())
.localPaths(localPath)
Expand Down Expand Up @@ -125,18 +143,25 @@ public List<Exception> pullTreeNode(final TreeNode rootNode, final String destin
rootNode.folder().host());

// ---
var forkJoinPool = ForkJoinPool.commonPool();
var task = new PullTreeNodeTask(
logger,
executor,
downloader,
filteredRoot,
rootPath.toString(),
overwrite,
generateEmptyFolders,
failFast,
language,
progressBar);
return forkJoinPool.invoke(task);
fileHashCalculatorService
);

task.setTraversalParams(PullTreeNodeTaskParams.builder()
.rootNode(filteredRoot)
.destination(rootPath.toString())
.overwrite(overwrite)
.generateEmptyFolders(generateEmptyFolders)
.failFast(failFast)
.language(language)
.progressBar(progressBar)
.build()
);

return task.compute();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import com.dotcms.api.client.files.traversal.data.Pusher;
import com.dotcms.api.client.files.traversal.data.Retriever;
import com.dotcms.api.client.files.traversal.task.PushTreeNodeTask;
import com.dotcms.api.client.files.traversal.task.PushTreeNodeTaskParams;
import com.dotcms.api.client.files.traversal.task.RemoteFolderTraversalTask;
import com.dotcms.api.client.files.traversal.task.RemoteFolderTraversalTaskParams;
import com.dotcms.api.traversal.Filter;
import com.dotcms.api.traversal.TreeNode;
import com.dotcms.common.AssetsUtils;
Expand All @@ -12,7 +14,6 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ForkJoinPool;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.control.ActivateRequestContext;
import javax.inject.Inject;
Expand Down Expand Up @@ -135,12 +136,25 @@ public List<Exception> pushTreeNode(PushTraverseParams traverseParams) {
}

// ---
var forkJoinPool = ForkJoinPool.commonPool();
var task = new PushTreeNodeTask(PushTraverseParams.builder()
.from(traverseParams)
.pusher(pusher)
.build());
return forkJoinPool.invoke(task);
var task = new PushTreeNodeTask(
logger,
executor,
traverseParams.pushContext(),
pusher
);

task.setTraversalParams(PushTreeNodeTaskParams.builder()
.workspacePath(traverseParams.workspacePath())
.localPaths(traverseParams.localPaths())
.rootNode(traverseParams.rootNode())
.failFast(traverseParams.failFast())
.isRetry(traverseParams.isRetry())
.maxRetryAttempts(traverseParams.maxRetryAttempts())
.progressBar(traverseParams.progressBar())
.build()
);

return task.compute();
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.dotcms.api.client.files.traversal.task;

import com.dotcms.model.annotation.ValueType;
import java.io.File;
import java.io.Serializable;
import org.immutables.value.Value;
import org.immutables.value.Value.Default;

/**
* This interface represents the parameters for a {@link LocalFolderTraversalTask}.
*/
@ValueType
@Value.Immutable
public interface AbstractLocalFolderTraversalTaskParams extends Serializable {

/**
* Returns a boolean indicating whether the site exists.
*
* @return true if the site exists, false otherwise. Default is false.
*/
@Default
default boolean siteExists() {
return false;
}

/**
* Returns the source path for the traversal process.
*
* @return String representing the source path.
*/
String sourcePath();

/**
* Returns the workspace file.
*
* @return File representing the workspace.
*/
File workspace();

/**
* Returns a boolean flag indicating whether to remove assets.
*
* @return true if the assets should be removed, false otherwise.
*/
boolean removeAssets();

/**
* Returns a boolean flag indicating whether to remove folders.
*
* @return true if the folders should be removed, false otherwise.
*/
boolean removeFolders();

/**
* Returns a boolean flag indicating whether to ignore empty folders.
*
* @return true if the empty folders should be ignored, false otherwise.
*/
boolean ignoreEmptyFolders();

/**
* Returns a boolean flag indicating whether to fail fast.
*
* @return true if the operation should fail fast, false otherwise.
*/
boolean failFast();
}
Loading

0 comments on commit efef5a5

Please sign in to comment.