<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic custom-log4j2.properties changes are not affected, not able to see my logs in Alfresco Forum</title>
    <link>https://connect.hyland.com/t5/alfresco-forum/custom-log4j2-properties-changes-are-not-affected-not-able-to/m-p/499126#M40851</link>
    <description>&lt;P&gt;Here are our two custom classes&lt;/P&gt;&lt;P&gt;We tried putting &lt;STRONG&gt;custom-log4j2.properties&lt;/STRONG&gt;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;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&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Note&lt;/STRONG&gt;: I tried putting logger in custom-log4j2.properties /&amp;nbsp; log4j2.properties as well, but I still can't see any custom loggers.&lt;/P&gt;&lt;P&gt;If I put logger in admin console it is showing&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Any HELP&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;what I need to change???&amp;nbsp; what to check&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;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.

	 *

	 * &amp;lt;p&amp;gt;

	 * &amp;lt;b&amp;gt;Sample Request JSON:&amp;lt;/b&amp;gt;

	 * &amp;lt;/p&amp;gt;

	 * 

	 * &amp;lt;pre&amp;gt;

	 * {

	 *   "bucketName": "bucket2_dev",

	 *   "hold": true,

	 * }

	 * &amp;lt;/pre&amp;gt;

	 *

	 * &amp;lt;p&amp;gt;

	 * &amp;lt;b&amp;gt;Field Description:&amp;lt;/b&amp;gt;

	 * &amp;lt;/p&amp;gt;

	 * &amp;lt;ul&amp;gt;

	 * &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;bucketName&amp;lt;/b&amp;gt; (required): Name of the GCS bucket&amp;lt;/li&amp;gt;

	 * &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;hold&amp;lt;/b&amp;gt; (required): true = apply hold, false = remove hold&amp;lt;/li&amp;gt;

	 * &amp;lt;/ul&amp;gt;

	 */
 
	@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(() -&amp;gt; {

                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) &amp;amp;&amp;amp; 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&amp;lt;&amp;gt;(QUEUE_CAPACITY), new ThreadPoolExecutor.CallerRunsPolicy() // backpressure

		);
 
		AtomicLong processed = new AtomicLong(0);

		AtomicLong skipped = new AtomicLong(0);

		AtomicLong failed = new AtomicLong(0);
 
		Page&amp;lt;Blob&amp;gt; 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(() -&amp;gt; {

					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,

				"");

	}
 
}

 &lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Mon, 29 Jun 2026 09:08:48 GMT</pubDate>
    <dc:creator>Ghanshyam_R</dc:creator>
    <dc:date>2026-06-29T09:08:48Z</dc:date>
    <item>
      <title>custom-log4j2.properties changes are not affected, not able to see my logs</title>
      <link>https://connect.hyland.com/t5/alfresco-forum/custom-log4j2-properties-changes-are-not-affected-not-able-to/m-p/499126#M40851</link>
      <description>&lt;P&gt;Here are our two custom classes&lt;/P&gt;&lt;P&gt;We tried putting &lt;STRONG&gt;custom-log4j2.properties&lt;/STRONG&gt;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;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&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Note&lt;/STRONG&gt;: I tried putting logger in custom-log4j2.properties /&amp;nbsp; log4j2.properties as well, but I still can't see any custom loggers.&lt;/P&gt;&lt;P&gt;If I put logger in admin console it is showing&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Any HELP&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;what I need to change???&amp;nbsp; what to check&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;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.

	 *

	 * &amp;lt;p&amp;gt;

	 * &amp;lt;b&amp;gt;Sample Request JSON:&amp;lt;/b&amp;gt;

	 * &amp;lt;/p&amp;gt;

	 * 

	 * &amp;lt;pre&amp;gt;

	 * {

	 *   "bucketName": "bucket2_dev",

	 *   "hold": true,

	 * }

	 * &amp;lt;/pre&amp;gt;

	 *

	 * &amp;lt;p&amp;gt;

	 * &amp;lt;b&amp;gt;Field Description:&amp;lt;/b&amp;gt;

	 * &amp;lt;/p&amp;gt;

	 * &amp;lt;ul&amp;gt;

	 * &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;bucketName&amp;lt;/b&amp;gt; (required): Name of the GCS bucket&amp;lt;/li&amp;gt;

	 * &amp;lt;li&amp;gt;&amp;lt;b&amp;gt;hold&amp;lt;/b&amp;gt; (required): true = apply hold, false = remove hold&amp;lt;/li&amp;gt;

	 * &amp;lt;/ul&amp;gt;

	 */
 
	@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(() -&amp;gt; {

                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) &amp;amp;&amp;amp; 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&amp;lt;&amp;gt;(QUEUE_CAPACITY), new ThreadPoolExecutor.CallerRunsPolicy() // backpressure

		);
 
		AtomicLong processed = new AtomicLong(0);

		AtomicLong skipped = new AtomicLong(0);

		AtomicLong failed = new AtomicLong(0);
 
		Page&amp;lt;Blob&amp;gt; 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(() -&amp;gt; {

					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,

				"");

	}
 
}

 &lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 29 Jun 2026 09:08:48 GMT</pubDate>
      <guid>https://connect.hyland.com/t5/alfresco-forum/custom-log4j2-properties-changes-are-not-affected-not-able-to/m-p/499126#M40851</guid>
      <dc:creator>Ghanshyam_R</dc:creator>
      <dc:date>2026-06-29T09:08:48Z</dc:date>
    </item>
  </channel>
</rss>

