??xml version="1.0" encoding="utf-8" standalone="yes"?>
One approach for doing this to create a new UserType as follows.
package mypackage; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.sql.Blob; import cirrus.hibernate.Hibernate; import cirrus.hibernate.HibernateException; import cirrus.hibernate.UserType; public class BinaryBlobType implements UserType { public int[] sqlTypes() { return new int[] { Types.BLOB }; } public Class returnedClass() { return byte[].class; } public boolean equals(Object x, Object y) { return (x == y) || (x != null && y != null && java.util.Arrays.equals((byte[]) x, (byte[]) y)); } public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException { Blob blob = rs.getBlob(names[0]); return blob.getBytes(1, (int) blob.length()); } public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException { st.setBlob(index, Hibernate.createBlob((byte[]) value)); } public Object deepCopy(Object value) { if (value == null) return null; byte[] bytes = (byte[]) value; byte[] result = new byte[bytes.length]; System.arraycopy(bytes, 0, result, 0, bytes.length); return result; } public boolean isMutable() { return true; } }
The BinaryBlobType will convert a blob into a byte array and back again.
Here's how to use it. First, define an entity that contains a byte[] property:
public class ImageValue { private long id; private image byte[]; public long getId() { return id; } public void setId(long id) { this.id = id; } public byte[] getImage() { return image; } public void setImage(byte[] image) { this.image = image; } }
Then map a blob column onto the byte[] property:
<class name="ImageValue" table="IMAGE_VALUE">
<id name="id/>
<property name="image" column="IMAGE" type="mypackage.BinaryBlobType"/>
</class>
Notes:
1) Blobs aren't cachable. By converting the blob into a byte array, you can now cache the entity.
2) This approach reads the whole blob into memory at once.
3) The above type is known to work for reading blobs out of the db. Other usage patterns might also work.
Comments (GK)
I changed isMutable() to return true, since an array is a mutable object.
The use of setBlob() will work on some drivers, but not all. I think its more portable to use setBytes() or even setBinaryStream().
comments (db)
db's comment above was right, setBlob() didn't work on Oracle, I used setBytes().
comments (Chad Woolley)
Below is a modified nullsafeset() that i needed to use to get it to work with tomcat 4.1.27 & oracle 8/9i - the normal calls don't work through the tomcat/dbcp connection pool wrapper objects... (this caused me great pain)
pls note that the setBytes() doesn't seem to work with oracle driver & hibernate
d.birch@eclipsegroup.com.au
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException { if(st instanceof org.apache.commons.dbcp.DelegatingPreparedStatement && ((org.apache.commons.dbcp.DelegatingPreparedStatement)st).getDelegate() instanceof oracle.jdbc.OraclePreparedStatement) { oracle.sql.BLOB blob = oracle.sql.BLOB.createTemporary( ((org.apache.commons.dbcp.PoolableConnection)st.getConnection()).getDelegate(), false, oracle.sql.BLOB.DURATION_SESSION); blob.open(BLOB.MODE_READWRITE); OutputStream out = blob.getBinaryOutputStream(); try { out.write((byte[])value); out.flush(); out.close(); } catch(IOException e) { throw new SQLException("failed write to blob" + e.getMessage()); } blob.close(); ((oracle.jdbc.OraclePreparedStatement)((org.apache.commons.dbcp.DelegatingPreparedStatement)st).getDelegate()).setBLOB(index, blob); } else { st.setBlob(index, Hibernate.createBlob((byte[]) value)); } } //and.. note the null check, oracle drivers return a null blob... public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException { final Blob blob = rs.getBlob(names[0]); return blob != null?blob.getBytes(1, (int)blob.length()):null; }
/ comments Vanitha
I had to use the user type to save pdfs as oraBLOBs in oracle 91 database. nullsafeSet
needed a sligh modification , or else ther was a classcastexception. Used oracle Blob instead of Hibernate Blob type and it works.
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException { oracle.sql.BLOB t_blob = oracle.sql.BLOB.createTemporary(((org.jboss.resource.adapter.jdbc.WrappedConnection) st.getConnection()).getUnderlyingConnection(), false, oracle.sql.BLOB.DURATION_SESSION); OutputStream t_out = null; t_blob.open(BLOB.MODE_READWRITE); t_out = t_blob.getBinaryOutputStream(); try { t_out.write((byte[]) value); t_out.flush(); t_out.close(); } catch (IOException e) { throw new SQLException("failed write to blob" + e.getMessage()); } t_blob.close(); st.setBlob(index, t_blob); }
</code>
1. 创徏表:
2. 创徏存储q程Q?
q个results_out是一个游标类型,用来q回查找的结果集?/p>
3. 完整实现代码Q?
4. 试代码Q在q里使用了Junit4试Q?
5. 试的输出结果:
ActiveMQ 实践之\(? ActiveMQ 4.x +JBoss 4.x MDP实战?/font> xml 代码
其中destination,destinationType是ra.xml里面提供的配|属?(q里官方的文档是Destination,DestinationType, 而实际上activemq-ra.rar里面的ra.xml是destination,destinationTypeQ注意大写区别) ?font face="Arial">. 配置jboss.xml xml 代码
2.) 使用ActiveMQ RA的配|?/p> xml 代码
2. jboss.xml 配置invoker proxy和container 支持ActiveMQ RA xml 代码
MessageDrivenBean的container配置,q里?container-name>必须和上面的
xml 代码
以上是ActiveMQ+JBoss InBound 的配|?/font> ?在Servlet中通过发送消?验证上面的Message Driven Bean Z验证q个MessageDrivenBean能够正常工作,我用一个很单的servlet向这个queue发送消?前一的activemq-ds.xml 已经提供在启动的时候绑定了JNDI activemq/QueueConnectionFactory,activemq/queue/outbound,我们直接使用p? java 代码
?关于durable方式订阅topic的补充说?br /> 使用durable方式,你需要在ejb-jar.xml中额外的配置,subscriptionDurability,clientId,subscriptionName xml 代码
ok q样可以用durable方式订阅topic了?/p> 参考文? |
ActiveMQ本n是开源项?所以采用ActiveMQ的项目往往也是以其他开源Y件共同构?目前L开源应用服务器有Boss,geronimo,JOnAsQ而其中geronimo 默认的JMS Provider是ActiveMQQ那我们q重介lActiveMQ与JBoss,JOnAs的整合方?/p> 本文参考了 Integrating Apache ActiveMQ with JBoss?a >JBoss Integration,再根据笔者实际整合经验ȝ而成?/p> 一.整合需要的环境. ?整合步骤 1. 步骤一: 解压activemq-rar-4.1.0-incubator.rar ?jboss-4.0.5.GA\server\default\deploy\activemq-ra.rar (q个是目录名? 下面是activemq-rar.rar目录下面的文件和子目?h意红色标记的地方(后面会逐一说明,整合的过E? activeio-core-3.0.0-incubator.jar activemq-core-4.1.0-incubator.jar activemq-ra-4.1.0-incubator.jar backport-util-concurrent-2.1.jar commons-logging-1.0.3.jar derby-10.1.1.0.jar geronimo-j2ee-management_1.0_spec-1.0.jar spring-2.0.jar spring-1.2.6.jar xbean-spring-2.7.jar broker-config.xml META-INF 2.步骤? 删除多余?font color="#ff0000">spring-1.2.6.jar,׃4.1.0的ra里面包含?个不同版本的spring会触发一个exception的?https://issues.apache.org/activemq/browse/AMQ-1124, 而且Z以后能够使用新的spring schema配置方式,我们q里会删?font color="#ff0000">spring-1.2.6.jar,保留spring-2.0.jar?最新的snapshot version的ra已经L了这个多余的spring-1.2.6.jar). 3.步骤? 修改META-INF\ra.xml,让JBoss使用broker-config.xml 作ؓ默认的配|文仉|borker. 修改下面的地?br />
改ؓ:
表示使用broker-config.xml来配|启动ActiveMQ. 4.步骤? 修改borker-config.xml,默认的borker-config.xml会生一个错?无论是我使用的版本还是最后的snapshot版本,默认的borker-config.xml都会让xbean-spring 2.7(snapshot 使用的是2.8)抛出exception.解决的办法如?/div>
?nbsp;
改ؓ
卛_
5.步骤? xbean-spring-2.7.jar (或者是2.8) 复制到jboss-4.0.5.GA\server\default\lib下面
?使用整合完毕的ActiveMQ作ؓdsl定到JBoss的JNDI服务?/div>
~写jboss-4.0.5.GA\server\default\depoly\activemq-ds.xml
xml 代码
启动JBoss.如果看见以下信息pCActiveMQ已经成功启动,q且使用上面的ds配置文g成功地将topic/queuel定CJNDI服务上?/div>
......
[TransportConnector] Connector tcp://localhost:61616 Started
[NetworkConnector] Network Connector bridge Started [BrokerService] ActiveMQ JMS Message Broker (localhost, ID:MyNoteBook-2165-1173250880171-1:0) started ......
[ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=activemq/QueueConnectionFactory' to JNDI name 'java:activemq/QueueConnectionFactory'
[ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=activemq/TopicConnectionFactory' to JNDI name 'java:activemq/TopicConnectionFactory' [AdminObject] Bound admin object 'org.apache.activemq.command.ActiveMQQueue' at 'activemq/queue/outbound' [AdminObject] Bound admin object 'org.apache.activemq.command.ActiveMQTopic' at 'activemq/topic/inbound ......
?验证ActiveMQ+JBoss
q里你可以用简单的jms clientq接到broker-config.xml里面的协议连接器上面,默认的是tcp://localhost:61616
在后面我们会在此整合基础上开发Message Driver Bean和用spring MDP 2U构?nbsp;来验证本ơActiveMQ+JBoss的整合?/div>
|
本篇主要讲解在未使用其他框架(Spring)整合情况?独立ZActiveMQ,使用JMS规范q行消息通信?br />
一.JMS回顾
因ؓActiveMQ是一个JMS Provider的实?因此在开始实作前,有必要复习下JMS的基知识
Java Message Service (JMS)是sun提出来的为J2EE提供企业消息处理的一套规?JMS目前?套规范还在用JMS 1.0.2b?.1. 1.1已经成ؓL的JMS Provider事实上的标准?
*1.1主要在session上面有一些重要改?比如支持建立同一session上的transaction,让他支持同时发送P2P(Queue)消息和接?br />
Topic消息?br />
在JMS中间主要定义?U消息模式Point-to-Point (点对?,Publich/Subscribe Model (发布/订阅?Q?br />
其中在Publich/Subscribe 模式下又有Nondurable subscription和durable subscription (持久化订?2U消息处理方式?br />
下面是JMS规范基本的接口和实现
JMS Common Interfacse PTP-Specific Interface Pub/Sub-specific interfaces
ConnectionFactory QueueConnectionFactory TopicConnectionFactory
Connection QueueConnection TopicConnection
Destination Queue Topic
Session QueueSession TopiSession
MessageProducer QueueSender TopicPublisher
MessageConsumer QueueReceiver/QueueBrwer TopicSubscriber
?使用Queue
下面以ActiveMQ example的代码ؓ主进行说?br />
1. 使用ActiveMQ的ConnectionQConnectionFactory 建立q接,注意q里没有用到pool
//建立Connection
//建立Session
2。发送消息的代码
//建立QueueSession
//使用Producer发送消息到Queue
3。接受消?在JMS规范里面,你可以?br />
以上是使用jms queue发送接受消息的基本方式
?Topic
1. 建立q接
2. 建立Session
3.创徏Producer 发送消息到Topic
4.创徏Consumer接受消息(基本上和Queue相同)
?q接ActiveMQ的方?br />
ActiveMQConnectionFactory 提供了多U连接到Broker的方?a title="ActiveMQq接协议" >activemq.apache.org/uri-protocols.html
常见的有
vm://host:port //vm
tcp://host:port //tcp
ssl://host:port //SSL
stomp://host:port //stomp协议可以跨语a,目前有很多种stomp client ?java,c#,c/c++,ruby,python...);
一.安装ActiveMQ
首先去http://activemq.apache.org/download.html 下蝲最新版?.1.0release (http://activemq.apache.org/activemq-410-release.html),
解压apache-activemq-4.1-incubator.zip(或者apache-activemq-4.1-incubator.tar.gz)目录如下:
+bin (windows下面的bat和unix/linux下面的sh)
+conf (activeMQ配置目录,包含最基本的activeMQ配置文g)
+data (默认是空?
+docs (index,replease版本里面没有文档,-.-b不知道ؓ啥不?
+example (几个例子
+lib (activemMQ使用到的lib)
-apache-activemq-4.1-incubator.jar (ActiveMQ的binary)
-LICENSE.txt
-NOTICE.txt
-README.txt
-user-guide.html
你可以用bin\activemq.bat(activemq) 启动,如果一切顺?你就会看见类g面的信息
(l节可能不一?比如路径,或者jmx,jdbc信息)
ACTIVEMQ_HOME: D:\java\framework_and_lib\activemq\apache-activemq-4.1-incubator\
bin\..
Loading message broker from: xbean:activemq.xml
INFO BrokerService - ActiveMQ null JMS Message Broker (localho
st) is starting
INFO BrokerService - For help or more information please see:
http://incubator.apache.org/activemq/
INFO ManagementContext - JMX consoles can connect to service:jmx:r
mi:///jndi/rmi://localhost:1099/jmxrmi
INFO JDBCPersistenceAdapter - Database driver recognized: [apache_derby
_embedded_jdbc_driver]
INFO DefaultDatabaseLocker - Attempting to acquire the exclusive lock
to become the Master broker
INFO DefaultDatabaseLocker - Becoming the master on dataSource: org.ap
ache.derby.jdbc.EmbeddedDataSource@1d840cd
INFO JournalPersistenceAdapter - Journal Recovery Started from: Active Jou
rnal: using 5 x 20.0 Megs at: D:\java\framework_and_lib\activemq\apache-activemq
-4.1-incubator\activemq-data\journal
INFO JournalPersistenceAdapter - Journal Recovered: 0 message(s) in transa
ctions recovered.
INFO TransportServerThreadSupport - Listening for connections at: tcp://P-SUW
EI:61616
WARN MulticastDiscoveryAgent - brokerName not set
INFO TransportConnector - Connector default Started
INFO TransportServerThreadSupport - Listening for connections at: stomp://P-S
UWEI:61613
INFO TransportConnector - Connector stomp Started
INFO NetworkConnector - Network Connector default Started
INFO BrokerService - ActiveMQ JMS Message Broker (localhost, I
D:P-SUWEI-1207-1170916242296-1:0) started
*。几个小提示
1. q个仅仅是最基础的ActiveMQ的配|?很多地方都没有配|因此不要直接用这个配|用于生产系l?br />
2. 有的时候由于端口被占用,DActiveMQ错误,ActiveMQ可能需要以下端?099(JMX),61616(默认的TransportConnector)
3. 如果没有物理|卡,或者MS的LoopBackAdpater Multicast会报一个错?/p>
? 试你的ActiveMQ
׃ActiveMQ是一个独立的jms provider,所以我们不需要其他Q何第三方服务器就可以马上做我们的试?~译
example目录下面的程?br />
ProducerTool/ConsumerTool 是JMS参考里面提到的典型应用,Producer产生消息,Consumer消费消息
而且q个例子q可以加入参数帮助你试刚才启动的本地ActiveMQ或者是q程的ActiveMQ
ProducerTool [url] broker的地址,默认的是tcp://localhost:61616
[true|flase] 是否使用topic,默认是false
[subject] subject的名?默认是TOOL.DEFAULT
[durabl] 是否持久化消?默认是false
[messagecount] 发送消息数?默认?0
[messagesize] 消息长度,默认?55
[clientID] durable为true的时?需要配|clientID
[timeToLive] 消息存活旉
[sleepTime] 发送消息中间的休眠旉
[transacte] 是否采用事务
ConsumerTool [url] broker的地址,默认的是tcp://localhost:61616
[true|flase] 是否使用topic,默认是false
[subject] subject的名?默认是TOOL.DEFAULT
[durabl] 是否持久化消?默认是false
[maxiumMessages] 接受最大消息数?0表示不限?br />
[clientID] durable为true的时?需要配|clientID
[transacte] 是否采用事务
[sleepTime] 接受消息中间的休眠时?默认?,onMeesageҎ不休?br />
[receiveTimeOut] 接受时
我们q样可以使用:
java -classpath .\apache-activemq-4.1-incubator.jar;example\bin ProducerTool tcp://192.168.3.142:61616 test.mysubject
java -classpath .\apache-activemq-4.1-incubator.jar;example\bin ConsumerTool tcp://192.168.3.142:61616 test.mysubject
当然你可以用上面的参数q行更复杂的试,持久,事务
如果出现下面的信?恭喜?你的ActiveMQ已经能够工作?br />
Connecting to URL: tcp://192.168.3.142:61616
Publishing a Message with size 255 to queue: TOOL.DEFAULT
Using non-durable publishing
Sleeping between publish 0 ms
Sending message: Message: 0 sent at: Thu Feb 08 15:05:34 CST 2007 ...
Sending message: Message: 1 sent at: Thu Feb 08 15:05:34 CST 2007 ...
。。。。。。。?/p>
Connecting to URL: tcp://192.168.3.142:61616
Consuming queue: test.mysubject
Using non-durable subscription
Received: Message: 0 sent at: Thu Feb 08 14:51:34 CST 2007 ...
Received: Message: 1 sent at: Thu Feb 08 14:51:34 CST 2007 ...
。。。?/p>
?结
我们已经下蝲,启动,q且用程序测试了我们的ActiveMQ,而后面将在这个能跑得ActiveMQq一步的C?一步一步展CActiveMQ的高U特性?/p>
先看web.xml里面关于activemq的invoke
<!--调用activemq -->
<context-param >
<param-name>brokerURI </param-name >
<param-value>/WEB-INF/activemq.xml </param-value >
</context-param>
<listener>
<listener-class>org.activemq.web.SpringBrokerContextListener</listener-class>
</listener>
郁闷了三天,今天l于把JMS弄出来了Q就是发送消息,然后消息监听器接收到了消息后发送邮件给理?/p>
先看web.xml里面关于activemq的invoke
<!--调用activemq -->
<context-param >
<param-name>brokerURI </param-name >
<param-value>/WEB-INF/activemq.xml </param-value >
</context-param>
<listener>
<listener-class>org.activemq.web.SpringBrokerContextListener</listener-class>
</listener>
然后是在上下文中定义的JmsTemplate和activemq监听
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--JMS Template-->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<bean class="org.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>tcp://localhost:61616</value>
</property>
</bean>
</property>
<property name="defaultDestinationName" value="Hello.Queue"/>
</bean>
<bean id="activeMQContainer" class="org.activemq.jca.JCAContainer">
<property name="workManager">
<bean id="workManager" class="org.activemq.work.SpringWorkManager"/>
</property>
<property name="resourceAdapter">
<bean id="activeMQResourceAdapter"
class="org.activemq.ra.ActiveMQResourceAdapter">
<property name="serverUrl" value="tcp://localhost:61616"/>
</bean>
</property>
</bean>
<!--监听 Message 的Message Driven POJO-->
<bean id="HelloPlaceBean" class="com.officetao.jms.HelloMDP" autowire="byName"/>
<bean id="HelloMDP" factory-method="addConnector" factory-bean="activeMQContainer">
<property name="activationSpec">
<bean class="org.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="Hello.Queue"/>
<property name="destinationType" value="javax.jms.Queue"/>
</bean>
</property>
<property name="ref" value="HelloBean" />
</bean>
</beans>
建立一个模拟的发送消息的beanQ内容如?/font>
final String mailContent = "新增单号?000的订? 金额";
try {
if (jmsTemplate != null)
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session)
throws JMSException {
Message message = session.createMessage();
message.setStringProperty("content", mailContent);
return message;
}
});
}
catch (Exception e) {
logger.error("JMS error when place order:", e);
}
最后就是监听消息然后采取行动的bean
public class HelloMDP implements MessageListener {
public void onMessage(javax.jms.Message arg0) {
try {
subAuthenticator subauth = new subAuthenticator("邮箱登陆?,"密码");//smtp验证 authenticator
props.put("mail.smtp.host","smtp.163.com");
props.put("mail.smtp.auth","true");
session = Session.getInstance(props,subauth);
MimeMessage message = new MimeMessage(session);
message.setRecipient(Message.RecipientType.TO,new InternetAddress("接收邮g的邮?/font>"));
message.setFrom(new InternetAddress("自己的邮?/font>"));
message.setSubject("ok");
message.setText("if you see it,it works!");
Transport.send(message);
}
catch(AuthenticationFailedException e1){
System.out.println("SMTP认证出错Q?);
}
catch (MessagingException e) {
e.printStackTrace();
}
}
public static Properties props = System.getProperties();
public static Session session = null;
/**
*此内部类定义了smtp认证Ҏ
*/
public class subAuthenticator extends Authenticator{
private String userName;
private String password;
public subAuthenticator(String user,String pass){
userName=user;
password=pass;
}
public PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(userName,password);
}
|
|