我的妻子已经生下了我们的女儿,而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("greetingmaster@nowhere.org"));
message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress("you@yourplace.com"));
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)。