我有一个可行的解决方案。这不是最好的解决方案,但它确实工作得非常好。
我们所做的是创建一个非常简单的ActivationSpecWrapper类来扩展 IBM com.ibm.mq.connector.inbound.ActivationSpecImpl类。这个包装类有一个公共的 set/get 属性(asJNDI)。该类的目的是通过 JNDI 上下文读取应用服务器中定义的 Properties 类,该类包含要在激活 MDB 时分配的所有属性。
首先,创建新的ActivationSpecWrapper类。您可以将其放入您选择的任何包装中。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.InitialContext;
import com.ibm.mq.connector.inbound.ActivationSpecImpl;
public class ActivationSpecWrapper extends ActivationSpecImpl
{
private static final long serialVersionUID = -529716553593856979L;
private static final String sourceClass = ActivationSpecWrapper.class.getName();
private static final Logger log = Logger.getLogger(sourceClass);
private String asJNDI = null;
public void setAsJNDI(String asJNDI)
{
log.config("asJNDI = " + asJNDI);
this.asJNDI = asJNDI;
try
{
final InitialContext ctx = new InitialContext();
final Properties properties = (Properties) ctx.lookup(asJNDI);
for (final Object key : properties.keySet())
{
try
{
final String value = properties.getProperty((String) key);
final Object field = getSetter((String) key);
if (field != null)
{
if (field instanceof Field)
{
log.fine("Setting " + key + " via Field " + (String) key + " = " + value);
((Field) field).set(this, value);
}
else
{
log.fine("Setting " + key + " via Method " + (String) key + " = " + value);
((Method) field).invoke(this, value);
}
log.config(key + " = " + value);
}
else
{
log.warning("Invalid ActivationSpec Field: " + key);
}
}
catch (final NoSuchFieldException e)
{
log.throwing(sourceClass, "setAsJNDI", e);
}
}
}
catch (final Exception e)
{
log.log(Level.SEVERE, "Error looking up " + asJNDI, e);
return;
}
}
public String getAsJNDI()
{
return asJNDI;
}
private static Object getField(String fieldName) throws NoSuchFieldException
{
return ActivationSpecWrapper.class.getField(fieldName);
}
private static Object getSetter(String fieldName) throws NoSuchFieldException
{
try
{
final StringBuilder sb = new StringBuilder(fieldName.length() + 3).append("set").append(fieldName);
sb.setCharAt(3, Character.toUpperCase(sb.charAt(3)));
return ActivationSpecWrapper.class.getMethod(sb.toString(), String.class);
}
catch (final NoSuchMethodException e)
{
return getField(fieldName);
}
}
}
要实现该类,您只需修改wmq.jmsra.rar文件中的META-INF/ra.xml文件。将 ActivationSpecImpl 类的一次出现更改为您的类。这将是它使用的新传入连接工厂的ActivationSpecWrapper类。因此,现在您的包装类可以查看应用服务器以获取要使用的属性。
我这样做如下:
: jar -xvf wmq.jmsra.rar META-INF/ra.xml
: perl -pi -e 's/com\.ibm\.mq\.connector\.inbound\.ActivationSpecImpl/your.new.package.ActivatonSpecWrapper/g' META-INF/ra.xml
: jar -uvf wmq.jmsra.rar META-INF/ra.xml
在修改META-INF/ra.xml之前看起来像:
<activationspec>
<activationspec-class>
com.ibm.mq.connector.inbound.ActivationSpecImpl
</activationspec-class>
<required-config-property>
<config-property-name>destination</config-property-name>
</required-config-property>
<required-config-property>
<config-property-name>destinationType</config-property-name>
</required-config-property>
</activationspec>
更改后,META-INF/ra.xml应如下所示:
<activationspec>
<activationspec-class>
your.new.package.ActivatonSpecWrapper
</activationspec-class>
<required-config-property>
<config-property-name>destination</config-property-name>
</required-config-property>
<required-config-property>
<config-property-name>destinationType</config-property-name>
</required-config-property>
</activationspec>
现在您需要将新包添加到 RAR 文件中。它应该在标准目录结构中。像这样:
: jar -uvf wmq.jmsra.rar your/new/package/ActivationSpecWrapper.class
问题源于 IBM 将主机/端口/队列管理器/通道(等)放入激活规范而不是管理对象。它属于管理对象,因为它是 MDB 队列的连接工厂。IBM 在那里只允许两个属性。
此外,如果您使用 glassfish,oracle 确实为需要资源适配器的 MDB 类搞砸了,因为 glassfish @MessageDriven注释假定 JMS 的应用程序容器默认资源适配器 ( OpenMQ )。这意味着供应商特定的ActivationSpecImpl不起作用,因此在通过glassfish-ejb-jar.xml切换资源适配器之后,IMB 的主机/端口和其他激活配置属性的自定义参数不通过注释得到支持。
JBoss 允许@ResourceAdapter注释更改资源适配器,但 Glassfish 仅允许通过glassfish-ejb-jar.xml文件进行此操作。当使用它时,您只需要使用三个激活配置属性(destinationType)来注释您的 MDB。您将在 JNDI 发布的属性中放置的所有其他内容。
glassfish-ejb-jar.xml应该如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<glassfish-ejb-jar>
<enterprise-beans >
<unique-id>1</unique-id>
<ejb>
<ejb-name>MyMDB</ejb-name>
<mdb-resource-adapter>
<resource-adapter-mid>wmq.jmsra</resource-adapter-mid>
<activation-config>
<activation-config-property>
<activation-config-property-name>asJNDI</activation-config-property-name>
<activation-config-property-value>mq/InboundMessages</activation-config-property-value>
</activation-config-property>
</activation-config>
</mdb-resource-adapter>
</ejb>
</enterprise-beans>
</glassfish-ejb-jar>
MDB @MessageDriven注释将如下所示:
@MessageDriven(activationConfig =
{
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/InboundMessage_queue"),
@ActivationConfigProperty(propertyName = "useJNDI", propertyValue = "true") })
public class MyMDB implement MessageListener
{
public void onMessage(Message message)
{
// message handler code goes here...
}
}
完成这项工作的最后一步是将mq/InboundMessages属性添加到 JDNI,以定义 MQ 侦听器资源的工厂属性。这就是它在domain.xml文件中的定义方式:
<custom-resource res-type="java.util.Properties" jndi-name="mq/InboundMessages" factory-class="org.glassfish.resources.custom.factory.PropertiesFactory">
<property name="hostName" value="mqserver"></property>
<property name="port" value="1422"></property>
<property name="queueManager" value="MQMNGR"></property>
<property name="channel" value="MQMNGR.SM.S1"></property>
<property name="transportType" value="CLIENT"></property>
</custom-resource>
我希望这有帮助。这不是最简单的解决方案,但它足够简单,而且一旦建立,它就非常便携,并且允许应用服务器管理员管理与 MQ 的连接详细信息,而不是开发人员。