我的妻子已经生下了我们的女儿,而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)。


回到顶部