2 hours ago
Here are our two custom classes
We tried putting custom-log4j2.properties
logger.gedse.name=fr.xyz.alfresco.gcs.connector.GcsAdapter
logger.gedse.level=debug
logger.gedwebscript.name=fr.xyz.alfresco.se.ged_se.webscripts
logger.gedwebscript.level=debug
Note: I tried putting logger in custom-log4j2.properties / log4j2.properties as well, but I still can't see any custom loggers.
If I put logger in admin console it is showing
Any HELP
what I need to change??? what to check
package fr.xyz.alfresco.se.ged_se.webscripts;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.json.JSONObject;
import org.springframework.extensions.webscripts.*;
import fr.xyz.alfresco.gcs.connector.GcsAdapter;
public class UpdateEventBasedHold extends AbstractWebScript {
//private static final Log LOGGER = LogFactory.getLog(UpdateEventBasedHold.class);
private static final Logger LOGGER = LogManager.getLogger(UpdateEventBasedHold.class);
private static final ExecutorService executor = Executors.newFixedThreadPool(2);
/**
* Request object for updating Event-Based Hold on GCS objects.
*
* <p>
* <b>Sample Request JSON:</b>
* </p>
*
* <pre>
* {
* "bucketName": "bucket2_dev",
* "hold": true,
* }
* </pre>
*
* <p>
* <b>Field Description:</b>
* </p>
* <ul>
* <li><b>bucketName</b> (required): Name of the GCS bucket</li>
* <li><b>hold</b> (required): true = apply hold, false = remove hold</li>
* </ul>
*/
@Override
public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException {
GcsAdapter gcsAdapter = new GcsAdapter();
if (req.getContent() == null) {
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Request body is missing");
}
try {
JSONObject json = new JSONObject(req.getContent().getContent());
String bucketName = json.optString("bucketName");
Boolean hold = json.getBoolean("hold");
Boolean bucketHold = json.getBoolean("bucketHold");
if (bucketName.isBlank()) {
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "bucketName required");
}
executor.submit(() -> {
try {
// Simulate heavy processing
System.out.println("DEBUG ENABLED = " + LOGGER.isDebugEnabled());
System.out.println("Background job started with payload: ");
LOGGER.info("Updating event-based hold at bucket level = " + bucketName);
gcsAdapter.updateEventBasedHoldOnBucket(bucketName, bucketHold);
LOGGER.info("Updating event-based hold for bucket=" + bucketName);
LOGGER.debug(" debug Updating event-based hold for bucket=" + bucketName);
gcsAdapter.updateEventBasedHold(bucketName, hold);
System.out.println("Background job completed.");
} catch (Exception e) {
e.printStackTrace();
}
});
JSONObject response = new JSONObject();
response.put("message", "Background job started successfully");
//response.put("status", "success");
res.setStatus(Status.STATUS_OK);
res.setContentType("application/json");
res.getWriter().write(response.toString());
} catch (WebScriptException e) {
throw e;
} catch (Exception e) {
LOGGER.error("Unexpected error while processing request", e);
throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, "Internal server error");
}
}
}
package fr.xyz.alfresco.gcs.connector;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.alfresco.repo.content.ContentStore;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;
import org.threeten.bp.Duration;
import com.google.api.gax.paging.Page;
import com.google.api.gax.retrying.RetrySettings;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import fr.xyz.alfresco.gcs.encryption.Keystore;
import fr.xyz.alfresco.gcs.exception.EncryptionException;
public class GcsAdapter implements ServiceAdapter {
private static final Log LOGGER = LogFactory.getLog(GcsAdapter.class);
private static final String MIGRATION_DATA = "MIGRATION_DATA";
private String credentialFile;
private Storage storage;
private String bucketName;
private int maxAttempts;
private String gcsRoot;
private String contentstore;
private String contentstoreDeleted;
private Keystore keystore;
private String algoEncryption;
private static final int THREAD_COUNT = 20; // Tune based on CPU/network
private static final int PAGE_SIZE = 1000; // Max safe size
private static final int QUEUE_CAPACITY = 5000; // Backpressure control
public void setKeystore(Keystore keystore) {
this.keystore = keystore;
}
public void setAlgoEncryption(String algoEncryption) {
this.algoEncryption = algoEncryption;
}
public void setCredentialFile(String credentialFile) {
this.credentialFile = credentialFile;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
public String getBucketName() {
return bucketName;
}
public void setMaxAttempts(int maxAttempts) {
this.maxAttempts = maxAttempts;
}
public void setGcsRoot(String gcsRoot) {
this.gcsRoot = gcsRoot;
}
public void setContentstore(String contentstore) {
this.contentstore = contentstore;
}
public void setContentstoreDeleted(String contentstoreDeleted) {
this.contentstoreDeleted = contentstoreDeleted;
}
public void init() throws Exception {
try {
RetrySettings retrySettings = RetrySettings.newBuilder().setMaxAttempts(maxAttempts)
.setInitialRetryDelay(Duration.ofSeconds(3)).setRetryDelayMultiplier(1.5)
.setMaxRetryDelay(Duration.ofSeconds(45)).build();
if (StringUtils.hasLength(credentialFile) && new ClassPathResource(credentialFile).exists()) {
LOGGER.info("Use credential json file");
this.storage = StorageOptions.newBuilder().setRetrySettings(retrySettings)
.setCredentials(ServiceAccountCredentials
.fromStream(new ClassPathResource(credentialFile).getInputStream()))
.build().getService();
} else {
LOGGER.info("Use default user account");
this.storage = StorageOptions.newBuilder().setRetrySettings(retrySettings).build().getService();
}
} catch (Exception e) {
String errorMsg = "GcsContentStore: Failed to init with credential file " + credentialFile + " ("
+ e.getMessage() + ")";
LOGGER.error("GcsContentStore: Failed to init", e);
throw new ContentIOException(errorMsg, e);
}
if (StringUtils.hasLength(algoEncryption)) {
LOGGER.info("Encryption mode activated");
}
}
@Override
public ReadableByteChannel getObject(String path) throws EncryptionException, IOException {
if (StringUtils.hasLength(algoEncryption)) {
try (ByteArrayInputStream stream = new ByteArrayInputStream(
storage.get(BlobId.of(bucketName, convertPath(path))).getContent())) {
return Channels.newChannel(
new ByteArrayInputStream(keystore.decrypt(algoEncryption, IOUtils.toByteArray(stream))));
}
} else {
return storage.reader(BlobId.of(bucketName, convertPath(path)));
}
}
@Override
public void storeObject(String path, InputStream is, long size) throws EncryptionException, IOException {
BlobInfo blobInfo = BlobInfo.newBuilder(BlobId.of(bucketName, convertPath(path))).build();
if (StringUtils.hasLength(algoEncryption)) {
Storage.BlobTargetOption precondition = Storage.BlobTargetOption.doesNotExist();
byte[] contentBytes = keystore.encrypt(algoEncryption, is);
blobInfo = storage.create(blobInfo, contentBytes, precondition);
} else {
Storage.BlobWriteOption precondition = Storage.BlobWriteOption.doesNotExist();
blobInfo = storage.createFrom(blobInfo, is, precondition);
}
LOGGER.debug("Object Created : " + blobInfo.getName());
}
@Override
public void deleteObject(String path) {
storage.delete(BlobId.of(bucketName, convertPath(path)));
}
@Override
public long getObjectSize(String path) {
return storage.get(BlobId.of(bucketName, convertPath(path))).getSize();
}
@Override
public boolean isObjectAvailable(String path) {
if (path.startsWith(GcsContentStore.GCS_STORE_PROTOCOL + "_" + bucketName + ContentStore.PROTOCOL_DELIMITER
+ MIGRATION_DATA) || path.startsWith(MIGRATION_DATA)) {
return storage.get(BlobId.of(bucketName, convertMigrationPath(path))) != null;
} else if (path
.startsWith(GcsContentStore.GCS_STORE_PROTOCOL + "_" + bucketName + ContentStore.PROTOCOL_DELIMITER)
|| path.startsWith(GcsContentStore.GCS_STORE_PROTOCOL + ContentStore.PROTOCOL_DELIMITER)) {
return storage.get(BlobId.of(bucketName, convertPath(path))) != null;
}
return false;
}
@Override
public long getLastModifiedObject(String path) {
return storage.get(BlobId.of(bucketName, convertPath(path))).getUpdateTime();
}
public void removeEventBaseHold(String path) {
BlobInfo blobInfo = storage.get(BlobId.of(bucketName, convertPath(path)));
if (blobInfo != null) {
LOGGER.info("----- Bucketinfo ----- : " + blobInfo.getName());
storage.update(blobInfo.toBuilder().setEventBasedHold(false).build());
}
}
// Updates bucket level default event-based hold
public void updateEventBasedHoldOnBucket(String bucketName, boolean bucketDefaultHold) {
try {
// Update bucket default event-based hold
Storage storage = StorageOptions.getDefaultInstance().getService();
Bucket bucket = storage.get(bucketName);
if (bucket == null) {
throw new RuntimeException("Bucket not found: " + bucketName);
}
Boolean currentDefaultHold = bucket.getDefaultEventBasedHold();
// Update only if needed
if (currentDefaultHold == null || currentDefaultHold != bucketDefaultHold) {
Bucket updatedBucket = storage
.update(bucket.toBuilder().setDefaultEventBasedHold(bucketDefaultHold).build());
LOGGER.info("Bucket default event-based hold updated to: " + updatedBucket.getDefaultEventBasedHold());
} else {
LOGGER.info("Bucket default hold already set to desired value");
}
} catch (Exception e) {
LOGGER.error("Error updating bucket default hold", e);
}
}
public void updateEventBasedHold(String bucketName, boolean applyHold) {
Storage storage = StorageOptions.getDefaultInstance().getService();
LOGGER.info("----- Starting EventHold Update -----");
LOGGER.info("Bucket: " + bucketName);
LOGGER.info("Apply Hold: " + applyHold);
// Thread pool with bounded queue (prevents OOM)
ThreadPoolExecutor executor = new ThreadPoolExecutor(THREAD_COUNT, THREAD_COUNT, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY), new ThreadPoolExecutor.CallerRunsPolicy() // backpressure
);
AtomicLong processed = new AtomicLong(0);
AtomicLong skipped = new AtomicLong(0);
AtomicLong failed = new AtomicLong(0);
Page<Blob> page = storage.list(bucketName, Storage.BlobListOption.pageSize(PAGE_SIZE));
// Iterate page-by-page (memory efficient)
while (page != null) {
for (Blob blob : page.getValues()) {
executor.submit(() -> {
try {
boolean currentHold = Boolean.TRUE.equals(blob.getEventBasedHold());
if (currentHold != applyHold) {
storage.update(blob.toBuilder().setEventBasedHold(applyHold).build());
long count = processed.incrementAndGet();
if (count % 10000 == 0) {
LOGGER.info("Processed: " + count);
}
} else {
skipped.incrementAndGet();
}
} catch (Exception e) {
failed.incrementAndGet();
LOGGER.error("Error processing: " + blob.getName(), e);
}
});
}
page = page.getNextPage(); // manual paging control
}
// Graceful shutdown
executor.shutdown();
try {
executor.awaitTermination(7, TimeUnit.DAYS); // large workload safe
} catch (InterruptedException e) {
LOGGER.error("Executor interrupted", e);
}
LOGGER.info("----- COMPLETED -----");
LOGGER.info("Updated: " + processed.get());
LOGGER.info("Skipped: " + skipped.get());
LOGGER.info("Failed: " + failed.get());
}
private String convertPath(String path) {
if (path.startsWith(GcsContentStore.GCS_STORE_PROTOCOL + "_" + bucketName + ContentStore.PROTOCOL_DELIMITER
+ MIGRATION_DATA) || path.startsWith(MIGRATION_DATA)) {
return convertMigrationPath(path);
} else if (path
.startsWith(GcsContentStore.GCS_STORE_PROTOCOL + "_" + bucketName + ContentStore.PROTOCOL_DELIMITER)) {
return path.replace(GcsContentStore.GCS_STORE_PROTOCOL + "_" + bucketName + ContentStore.PROTOCOL_DELIMITER,
(gcsRoot.startsWith("/") ? gcsRoot.substring(1) : gcsRoot) + File.separator + contentstore
+ File.separator);
}
return path.replace(GcsContentStore.GCS_STORE_PROTOCOL + ContentStore.PROTOCOL_DELIMITER,
(gcsRoot.startsWith("/") ? gcsRoot.substring(1) : gcsRoot) + File.separator + contentstore
+ File.separator);
}
private String convertMigrationPath(String path) {
return path.replace(GcsContentStore.GCS_STORE_PROTOCOL + "_" + bucketName + ContentStore.PROTOCOL_DELIMITER,
"");
}
}
Explore our Alfresco products with the links below. Use labels to filter content by product module.