refactored
This commit is contained in:
BIN
jakarta/metadata.keystore
Normal file
BIN
jakarta/metadata.keystore
Normal file
Binary file not shown.
54
jakarta/pom.xml
Normal file
54
jakarta/pom.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.inteligr8.alfresco</groupId>
|
||||
<artifactId>annotations-platform-module</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>annotations-jakarta-platform-module</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<alfresco.platform.version>23.2.1</alfresco.platform.version>
|
||||
<alfresco.platform.war.version>23.2.0.60</alfresco.platform.war.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>annotations-core-platform-module</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.transaction</groupId>
|
||||
<artifactId>jakarta.transaction-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.repaint.maven</groupId>
|
||||
<artifactId>tiles-maven-plugin</artifactId>
|
||||
<version>2.40</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<tiles>
|
||||
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-rad-tile -->
|
||||
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-rad-tile:[1.1.0,1.2.0)</tile>
|
||||
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-module-tile -->
|
||||
<tile>com.inteligr8.ootbee:beedk-acs-platform-module-tile:[1.1.0,1.2.0)</tile>
|
||||
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-it-tile -->
|
||||
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-it-tile:[1.1.0,1.2.0)</tile>
|
||||
</tiles>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
74
jakarta/rad.ps1
Normal file
74
jakarta/rad.ps1
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
function discoverArtifactId {
|
||||
$script:ARTIFACT_ID=(mvn -q -Dexpression=project"."artifactId -DforceStdout help:evaluate)
|
||||
}
|
||||
|
||||
function rebuild {
|
||||
echo "Rebuilding project ..."
|
||||
mvn process-classes
|
||||
}
|
||||
|
||||
function start_ {
|
||||
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
|
||||
mvn -Drad process-classes
|
||||
}
|
||||
|
||||
function start_log {
|
||||
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
|
||||
mvn -Drad "-Ddocker.showLogs" process-classes
|
||||
}
|
||||
|
||||
function stop_ {
|
||||
discoverArtifactId
|
||||
echo "Stopping Docker containers that supported rapid application development ..."
|
||||
docker container ls --filter name=${ARTIFACT_ID}-*
|
||||
echo "Stopping containers ..."
|
||||
docker container stop (docker container ls -q --filter name=${ARTIFACT_ID}-*)
|
||||
echo "Removing containers ..."
|
||||
docker container rm (docker container ls -aq --filter name=${ARTIFACT_ID}-*)
|
||||
}
|
||||
|
||||
function tail_logs {
|
||||
param (
|
||||
$container
|
||||
)
|
||||
|
||||
discoverArtifactId
|
||||
docker container logs -f (docker container ls -q --filter name=${ARTIFACT_ID}-${container})
|
||||
}
|
||||
|
||||
function list {
|
||||
discoverArtifactId
|
||||
docker container ls --filter name=${ARTIFACT_ID}-*
|
||||
}
|
||||
|
||||
switch ($args[0]) {
|
||||
"start" {
|
||||
start_
|
||||
}
|
||||
"start_log" {
|
||||
start_log
|
||||
}
|
||||
"stop" {
|
||||
stop_
|
||||
}
|
||||
"restart" {
|
||||
stop_
|
||||
start_
|
||||
}
|
||||
"rebuild" {
|
||||
rebuild
|
||||
}
|
||||
"tail" {
|
||||
tail_logs $args[1]
|
||||
}
|
||||
"containers" {
|
||||
list
|
||||
}
|
||||
default {
|
||||
echo "Usage: .\rad.ps1 [ start | start_log | stop | restart | rebuild | tail {container} | containers ]"
|
||||
}
|
||||
}
|
||||
|
||||
echo "Completed!"
|
||||
|
71
jakarta/rad.sh
Normal file
71
jakarta/rad.sh
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/bin/sh
|
||||
|
||||
discoverArtifactId() {
|
||||
ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'`
|
||||
}
|
||||
|
||||
rebuild() {
|
||||
echo "Rebuilding project ..."
|
||||
mvn process-test-classes
|
||||
}
|
||||
|
||||
start() {
|
||||
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
|
||||
mvn -Drad process-test-classes
|
||||
}
|
||||
|
||||
start_log() {
|
||||
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
|
||||
mvn -Drad -Ddocker.showLogs process-test-classes
|
||||
}
|
||||
|
||||
stop() {
|
||||
discoverArtifactId
|
||||
echo "Stopping Docker containers that supported rapid application development ..."
|
||||
docker container ls --filter name=${ARTIFACT_ID}-*
|
||||
echo "Stopping containers ..."
|
||||
docker container stop `docker container ls -q --filter name=${ARTIFACT_ID}-*`
|
||||
echo "Removing containers ..."
|
||||
docker container rm `docker container ls -aq --filter name=${ARTIFACT_ID}-*`
|
||||
}
|
||||
|
||||
tail_logs() {
|
||||
discoverArtifactId
|
||||
docker container logs -f `docker container ls -q --filter name=${ARTIFACT_ID}-$1`
|
||||
}
|
||||
|
||||
list() {
|
||||
discoverArtifactId
|
||||
docker container ls --filter name=${ARTIFACT_ID}-*
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
start_log)
|
||||
start_log
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
rebuild)
|
||||
rebuild
|
||||
;;
|
||||
tail)
|
||||
tail_logs $2
|
||||
;;
|
||||
containers)
|
||||
list
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ./rad.sh [ start | start_log | stop | restart | rebuild | tail {container} | containers ]"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
echo "Completed!"
|
||||
|
@@ -0,0 +1,23 @@
|
||||
package com.inteligr8.alfresco.annotations.aspect;
|
||||
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
/**
|
||||
* @see jakarta.transaction.Transactional
|
||||
*/
|
||||
@Aspect
|
||||
public class RetryingTransactionAspect extends AbstractRetryingTransactionAspect {
|
||||
|
||||
@Override
|
||||
public String getJtaInterfaceName() {
|
||||
return Transactional.class.getName();
|
||||
}
|
||||
|
||||
@Pointcut("@annotation(jakarta.transaction.Transactional) && execution(* *(..))")
|
||||
public void isJtaTransactionalAnnotated() {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
package com.inteligr8.alfresco.annotations.service.impl;
|
||||
|
||||
import javax.transaction.xa.XAResource;
|
||||
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionListener;
|
||||
|
||||
import jakarta.transaction.HeuristicMixedException;
|
||||
import jakarta.transaction.HeuristicRollbackException;
|
||||
import jakarta.transaction.NotSupportedException;
|
||||
import jakarta.transaction.RollbackException;
|
||||
import jakarta.transaction.Status;
|
||||
import jakarta.transaction.Synchronization;
|
||||
import jakarta.transaction.SystemException;
|
||||
import jakarta.transaction.Transaction;
|
||||
import jakarta.transaction.UserTransaction;
|
||||
|
||||
public class AlfrescoTransaction implements UserTransaction, Transaction {
|
||||
|
||||
private UserTransaction userTx;
|
||||
|
||||
public AlfrescoTransaction(UserTransaction userTx) {
|
||||
this.userTx = userTx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() throws NotSupportedException, SystemException {
|
||||
this.userTx.begin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
|
||||
SecurityException, IllegalStateException, SystemException {
|
||||
this.userTx.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException, SystemException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enlistResource(XAResource xaRes) throws RollbackException, IllegalStateException, SystemException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatus() throws SystemException {
|
||||
return this.userTx.getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSynchronization(Synchronization sync)
|
||||
throws RollbackException, IllegalStateException, SystemException {
|
||||
AlfrescoTransactionSupport.bindListener(new TransactionListener() {
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeCompletion() {
|
||||
sync.beforeCompletion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeCommit(boolean readOnly) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRollback() {
|
||||
sync.afterCompletion(Status.STATUS_ROLLEDBACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
sync.afterCompletion(Status.STATUS_COMMITTED);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws IllegalStateException, SecurityException, SystemException {
|
||||
this.userTx.rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() throws IllegalStateException, SystemException {
|
||||
this.userTx.setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionTimeout(int seconds) throws SystemException {
|
||||
this.userTx.setTransactionTimeout(seconds);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,112 @@
|
||||
package com.inteligr8.alfresco.annotations.service.impl;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.transaction.HeuristicMixedException;
|
||||
import jakarta.transaction.HeuristicRollbackException;
|
||||
import jakarta.transaction.InvalidTransactionException;
|
||||
import jakarta.transaction.NotSupportedException;
|
||||
import jakarta.transaction.RollbackException;
|
||||
import jakarta.transaction.Status;
|
||||
import jakarta.transaction.SystemException;
|
||||
import jakarta.transaction.Transaction;
|
||||
import jakarta.transaction.TransactionManager;
|
||||
import jakarta.transaction.UserTransaction;
|
||||
|
||||
/**
|
||||
* This bean implements a standard TransactionManager for ACS.
|
||||
*/
|
||||
@Component
|
||||
public class AlfrescoTransactionManager implements TransactionManager {
|
||||
|
||||
@Autowired
|
||||
private TransactionService txService;
|
||||
|
||||
private ThreadLocal<AlfrescoTransaction> tx = ThreadLocal.withInitial(new Supplier<AlfrescoTransaction>() {
|
||||
@Override
|
||||
public AlfrescoTransaction get() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public void begin() throws NotSupportedException, SystemException {
|
||||
UserTransaction userTx = this.txService.getNonPropagatingUserTransaction();
|
||||
AlfrescoTransaction tx = new AlfrescoTransaction(userTx);
|
||||
tx.begin();
|
||||
this.tx.set(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
|
||||
SecurityException, IllegalStateException, SystemException {
|
||||
AlfrescoTransaction tx = this.tx.get();
|
||||
if (tx == null)
|
||||
throw new IllegalStateException();
|
||||
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatus() throws SystemException {
|
||||
AlfrescoTransaction tx = this.tx.get();
|
||||
if (tx == null)
|
||||
return Status.STATUS_NO_TRANSACTION;
|
||||
|
||||
return tx.getStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction getTransaction() throws SystemException {
|
||||
return this.tx.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(Transaction tx) throws InvalidTransactionException, IllegalStateException, SystemException {
|
||||
if (!(tx instanceof AlfrescoTransaction))
|
||||
throw new InvalidTransactionException("An AlfrescoTransaction is expected; received: " + tx.getClass());
|
||||
if (this.tx.get() != null)
|
||||
throw new IllegalStateException();
|
||||
|
||||
this.tx.set((AlfrescoTransaction) tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws IllegalStateException, SecurityException, SystemException {
|
||||
AlfrescoTransaction tx = this.tx.get();
|
||||
if (tx == null)
|
||||
throw new IllegalStateException();
|
||||
|
||||
tx.rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() throws IllegalStateException, SystemException {
|
||||
AlfrescoTransaction tx = this.tx.get();
|
||||
if (tx == null)
|
||||
throw new IllegalStateException();
|
||||
|
||||
tx.setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionTimeout(int seconds) throws SystemException {
|
||||
AlfrescoTransaction tx = this.tx.get();
|
||||
if (tx != null)
|
||||
tx.setTransactionTimeout(seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction suspend() throws SystemException {
|
||||
try {
|
||||
return this.tx.get();
|
||||
} finally {
|
||||
this.tx.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,354 @@
|
||||
package com.inteligr8.alfresco.annotations.service.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.repo.dictionary.M2Model;
|
||||
import org.alfresco.repo.version.common.VersionImpl;
|
||||
import org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.action.ActionService;
|
||||
import org.alfresco.service.cmr.dictionary.CustomModelService;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.version.Version;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.apache.activemq.ActiveMQConnectionFactory;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.messaginghub.pooled.jms.JmsPoolConnectionFactory;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.Trigger;
|
||||
import org.quartz.TriggerBuilder;
|
||||
import org.quartz.impl.JobDetailImpl;
|
||||
import org.quartz.impl.StdSchedulerFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.inteligr8.alfresco.annotations.AuthorizedAsSystem;
|
||||
import com.inteligr8.alfresco.annotations.Threadable;
|
||||
import com.inteligr8.alfresco.annotations.Threaded;
|
||||
import com.inteligr8.alfresco.annotations.TransactionalRetryable;
|
||||
import com.inteligr8.alfresco.annotations.job.AsyncJob;
|
||||
import com.inteligr8.alfresco.annotations.service.AsyncProcessException;
|
||||
import com.inteligr8.alfresco.annotations.service.AsyncService;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import jakarta.jms.Connection;
|
||||
import jakarta.jms.JMSException;
|
||||
import jakarta.jms.Message;
|
||||
import jakarta.jms.MessageConsumer;
|
||||
import jakarta.jms.MessageProducer;
|
||||
import jakarta.jms.Queue;
|
||||
import jakarta.jms.Session;
|
||||
|
||||
/**
|
||||
* This class provides integration with MQ for the asynchronous method executions.
|
||||
*
|
||||
* @author brian@inteligr8.com
|
||||
*/
|
||||
@Component("async.mq")
|
||||
public class MqAsyncService extends AbstractMqAsyncService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private JmsPoolConnectionFactory factory;
|
||||
|
||||
/**
|
||||
* @PostConstruct does not work in ACS
|
||||
*/
|
||||
@PostConstruct
|
||||
protected void init() {
|
||||
if (!this.enabled)
|
||||
return;
|
||||
|
||||
super.init();
|
||||
|
||||
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(this.url);
|
||||
|
||||
JmsPoolConnectionFactory pool = new JmsPoolConnectionFactory();
|
||||
pool.setConnectionFactory(factory);
|
||||
pool.setMaxConnections(this.maxConnections);
|
||||
pool.start();
|
||||
|
||||
this.factory = pool;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
protected void uninit() {
|
||||
super.uninit();
|
||||
|
||||
if (this.factory != null)
|
||||
this.factory.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void poll() throws AsyncProcessException {
|
||||
this.logger.trace("poll()");
|
||||
this.isAsync.set(true);
|
||||
|
||||
try {
|
||||
Connection mqcon = this.factory.createConnection(this.username, this.password);
|
||||
try {
|
||||
mqcon.setClientID(this.clientId + "-service-" + this.hostname);
|
||||
|
||||
Session mqsession = mqcon.createSession(true, Session.CLIENT_ACKNOWLEDGE);
|
||||
try {
|
||||
this.logger.debug("Polling messages for asynchronous policy execution");
|
||||
this.pollErrors(mqsession);
|
||||
this.pollMain(mqsession);
|
||||
} finally {
|
||||
mqsession.close();
|
||||
}
|
||||
} finally {
|
||||
mqcon.close();
|
||||
}
|
||||
} catch (JMSException je) {
|
||||
throw new AsyncProcessException("A JMS messaging issue occurred", je);
|
||||
}
|
||||
}
|
||||
|
||||
private void pollErrors(Session mqsession) throws JMSException {
|
||||
this.logger.debug("Polling previously errored messages");
|
||||
|
||||
Queue mqqueue = mqsession.createQueue(this.errorQueueName);
|
||||
Set<String> msgIds = new HashSet<>();
|
||||
int ackd = 0;
|
||||
|
||||
MessageConsumer consumer = mqsession.createConsumer(mqqueue);
|
||||
try {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
Boolean processed = this.pollTx(mqsession, consumer, msgIds);
|
||||
if (processed == null) {
|
||||
break;
|
||||
} else if (processed.booleanValue()) {
|
||||
ackd++;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
consumer.close();
|
||||
}
|
||||
|
||||
this.logger.info("Successfully processed {} of {} previously errored messages", ackd, msgIds.size());
|
||||
}
|
||||
|
||||
private void pollMain(Session mqsession) throws JMSException {
|
||||
this.logger.debug("Polling ongoing messages ...");
|
||||
|
||||
Queue mqqueue = mqsession.createQueue(this.queueName);
|
||||
this.pollMainThreaded(mqsession, mqqueue);
|
||||
}
|
||||
|
||||
@Threaded(name = "mq-poll", join = true)
|
||||
private void pollMainThreaded(Session mqsession, Queue mqqueue) throws JMSException {
|
||||
MessageConsumer consumer = mqsession.createConsumer(mqqueue);
|
||||
try {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
pollTx(mqsession, consumer, null);
|
||||
}
|
||||
} finally {
|
||||
consumer.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@TransactionalRetryable(maxRetries = 3)
|
||||
@AuthorizedAsSystem
|
||||
private Boolean pollTx(Session mqsession, MessageConsumer consumer, Set<String> msgIds) throws JMSException {
|
||||
Message mqmsg = consumer.receive();
|
||||
|
||||
if (msgIds != null && !msgIds.add(mqmsg.getJMSMessageID())) {
|
||||
this.logger.debug("Received a message again; assuming we have (re)tried all errored messages: {}", mqmsg.getJMSMessageID());
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.processIncomingMessage(mqsession, mqmsg, msgIds != null)) {
|
||||
mqmsg.acknowledge();
|
||||
return true;
|
||||
}
|
||||
} catch (RuntimeException | Error e) {
|
||||
this.logger.error("An unexpected issue occurred", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processIncomingMessage(Session mqsession, Message mqmsg, boolean isErrorQueue) throws JMSException {
|
||||
String msgId = mqmsg.getJMSMessageID();
|
||||
this.logger.debug("Received message: {}", msgId);
|
||||
|
||||
String type = mqmsg.getJMSType();
|
||||
Matcher matcher = this.typePattern.matcher(type);
|
||||
if (!matcher.find()) {
|
||||
this.logger.warn("The queue has a message ('{}') with an unsupported JMS type: {}", msgId, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> beanClass = Class.forName(matcher.group(2));
|
||||
this.logger.trace("Preparing to execute using bean type: {}", beanClass);
|
||||
Object bean = this.getApplicationContext().getBean(beanClass);
|
||||
this.logger.trace("Found qualifying bean: {}", bean);
|
||||
|
||||
String methodName = matcher.group(3);
|
||||
Method method = this.findMethod(beanClass, methodName);
|
||||
this.logger.trace("Found qualifying method: {}", method);
|
||||
Parameter[] params = method.getParameters();
|
||||
|
||||
Object[] args = new Object[params.length];
|
||||
|
||||
for (int a = 0; a < args.length; a++) {
|
||||
Object arg = mqmsg.getObjectProperty("arg" + a);
|
||||
if (arg == null)
|
||||
continue;
|
||||
|
||||
args[a] = this.unmarshal(params[a], arg);
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "onLoadDynamicModel":
|
||||
args[1] = args[0];
|
||||
args[0] = this.loadModel((NodeRef) args[1]);
|
||||
}
|
||||
|
||||
method.invoke(bean, args);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
this.logger.error("A bean could not be found; will try on next restart");
|
||||
this.logger.error("The bean '{}' could not be found: {}", matcher.group(2), cnfe.getMessage());
|
||||
if (isErrorQueue)
|
||||
return false;
|
||||
this.moveToErrorQueue(mqsession, mqmsg);
|
||||
} catch (IOException ie) {
|
||||
this.logger.warn("This should never happen: " + ie.getMessage());
|
||||
// return to queue and retry indefinitely
|
||||
return false;
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
this.logger.error("A bean enum argument could not be constructed; will try on next restart");
|
||||
this.logger.error("An argument could not be The bean '{}' could not be found: {}", matcher.group(2), nsme.getMessage());
|
||||
if (isErrorQueue)
|
||||
return false;
|
||||
this.moveToErrorQueue(mqsession, mqmsg);
|
||||
} catch (IllegalAccessException iae) {
|
||||
this.logger.error("A bean method was not accessible (public); will try on next restart");
|
||||
this.logger.warn("The bean '{}' method '{}' is not accessible: {}", matcher.group(2), matcher.group(3), iae.getMessage());
|
||||
if (isErrorQueue)
|
||||
return false;
|
||||
this.moveToErrorQueue(mqsession, mqmsg);
|
||||
} catch (InstantiationException | InvocationTargetException ie) {
|
||||
this.logger.error("A bean method execution failed; will try on next restart");
|
||||
this.logger.warn("The bean '{}' method '{}' execution failed: {}", matcher.group(2), matcher.group(3), ie.getMessage());
|
||||
if (isErrorQueue)
|
||||
return false;
|
||||
this.moveToErrorQueue(mqsession, mqmsg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void moveToErrorQueue(Session mqsession, Message mqmsg) throws JMSException {
|
||||
Queue mqqueue = mqsession.createQueue(this.errorQueueName);
|
||||
|
||||
MessageProducer producer = mqsession.createProducer(mqqueue);
|
||||
try {
|
||||
producer.send(mqmsg);
|
||||
} finally {
|
||||
producer.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void push(Object callbackBean, String callbackMethod, List<Object> args) throws AsyncProcessException {
|
||||
this.logger.trace("push({}, {}, {})", callbackBean.getClass(), callbackMethod, args);
|
||||
|
||||
UUID msgId = UUID.randomUUID();
|
||||
|
||||
try {
|
||||
Connection mqcon = this.factory.createConnection(this.username, this.password);
|
||||
try {
|
||||
mqcon.setClientID(this.clientId + "-client-" + this.hostname);
|
||||
|
||||
Session mqsession = mqcon.createSession(true, Session.AUTO_ACKNOWLEDGE);
|
||||
try {
|
||||
this.logger.trace("Sending policy as message: {} => {}", callbackMethod, msgId);
|
||||
|
||||
Queue mqqueue = mqsession.createQueue(this.queueName);
|
||||
|
||||
Message mqmsg = mqsession.createMessage();
|
||||
mqmsg.setJMSMessageID(msgId.toString());
|
||||
mqmsg.setJMSType("v1:" + callbackBean.getClass() + "#" + callbackMethod);
|
||||
|
||||
int i = 0;
|
||||
for (Object arg : args)
|
||||
mqmsg.setObjectProperty("arg" + (i++), this.marshal(arg));
|
||||
|
||||
MessageProducer producer = mqsession.createProducer(mqqueue);
|
||||
try {
|
||||
producer.send(mqmsg);
|
||||
} finally {
|
||||
producer.close();
|
||||
}
|
||||
|
||||
this.logger.debug("Sent node as message: {} => {}", callbackMethod, msgId);
|
||||
} finally {
|
||||
mqsession.close();
|
||||
}
|
||||
} finally {
|
||||
mqcon.close();
|
||||
}
|
||||
} catch (JMSException je) {
|
||||
throw new AsyncProcessException("A JMS messaging issue occurred", je);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package com.inteligr8.alfresco.annotations.util;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import org.springframework.transaction.annotation.Isolation;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
|
||||
public class JtaTransactionalAnnotationAdapter implements TransactionalAnnotationAdapter {
|
||||
|
||||
public static final String JTA_INTERFACE_NAME = "jakarta.transaction.Transactional";
|
||||
|
||||
private final Transactional txl;
|
||||
|
||||
public static JtaTransactionalAnnotationAdapter cast(Object obj) {
|
||||
return new JtaTransactionalAnnotationAdapter((Transactional) obj);
|
||||
}
|
||||
|
||||
public JtaTransactionalAnnotationAdapter(Transactional txl) {
|
||||
this.txl = txl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Propagation getPropagation() {
|
||||
switch (this.txl.value()) {
|
||||
case MANDATORY:
|
||||
return Propagation.MANDATORY;
|
||||
case REQUIRED:
|
||||
return Propagation.REQUIRED;
|
||||
case REQUIRES_NEW:
|
||||
return Propagation.REQUIRES_NEW;
|
||||
case SUPPORTS:
|
||||
return Propagation.SUPPORTS;
|
||||
case NOT_SUPPORTED:
|
||||
return Propagation.NOT_SUPPORTED;
|
||||
case NEVER:
|
||||
return Propagation.NEVER;
|
||||
default:
|
||||
throw new IllegalStateException("This should never happen");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Isolation getIsolation() {
|
||||
return Isolation.DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeoutInSeconds() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Class<? extends Throwable>[] getRollbackFor() {
|
||||
return this.txl.rollbackOn();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Class<? extends Throwable>[] getNoRollbackFor() {
|
||||
return this.txl.dontRollbackOn();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
module.id=${project.groupId}.${project.artifactId}
|
||||
module.title=${project.name}
|
||||
module.description=${project.description}
|
||||
module.version=${project.version}
|
||||
|
||||
module.repo.version.min=6.0
|
||||
#module.repo.version.max=
|
||||
|
||||
module.depends.com.inteligr8.alfresco.aspectj-platform-module=1.0-*
|
@@ -0,0 +1,224 @@
|
||||
package com.inteligr8.alfresco.annotations;
|
||||
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.IllegalTransactionStateException;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@Component
|
||||
public class TransactionalTest extends AbstractLifecycleBean {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Override
|
||||
protected void onBootstrap(ApplicationEvent event) {
|
||||
this.logger.info("Running test: " + this.getClass());
|
||||
Assert.isNull(AlfrescoTransactionSupport.getTransactionId(), "An unexpected transaction: " + AlfrescoTransactionSupport.getTransactionId());
|
||||
|
||||
this.tryOutsideTx();
|
||||
this.tryWithinTx();
|
||||
this.tryWithinReadonlyTx();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShutdown(ApplicationEvent event) {
|
||||
}
|
||||
|
||||
private void tryOutsideTx() {
|
||||
this.logger.info("Running outside TX test");
|
||||
|
||||
this.tryDefaultTransactional(null, false);
|
||||
this.tryReadOnlyTransactional(null, false);
|
||||
this.tryRetryOnlyTransactional(null);
|
||||
this.trySupportsTransactional(null, false);
|
||||
this.tryRequiresNewTransactional(null, false);
|
||||
this.tryRequiredTransactional(null, false);
|
||||
this.tryNeverTransactional(null);
|
||||
|
||||
try {
|
||||
this.tryNoSupportsTransactional();
|
||||
} catch (IllegalTransactionStateException uoe) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
try {
|
||||
this.tryMandatoryTransactional(null, false);
|
||||
throw new IllegalStateException();
|
||||
} catch (IllegalTransactionStateException itse) {
|
||||
// suppress
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
private void tryWithinTx() {
|
||||
this.logger.info("Running inside read/write TX test");
|
||||
|
||||
String txId = AlfrescoTransactionSupport.getTransactionId();
|
||||
boolean readonly = false;
|
||||
|
||||
this.tryDefaultTransactional(txId, readonly);
|
||||
this.tryReadOnlyTransactional(txId, readonly);
|
||||
this.tryRetryOnlyTransactional(txId);
|
||||
this.trySupportsTransactional(txId, readonly);
|
||||
this.tryRequiresNewTransactional(txId, readonly);
|
||||
this.tryRequiredTransactional(txId, readonly);
|
||||
this.tryMandatoryTransactional(txId, readonly);
|
||||
|
||||
try {
|
||||
this.tryNoSupportsTransactional();
|
||||
throw new IllegalStateException();
|
||||
} catch (IllegalTransactionStateException uoe) {
|
||||
// suppress
|
||||
}
|
||||
|
||||
try {
|
||||
this.tryNeverTransactional(txId);
|
||||
throw new IllegalStateException();
|
||||
} catch (IllegalTransactionStateException itse) {
|
||||
// suppress
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
private void tryWithinReadonlyTx() {
|
||||
this.logger.info("Running inside read-only TX test");
|
||||
|
||||
String txId = AlfrescoTransactionSupport.getTransactionId();
|
||||
boolean readonly = true;
|
||||
|
||||
this.tryDefaultTransactional(txId, readonly);
|
||||
this.tryReadOnlyTransactional(txId, readonly);
|
||||
this.tryRetryOnlyTransactional(txId);
|
||||
this.trySupportsTransactional(txId, readonly);
|
||||
this.tryRequiresNewTransactional(txId, readonly);
|
||||
this.tryRequiredTransactional(txId, readonly);
|
||||
|
||||
try {
|
||||
this.tryNoSupportsTransactional();
|
||||
throw new IllegalStateException();
|
||||
} catch (IllegalTransactionStateException uoe) {
|
||||
// suppress
|
||||
}
|
||||
|
||||
try {
|
||||
this.tryMandatoryTransactional(txId, readonly);
|
||||
throw new IllegalStateException();
|
||||
} catch (IllegalTransactionStateException itse) {
|
||||
// suppress
|
||||
}
|
||||
|
||||
try {
|
||||
this.tryNeverTransactional(txId);
|
||||
throw new IllegalStateException();
|
||||
} catch (IllegalTransactionStateException itse) {
|
||||
// suppress
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
private void tryDefaultTransactional(String originTxId, boolean originReadonly) {
|
||||
Assert.hasText(AlfrescoTransactionSupport.getTransactionId(), "Expected a transaction");
|
||||
Assert.isTrue(TxnReadState.TXN_READ_WRITE.equals(AlfrescoTransactionSupport.getTransactionReadState()), "Expected a read/write transaction");
|
||||
if (originTxId != null) {
|
||||
if (originReadonly) {
|
||||
// changed from readonly to read/write; need new TX
|
||||
Assert.isTrue(!AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected a different transaction: " + AlfrescoTransactionSupport.getTransactionId() + " == " + originTxId);
|
||||
} else {
|
||||
// no changes; same TX
|
||||
Assert.isTrue(AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected the same transaction: " + AlfrescoTransactionSupport.getTransactionId() + " != " + originTxId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
private void tryReadOnlyTransactional(String originTxId, boolean originReadonly) {
|
||||
Assert.hasText(AlfrescoTransactionSupport.getTransactionId(), "Expected a transaction");
|
||||
Assert.isTrue(TxnReadState.TXN_READ_ONLY.equals(AlfrescoTransactionSupport.getTransactionReadState()), "Expected a readonly transaction");
|
||||
if (originTxId != null) {
|
||||
if (originReadonly) {
|
||||
// no changes; same TX
|
||||
Assert.isTrue(AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected the same transaction: " + AlfrescoTransactionSupport.getTransactionId() + " != " + originTxId);
|
||||
} else {
|
||||
// changed from read/write to readonly; need new TX
|
||||
Assert.isTrue(!AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected a different transaction: " + AlfrescoTransactionSupport.getTransactionId() + " == " + originTxId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
private void trySupportsTransactional(String originTxId, boolean originReadonly) {
|
||||
if (originTxId == null) {
|
||||
Assert.isNull(AlfrescoTransactionSupport.getTransactionId(), "Unexpected transaction");
|
||||
} else {
|
||||
Assert.hasText(AlfrescoTransactionSupport.getTransactionId(), "Expected a transaction");
|
||||
Assert.isTrue(originReadonly == TxnReadState.TXN_READ_ONLY.equals(AlfrescoTransactionSupport.getTransactionReadState()), "Expected the same read-state transaction");
|
||||
Assert.isTrue(AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected the same transaction: " + AlfrescoTransactionSupport.getTransactionId() + " != " + originTxId);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
private void tryRequiredTransactional(String originTxId, boolean originReadonly) {
|
||||
Assert.hasText(AlfrescoTransactionSupport.getTransactionId(), "Expected a transaction");
|
||||
Assert.isTrue(TxnReadState.TXN_READ_WRITE.equals(AlfrescoTransactionSupport.getTransactionReadState()), "Expected a read/write transaction");
|
||||
if (originTxId != null) {
|
||||
if (originReadonly) {
|
||||
// changed from readonly to read/write; need new TX
|
||||
Assert.isTrue(!AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected a different transaction: " + AlfrescoTransactionSupport.getTransactionId() + " == " + originTxId);
|
||||
} else {
|
||||
// no changes; same TX
|
||||
Assert.isTrue(AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected the same transaction: " + AlfrescoTransactionSupport.getTransactionId() + " != " + originTxId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
private void tryRequiresNewTransactional(String originTxId, boolean originReadonly) {
|
||||
Assert.hasText(AlfrescoTransactionSupport.getTransactionId(), "Expected a transaction");
|
||||
Assert.isTrue(TxnReadState.TXN_READ_WRITE.equals(AlfrescoTransactionSupport.getTransactionReadState()), "Expected a read/write transaction");
|
||||
if (originTxId != null)
|
||||
Assert.isTrue(!AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected a different transaction: " + AlfrescoTransactionSupport.getTransactionId() + " == " + originTxId);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.MANDATORY)
|
||||
private void tryMandatoryTransactional(String originTxId, boolean originReadonly) {
|
||||
if (originTxId == null) {
|
||||
throw new IllegalStateException();
|
||||
} else {
|
||||
Assert.hasText(AlfrescoTransactionSupport.getTransactionId(), "Expected a transaction");
|
||||
Assert.isTrue(AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected the same transaction: " + AlfrescoTransactionSupport.getTransactionId() + " != " + originTxId);
|
||||
Assert.isTrue(originReadonly == TxnReadState.TXN_READ_ONLY.equals(AlfrescoTransactionSupport.getTransactionReadState()), "Expected the same read-state transaction");
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
private void tryNoSupportsTransactional() {
|
||||
Assert.isNull(AlfrescoTransactionSupport.getTransactionId(), "Expected no transaction");
|
||||
Assert.isTrue(TxnReadState.TXN_NONE.equals(AlfrescoTransactionSupport.getTransactionReadState()), "Expected not transaction");
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
private void tryNeverTransactional(String originTxId) {
|
||||
if (originTxId == null) {
|
||||
Assert.isNull(AlfrescoTransactionSupport.getTransactionId(), "Unexpected transaction");
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
@TransactionalRetryable
|
||||
private void tryRetryOnlyTransactional(String originTxId) {
|
||||
if (originTxId == null) {
|
||||
Assert.isTrue(AlfrescoTransactionSupport.getTransactionId() != null, "Expected a new transaction");
|
||||
} else {
|
||||
Assert.isTrue(AlfrescoTransactionSupport.getTransactionId().equals(originTxId), "Expected the same transaction: " + AlfrescoTransactionSupport.getTransactionId() + " != " + originTxId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user