我的妻子已经生下了我们的女儿,而JBoss已经发布了AS 6的快照,它可以完成我们大部分的目标,让我们继续前进吧!
好的、不好的和我们的日常解决方案
因此,既然我们正生活在这条边缘,请转到 JBoss Hudson 并获取最新的成功构建(#1750或更高版本)。在服务器中安装envers.jar,并更改http监听器端口,就像我们在第二部分的结尾所做的那样。如果您使用JBOSS_HOME环境变量,请记得更新它。
由于自动注册faces servlet存在一点回归,所以请将以下内容添加到您的web.xml中
<servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping>
ZipException已经消失,所以请从合并的ICEfaces内容中清除faces-config.xml,并从您的本地存储库中删除icefaces目录,以便您可以获取原始jar的新副本。
还有另一个回归,可能与重定向和ICEfaces PushRenderer有关(也许)。如果您直接进入/Greetings上下文根,PushRenderer并不是那么“积极”,您现在必须使用完整的/Greetings/faces/greetings.xhtml。
看看妈妈,没有EARs
我们将使用EJB,并在进行重构的同时做一些事情。删除GreetingBean并创建以下类
package com.acme.greetings; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; import javax.annotation.PostConstruct; import javax.ejb.Lock; import javax.ejb.LockType; import javax.ejb.Stateful; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.Event; import javax.inject.Inject; import javax.inject.Named; import org.icefaces.application.PushRenderer; @Stateful @ApplicationScoped @Named public class GreetingServer implements Serializable { private static final long serialVersionUID = 1L; List<Greeting> greetings = new ArrayList<Greeting>(); @Inject @Added Event<Greeting> greetingAddedEvent; @Inject GreetingArchiver greetingArchiver; @Inject GreetingEcho greetingEcho; @PostConstruct public void init() { greetings = greetingArchiver.loadGreetings(); } @Lock(LockType.WRITE) public void addGreeting(Greeting greeting) { greetings.add(greeting); PushRenderer.render("greetings"); greetingAddedEvent.fire(greeting); Future<Boolean> result = greetingEcho.echo(greeting); } @Lock(LockType.READ) public List<Greeting> getGreetings() { return greetings; } }
和
package com.acme.greetings; import javax.annotation.PostConstruct; import javax.ejb.Stateful; import javax.enterprise.inject.Model; import javax.inject.Inject; import org.icefaces.application.PushRenderer; import com.icesoft.faces.context.effects.Appear; import com.icesoft.faces.context.effects.Effect; @Stateful @Model public class GreetingClient { Greeting greeting = new Greeting(); @Inject GreetingServer greetingServer; @PostConstruct public void init() { PushRenderer.addCurrentSession("greetings"); } public Effect getAppear() { return new Appear(); } public Greeting getGreeting() { return greeting; } public void setGreeting(Greeting greeting) { this.greeting = greeting; } public void addGreeting() { greetingServer.addGreeting(greeting); } }
GreetingServer保存问候语,客户端注入服务器并与它交互(我们在问候语列表中添加了一些用于并发控制的注解)。您可能会注意到我们还有一个新的注入
@Inject GreetingEcho greetingEcho;
其用法如下
Future<Boolean> result = greetingEcho.echo(greeting);
这是对GreetingEcho的异步调用,看起来像
package com.acme.greetings; import java.util.concurrent.Future; import javax.ejb.AsyncResult; import javax.ejb.Asynchronous; import javax.ejb.Stateless; @Stateless public class GreetingEcho { @Asynchronous public Future<Boolean> echo(Greeting greeting) { System.out.println(String.format("Got a new greeting: %s", greeting.getText())); return new AsyncResult<Boolean>(true); } }
这是一个有趣的EE 6结构,因为它实际上立即退出@Asynchronous方法,返回任务的句柄。好吧,祝您好运,在它完成之前取消该输出。这有点像JMS-light。而且显然这里只是用来炫耀。
您还会注意到我们的数据库 Bean 获得了一个新的外观
package com.acme.greetings; import java.util.Date; import java.util.List; import javax.ejb.Stateless; import javax.enterprise.event.Observes; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.ParameterExpression; import javax.persistence.criteria.Root; @Stateless public class GreetingArchiver { @Inject @GreetingDB EntityManager db; CriteriaQuery<Greeting> loadQuery; ParameterExpression<Date> timestampParam; @Inject public void initQuery(@GreetingDB EntityManagerFactory emf) { CriteriaBuilder cb = emf.getCriteriaBuilder(); timestampParam = cb.parameter(Date.class); loadQuery = cb.createQuery(Greeting.class); Root<Greeting> greeting = loadQuery.from(Greeting.class); loadQuery.select(greeting); loadQuery.where(cb.greaterThan(greeting.get(Greeting_.created), timestampParam)); } public void saveGreeting(@Observes @Added Greeting greeting) { db.persist(greeting); } public List<Greeting> loadGreetings() { Date tenMinutesAgo = new Date(); tenMinutesAgo.setTime(tenMinutesAgo.getTime() - 10 * 60 * 1000); return db.createQuery(loadQuery).setParameter(timestampParam, tenMinutesAgo).getResultList(); } }
由于 CMT 的应用,持久化已经被大大简化
最后是使用它们的视图
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ice="http://www.icesoft.com/icefaces/component" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title> Greetings </title> </h:head> <h:body> <h:form> <ice:inputText id="feedback" value="#{greetingClient.greeting.text}" effect="#{greetingClient.appear}"/> <h:message for="feedback" /> <h:commandButton value="Add" action="#{greetingClient.addGreeting}" /> <h:dataTable value="#{greetingServer.greetings}" var="greeting"> <h:column> <h:outputText value="#{greeting.text}"/> </h:column> </h:dataTable> </h:form> </h:body> </html>
JMS 和 JavaMail
谈到 JMS,让我们添加一个消息驱动 Bean
package com.acme.greetings; import javax.annotation.Resource; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/GreetingTopic"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic") }) public class GreetingMailer implements MessageListener { @Resource(mappedName = "java:/Mail") Session mailSession; @Override public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { mail(textMessage.getText()); } catch (Exception e) { // Forgive me, for I have sinned System.out.println(String.format("No mailing: %s", e.getMessage())); } } private void mail(String greeting) throws MessagingException { MimeMessage message = new MimeMessage(mailSession); message.setFrom(new InternetAddress("[email protected]")); message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress("[email protected]")); message.setSubject("New greeting has arrived!"); message.setText(greeting); Transport.send(message); } }
这是一个监听主题 /topic/GreetingTopic
的消息驱动 Bean,当收到消息时,它会从应用服务器中获取默认的邮件会话并发送通知。您还需要在部署目录中的 mail-service.xml 中配置它。您还应该在 deploy/hornetq/hornetq-jms.xml 中添加该主题,如下所示:
<topic name="GreetingTopic"> <entry name="/topic/GreetingTopic"/> </topic>
可能还有更好的部署主题的方法,但这里不是写 JBoss AS 教程的地方。除非有人实际发送消息给它,否则这个 MDB 将不会看到任何动作。让我们添加一个
package com.acme.greetings; import javax.annotation.Resource; import javax.ejb.Stateless; import javax.enterprise.event.Observes; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; @Stateless public class JMSDispatcher { @Resource(mappedName = "/ConnectionFactory") ConnectionFactory connectionFactory; @Resource(mappedName = "/topic/GreetingTopic") Topic topic; public void broadcast(@Observes @Added Greeting greeting) throws JMSException { Session session = null; MessageProducer producer = null; Connection connection = null; try { connection = connectionFactory.createConnection(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); producer = session.createProducer(topic); TextMessage message = session.createTextMessage(greeting.getText()); producer.send(message); } finally { if (producer != null) { producer.close(); } if (session != null) { session.close(); } if (connection != null) { connection.close(); } } } }
这个 Bean 会观察与 GreetingArchiver 相同的问候,并将问候发送到我们的主题。可能还有更好的编写方法,但这里不是写 JMS 教程(也不是)。嗯,我忘记了我在写什么样的教程。
未来的事情
这个快照还没有所有东西,我希望有 @Scheduled 的问候,这样我们就不会感到孤独。这实际上还没有工作,但让我们添加它,并不断升级我们的快照,也许有一天我们会走运。
package com.acme.greetings; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.ejb.Schedule; import javax.ejb.Singleton; import javax.inject.Inject; @Singleton public class ArbiBean { static final List<String> comments = new ArrayList<String>() { private static final long serialVersionUID = 1L; { add("Tried the new JRebel? I heard it can read your thoughts?"); add("Where can I find the specs?"); add("Shouldn't you be using RichFaces 4 instead?"); add("How is the Seam 3 performance compared to Seam 2?"); add("PIGBOMB!"); } }; @Inject GreetingServer greetingServer; @Schedule(second = "*/15") public void comment() { greetingServer.addGreeting(new Greeting(getRandomComment())); } private String getRandomComment() { return comments.get(new Random(comments.size()).nextInt()); } }
这个应该每 15 秒启动一次,并向服务器发送评论。但像我说的一样,还没有。
结论
这结束了第 IV 部分。在第 V 部分,我们将添加 JAX-RS 和 JAX-WS 以与世界分享我们的问候。如果您到现在还没有注意到,针对快照和 alpha 发布版进行开发需要相当多的 workaround。但作为开发者,您可以选择 - 您是想坐着等待最终产品,还是想加入这个圈子,并亲身体验即将到来的事物(在遇到问题时提交 JIRA)。