Adobe Experience Manager (AEM) 与 ActiveMQ 的集成可实现系统间的解耦和异步通信,以下是详细的集成步骤与代码实现:
一、环境准备
- AEM 环境:AEM 6.5+ 或 AEM as a Cloud Service
- ActiveMQ:版本 5.15+(推荐 5.17.x)
- Maven 项目:用于开发 AEM 组件和配置
二、ActiveMQ 配置
1. 启用 JMS 连接
修改 ActiveMQ 的
conf/activemq.xml
,确保支持 OpenWire 协议:
<transportConnectors> <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/> </transportConnectors>
2. 创建 JMS 用户(可选)
修改
conf/users.properties
添加专用用户:
jmsuser=jms password,users
三、AEM 集成步骤
1. 添加 ActiveMQ 依赖
在 AEM 项目的
pom.xml
中添加依赖:
<dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-client</artifactId> <version>5.17.10</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.36</version> </dependency>
2. 创建 OSGi 配置
定义 ActiveMQ 连接工厂配置:
import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @ObjectClassDefinition(name = "ActiveMQ Connection Factory Configuration") public @interface ActiveMQConfig { @AttributeDefinition(name = "Broker URL", description = "ActiveMQ Broker URL") String brokerUrl() default "tcp://localhost:61616"; @AttributeDefinition(name = "Username", description = "ActiveMQ Username") String username() default "admin"; @AttributeDefinition(name = "Password", description = "ActiveMQ Password") String password() default "admin"; }
3. 实现 JMS 连接工厂服务
import javax.jms.ConnectionFactory; import org.apache.activemq.ActiveMQConnectionFactory; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Modified; import org.osgi.service.metatype.annotations.Designate; @Component(service = ActiveMQService.class, immediate = true) @Designate(ocd = ActiveMQConfig.class) public class ActiveMQService { private ConnectionFactory connectionFactory; private ActiveMQConfig config; @Activate @Modified protected void activate(ActiveMQConfig config) { this.config = config; initConnectionFactory(); } private void initConnectionFactory() { ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory( config.username(), config.password(), config.brokerUrl() ); // 配置选项(可选) factory.setTrustAllPackages(true); this.connectionFactory = factory; } public ConnectionFactory getConnectionFactory() { return connectionFactory; } }
4. 实现 JMS 消息生产者
import javax.jms.*; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(service = JmsMessageProducer.class) public class JmsMessageProducer { private static final Logger LOG = LoggerFactory.getLogger(JmsMessageProducer.class); @Reference private ActiveMQService activeMQService; public void sendMessage(String queueName, String messageContent) { Connection connection = null; Session session = null; MessageProducer producer = null; try { ConnectionFactory factory = activeMQService.getConnectionFactory(); connection = factory.createConnection(); connection.start(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Destination destination = session.createQueue(queueName); producer = session.createProducer(destination); TextMessage message = session.createTextMessage(messageContent); producer.send(message); LOG.info("Message sent to queue: {}", queueName); } catch (JMSException e) { LOG.error("Failed to send message", e); } finally { // 关闭资源 closeQuietly(producer); closeQuietly(session); closeQuietly(connection); } } private void closeQuietly(AutoCloseable closeable) { if (closeable != null) { try { closeable.close(); } catch (Exception e) { LOG.warn("Error closing resource", e); } } } }
5. 实现 JMS 消息消费者(Sling Model)
import javax.jms.*; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.Model; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Model(adaptables = SlingHttpServletRequest.class) public class JmsMessageConsumer { private static final Logger LOG = LoggerFactory.getLogger(JmsMessageConsumer.class); @Reference private ActiveMQService activeMQService; public String receiveMessage(String queueName) { Connection connection = null; Session session = null; MessageConsumer consumer = null; try { ConnectionFactory factory = activeMQService.getConnectionFactory(); connection = factory.createConnection(); connection.start(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Destination destination = session.createQueue(queueName); consumer = session.createConsumer(destination); // 同步接收消息(超时时间 5 秒) Message message = consumer.receive(5000); if (message instanceof TextMessage) { return ((TextMessage) message).getText(); } } catch (JMSException e) { LOG.error("Failed to receive message", e); } finally { // 关闭资源 closeQuietly(consumer); closeQuietly(session); closeQuietly(connection); } return null; } // 省略 closeQuietly 方法(同上) }
四、AEM 组件集成示例
1. 创建 Sling Model 调用生产者
import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.Model; import org.osgi.service.component.annotations.Reference; @Model(adaptables = SlingHttpServletRequest.class) public class AemMessageSender { @Reference private JmsMessageProducer messageProducer; public void sendAemContent(String content) { messageProducer.sendMessage("aem.content.queue", content); } }
2. 创建 HTL 模板触发消息发送
<sly data-sly-use.model="com.example.models.AemMessageSender"> <button οnclick="sendContent()">Send Content to ActiveMQ</button> <script> function sendContent() { fetch('/bin/sendMessage', { method: 'POST' }) .then(response => response.text()) .then(data => console.log(data)); } </script> </sly>
3. 创建 Sling Servlet 处理消息发送请求
import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.servlets.SlingAllMethodsServlet; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import javax.servlet.Servlet; import java.io.IOException; @Component(service = Servlet.class, property = { "sling.servlet.paths=/bin/sendMessage", "sling.servlet.methods=POST" } ) public class MessageSendServlet extends SlingAllMethodsServlet { @Reference private AemMessageSender messageSender; @Override protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException { String content = request.getParameter("content"); messageSender.sendAemContent(content); response.getWriter().write("Message sent successfully"); } }
五、高级配置(可选)
1. 配置 JMS 持久化
修改
activemq.xml
启用持久化:
<persistenceAdapter> <kahaDB directory="${activemq.data}/kahadb"/> </persistenceAdapter>
2. 配置消息监听器(异步消费)
@Component(service = MessageListener.class, immediate = true) public class AemMessageListener implements MessageListener { @Activate protected void activate() { // 初始化 JMS 连接并注册监听器 // 代码略(类似消费者实现) } @Override public void onMessage(Message message) { try { if (message instanceof TextMessage) { String content = ((TextMessage) message).getText(); // 处理接收到的消息(如更新 AEM 内容) processAemContent(content); } } catch (JMSException e) { LOG.error("Error processing message", e); } } private void processAemContent(String content) { // 实现 AEM 内容处理逻辑 } }
六、验证集成效果
-
启动 ActiveMQ:
bin/activemq start
-
部署 AEM 包:将开发的 OSGi 组件打包部署到 AEM。
-
测试消息发送:
- 通过 AEM 页面触发消息发送
- 查看 ActiveMQ 控制台(http://localhost:8161/admin/queues.jsp)确认消息入队
-
测试消息接收:
- 实现消息消费者服务
- 验证消息是否正确处理
七、常见问题与解决方案
-
连接失败
- 检查 ActiveMQ 地址和端口
- 确认防火墙未阻止通信(默认 61616 端口)
-
消息丢失
- 确保使用
DeliveryMode.PERSISTENT
- 检查 ActiveMQ 持久化配置
- 确保使用
-
AEM 组件无法访问 JMS 服务
- 检查 OSGi 配置是否正确
- 确认依赖包已正确导入
通过以上步骤,你可以实现 AEM 与 ActiveMQ 的完整集成,实现系统间的异步通信和解耦。根据实际需求,你还可以进一步优化配置,如使用 JNDI 管理连接工厂、配置集群模式提高可用性等。