Apache TomEE(发音同“tommy”)是一个新的JavaEE服务器,由Apache软件基金会开发,你大概能够从它的名字猜到,它是从Tomcat而来, 同时加入的JavaEE的特征:TomEE=Tomcat+java EE。因此,它与其他的应用服务器有什么不同呢?
有一些应用服务器是使用Tomcat提供的servlet功能,这不是什么坏事——Tomcat是最好的servlet容器。TomEE的方法就有点不同 了——作为对直接把Tomcat嵌入到应用服务器的取代,TomEE嵌入了EJB、CDI和其他JavaEE特征到Tomcat里,给你一个完整符合 Web Profile的服务器但同时也保留了Tomcat的领导地位。在没解压的Tomcat里,加入了自己的jar包、一个对conf/server.xml 的单一监听器,然后压缩到备份里,通过这样把TomEE的包创建而成。
有三个使用TomEE开发的指导原则。 他们是:
每一条都对我们很重要,但是“是Tomcat的”部分尤其如此——我们加入特征到Tomcat,同时,重要的是TomEE没有丢失与Tomcat的一致 性。(TomEE下)的部署机制和常见的Tomcat一样(只需把你的。war文档放到webapps/folds)。资源工作在同样的方式下——你可以 使用TomEE的配置文件,但是定义在server.xml或者在你应用程序里的context.xml同样可以配置。一些以前不可能做到的或者是需要很 大工作量的东西现在可以做到了,例如让所有JavaEE的安全概念能像WebService或者EJB的安全无缝地工作在Tomcat Realms上。
这不仅仅是把事情在服务器上做的更好的哲学,还提供了其他的优势。因为TomEE仅仅是Tomcat的一个扩展版本,任何能在Tomcat上使用的工具,如像Eclipse WTP一样的IDE工具,全部都能用在TomEE上。
TomEE确实是一个真正的Tomcat服务器,它把所有JavaEE Web Profile特征加到Tomcat上同时没有去掉任何特性。
模式(flavours)
Apache TomEE有3种不同的工作模式可以使用:Webprofile,JAX-RS和Plus。Webprofile提供了最小的分支版本(仅仅27MB)同 时它完全符合JavaEE Web Profile。JAX-RS建立在Web Profile上,用一个修整过的Apache CXF版本加入对JAX-RS的支持,同时也保证对Web Profile的支持。Plus的包提供了TomEE可用的全部特征,包括了JMS、JAX-WS和JCA,但这次不对JavaEE的支持有所保证。
下面的表格展示了3个不同的模式所支持的不同特征。
Feature | WebProfile | JAX-RS | Plus |
Servlet 3.0 | Yes | Yes | Yes |
CDI | Yes | Yes | Yes |
EJB | Yes | Yes | Yes |
JPA | Yes | Yes | Yes |
JSF | Yes | Yes | Yes |
JSP | Yes | Yes | Yes |
JSTL | Yes | Yes | Yes |
JTA | Yes | Yes | Yes |
JavaMail | Yes | Yes | Yes |
Bean Validation | Yes | Yes | Yes |
JAX-RS | No | Yes | Yes |
JAX-WS | No | No | Yes |
JMS | No | No | Yes |
Connector | No | No | Yes |
让我开始吧
要开始使用,得到 Apache TomEE 下 载页面下载一个分发包,在撰写本文的时候,最新的版本是1.5.0。一旦下载完成,将zip文件解压到你的机器上的某一位置。如果你看看文件夹结构,你可 能会马上注意到TomEE多么像Tomcat规则的一个拷贝。常用的 shell/batch 脚本在bin/目录下,TomEE配置文件在conf/目录下,应用程序发布时通过拷贝.war文件到 webapps/目录下。通过改变到bin/文件 夹并运行./catalina.sh文件以启动TomEE (如果你是用的Windows那么就是catalina.bat文件)。
在一台常规配置的机器上,TomEE 启动一般需要几秒钟。一旦启动,打开浏览器输入 http://localhost:8080。如果一切正常启动,将会显示出默认的Tomcat应用程序。在该应用程序里有一个链接到TomEE控制台的额外按钮。TomEE控制台提供了一些页面和一个JNDI浏览器,在这些页面上能对你的启动程序是否正确进行核实。
使用Eclipse来搭建开发环境
让我们启动IDE,来演示一个简单的程序。我们这里的IDE使用Eclipse,其它的IDE,像Netbeans 和Intellij IDEA可以以同样的方式进行配置。 我有 一个带有WTP的Java EE Eclipse 包,可以启动多个不同的服务器,发布你的代码到服务器上,并且自动重新部署你所做的任何改变。在Eclipse中启动TomEE和你启动Tomcat是相 同的过程。
首先,在服务器视图下选择‘New server’。
然后,选择 Apache Tomcat 7,将服务器名称改成你喜欢的名称:
在向导的下一页, ‘select the installation directory’选择你解压的TomEE安装包的位置:
如果你已经配置Eclipse使用工作区元数据来接管TomEE的安全,一个好的建议是从TomEE包的conf/ 路径里复制文件到工作区。当运行在这种模式下,配置的文件会从工作区的conf/路径读取代替原来TomEE的conf路径。WTP插件对自动把 Tomcat的一些配置文件复制到工作区很有帮助,但不会获得tomee.xml或者system.properties。
要复制需要的文件到工作区,右键点击在Servers项目下的localhost-config文件夹,然后从conf/路径导入文件。
默认情况下,在web.xml文件中,TomEE没有为JSP编译设置开发者参数,也就意味着即使你保存了对他们的更改,JSP也不会更新。为了改变这一 点,打开在项目资源管理器里的 localhost-config 文文件夹,位于Servers->Tomcat v7.0 Server下的web.xml文件 ,更改jsp servlet,将开发者模式设为true,如列表1所示。
列表1:web.xml-为JSP配置开发者模式
<servlet> <servlet-name>jsp</servlet-name> <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> <init-param> <param-name>fork</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>xpoweredBy</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>development</param-name> <param-value>true</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet>
那么现在我们就有一个配置好的开发环境了,来看下一个简单的应用例子吧。Apache TomEE 里有一些应用例子来展示一些JavaEE里可用的特征。现在大概已经有100个例子了,这些都能开始学习新的JavaEE特征给与服务。这些例子都可以在 TomEE SubVersion仓库里看到。我打算在本文使用moviefun这个例子。完整的源代码在http://tomee.apache.org/examples-trunk/moviefun/README.html。这个例子只是一个简单的web应用程序,使用到一些Web Profile里的可用特征。
首先,应用有一个简单的POJO来代表一个Movie,这使用到JPA来存储和获取一个在数据库里的Movie。列表2显示了一个带有标题,导演,流派,年份,排名等属性的movie的POJO。
1 @Entity 2 public class Movie implements Serializable { 3 4 @Id 5 @GeneratedValue(strategy = GenerationType.AUTO) 6 private long id; 7 8 private String director; 9 private String title; 10 private int year; 11 private String genre; 12 private int rating; 13 14 public Movie() { 15 } 16 17 // SNIP getter and setters below... 18 }
使用JPA,提供了一个简单地能持久化到数据库的对象。为了实现这一点,一个持久化上下文需要注入到一个简单的EJB或者CDI bean,同时能被用来检索和持久化Movie对象到数据库中。
Apache TomEE为EJB提供了从版本 1到版本3.1的支持。EBJ3.0使EJB的创建较之前的版本更加简单,版本3.1更进一步。其中一个新特性之一是“no-interface”视图,这意味着EJB session bean 不再需要提供接口。
列表3 列出了一个简单的无状态会话EJB,它使用JPA2能够管理存储和获取数据。这个EJB是一个使用了@Stateless 来注解的简单POJO(这就是要使它成为EJB所要做的全部东西),以及它有一个使用@PersistenceContext注解由TomEE注入的实体 管理器。
列表3: 使用JPA2的无状态会话Bean
1 @Stateless 2 public class MoviesBean { 3 4 @PersistenceContext(unitName = "movie-unit") 5 private EntityManager entityManager; 6 7 public Movie find(Long id) { 8 return entityManager.find(Movie.class, id); 9 } 10 11 public void addMovie(Movie movie) { 12 entityManager.persist(movie); 13 } 14 15 public void deleteMovie(Movie movie) { 16 entityManager.remove(movie); 17 } 18 19 public void deleteMovieId(long id) { 20 Movie movie = entityManager.find(Movie.class, id); 21 deleteMovie(movie); 22 } 23 24 public List<Movie> getMovies() { 25 CriteriaQuery<Movie> cq = entityManager.getCriteriaBuilder().createQuery(Movie.class); 26 cq.select(cq.from(Movie.class)); 27 return entityManager.createQuery(cq).getResultList(); 28 } 29 }
这个类提供了一些简单方法,首先有委托到实体管理器的查询,增加和删除方法。getMovies()方法通过构造一个简单的JPA2查询来从数据库中获取 完整的Movie集合。注意这里并没有引用事务的begin或commit语句。EJB的方法默认是事务型的,所以这里并不需要直接引用。TomEE已经 在节约你的时间了
这个简单的Bean提供了简单的API来与数据库进行交互。现在我们需要一些东西来使用它。所以,让我们看下moviefun的用户接口吧。一个web前 端有很多方法来与EJB交互——例如它能够通过一个JSF的ManagedBean来引用火灾你能够通过其他的MVC框架来查阅EJB。为让它保持简单, 这个例子使用了一个展示在列表4里servlet来和EJB交互,然后转发结果到一个JSP页面。
作为你期待已久的,TomEE的核心包含了Tomcat7,它支持Servlet3.0规范。这允许我们创建一个继承 javax.servlet.http.HttpServelt和使用@WebServlet.Servlets注解的类,以及管理Bean能够用 @EJB注解在字段上来把EJB注入到它们的字段里,同时,CDI Bean能够使用@Inject注解来注入。TomEE同样支持使用@Inject构造函数对Servlet的注入,这个特征在JavaEE7的时候加入 的。
列表4: 使用了会话Bean注入的Servlet
1 @WebServlet("/moviefun/*") 2 public class ActionServlet extends HttpServlet { 3 private static final long serialVersionUID = -5832176047021911038L; 4 5 @EJB 6 private MoviesBean moviesBean; 7 8 @Override 9 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 10 11 List<Movie> range = moviesBean.getMovies(); 12 request.setAttribute("movies", movies); 13 } 14 15 request.getRequestDispatcher("WEB-INF/moviefun.jsp").forward(request, response); 16 } 17 }
我们添加一个简单地JSP来显示数据,同时在表格中增加一个新条目,如列表5中的片段。
<div> <table> <thead> <tr> <th>Title</th> <th>Director</th> <th>Genre</th> <th>Rating</th> <th>Year</th> </tr> </thead> <tbody> <c:forEach items="${movies}" var="movie"> <tr> <td><c:out value="${movie.title}" /></td> <td><c:out value="${movie.director}" /></td> <td><c:out value="${movie.genre}" /></td> <td><c:out value="${movie.rating}" /></td> <td><c:out value="${movie.year}" /></td> </tr> </c:forEach> </tbody> </table> </div>
到此,我们仅有三个类和一个JSP,我们准备去执行。还需要一个附加文件,META-INF/persistence.xml,该文件提供一些关于注入到EJB的持久化单元的一些基本信息。
列表6:Persistence.xml
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="movie-unit"> <jta-data-source>movieDatabase</jta-data-source> <non-jta-data-source>movieDatabaseUnmanaged</non-jta-data-source> <class>org.superbiz.moviefun.Movie</class> <properties> <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/> </properties> </persistence-unit> </persistence>
在列表6中显示的persistence.xml文件的例子,表明使用了 @Entity类(在该例子中是(org.superbiz.moviefun.Movie ),同时它不存在的话,指定的 OpenJPA 应该自动在数据库中为我们建立模式。
现在我们可以准备部署应用了。你可以把这个模块加入到服务器同时启动服务器。
你应该看到应用成果启动,也没什么错误。应用的URL会合.war包的文件名一样(或者是Eclipse的项目名),作为一个例子,example.war部署到 http://localhost:8080/example/.
你会注意到所有东西都在一个模块里,这个模块就是打包到一个单一的WAR文件里的。这里没有必要去把EJB分离到一个JAR包来构造一个EAR文件,虽然 这是以前JavaEE版本的做法。这实际上是来源于TomEE的一项特征,这花费了TomEE好几年的时间,同时这也是它对JavaEE的第一个贡献。
值得指出的是TomEE运行在默认的内存参数上——在我的MAC电脑上运行一个64位的JDK1.6服务版,使用了128MB作为默认最大内存,同时TomEE运行仅使用了大概30MB来运行moviefun这个例子。
这留给你的应用程序大量的内存。TomEE实际上在注册使用的Amazon EC2微实例,每个都有613MB的RAM——对于现今的标准来说不算太多。但使用TomEE作为你的应用服务器,对于你的应用来说是613MB是十分充 足了。我们甚至能够在一个有256MB内存的Raspberry PI(超小型电脑)上运行TomEE和moviefun应用例子,这电脑只需花费$35。谁说JavaEE是笨重和昂贵的?
你也可能注意到了,我们开发和部署了一个数据库应用,但无论什么也没在TomEE上做过配置。如果你在Eclipse里重启TomEE服务器,你就会注意到应用程序的所有数据已经持久化了。那么数据真正是存放在哪里呢,以及它的数据源是怎么配置的?
TomEE是使用一个XML配置文件来配置的,tomee.xml,这个文件放在conf/ 路径下。如果你看一下这个文件,你会看到一些使用默认设置的不同东西——EJB池,数据源和资源适配器。这里的语法可能和其他你看到过的应用服务器的 XML配置文件有所不同。它是基于Apache httpd服务器的配置风格的,目的是让人们更容易去阅读它。时间,例如一个bean池的超时时间,在这里并没有规定为毫秒,它能够随你喜欢使用各种时间 单位来组合的,例如“1小时10分30秒”。
能够通过列表7的方法通过在tomee.xml里加入一段内容来定义一个数据库。
列表7: 在tomee.xml里配置一个数据库
<Resource id="movieDatabase" type="DataSource"> JdbcDriver com.mysql.jdbc.Driver JdbcUrl jdbc:mysql://localhost:3306/moviefun UserName username Password password JtaManaged true </Resource>
设置你想用到的JdbcDriver来匹配JDBC提供的类,同时设置JdbcUrl,UserName和Password来匹配你的数据库。你也可能需要部署你想用的JDBC驱动,你可以简单地把jar包扔到TomEE/lib路径即可。
Resource ID应该和定义在persistence.xml里的<jta-data-source>或<non-jta-data- source>匹配。如果在tomee.xml文件里找不到匹配的资源,将要发生的是会使用默认的数据库。默认数据库是一个基于HSQLDB的文 件,它的目录在data/ 路径。这个文件也可以用来配置JMS队列和主题,以及其他你想用在你应用上的资源。
配置设置也能通过系统属性来指定。数据源例子可以像JAVA_OPS一样作为系统属性来配置:
-DmovieDatabase.JdbcDriver=com.mysql.jdbc.Driver -DmovieDatabase.JdbcUrl=jdbc:mysql://localhost:3306/moviefun
-DmovieDatabase.UserName=username -DmovieDatabase.Password=password
或者他们加入到conf/system.properties中。更多的信息以及系统属性的列表可以到这里查看 TomEE website。
TomEE在一个与web层、EJB和Web服务无缝和一致的方式下紧密地整合了JavaEE安全。加入一个应用程序或者任何定制的Tomcat Realm实现真的是很简单,例如JDBC、LDAP或MongoDB支持的Realms在不改变任何东西的情况下仍能在TomEE上工作。作为选择,也 支持通过在conf路径下的一个JAAS login.config文件定制安全模块。通过一下步骤能够把使用基于HTTP认证的安全加入到moviefun例子中:
在tomcat-users.xml文件中加入一个用户和角色:
<user username="movies" password="movies" roles="moviefun"/>
添加一些如列表8展示的安全配置到 web.xml中。
列表8: web.xml安全配置
<login-config> <auth-method>BASIC</auth-method> <realm-name>UserDatabase</realm-name> </login-config> <security-role> <role-name>moviefun</role-name> </security-role> <security-constraint> <web-resource-collection> <web-resource-name>Everything</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>moviefun</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint>
最后加入@RoleDeclared以及@RolesAllowed到MovieBean类中,或者加入到你想授权访问的单独方法上,如列表9展示:
列表9:加入角色到会话bean
@Stateless @DeclareRoles(value = { "moviefun" }) public class MoviesBean { @RolesAllowed(value = { "moviefun" }) public List<Movie> findAll(int firstResult, int maxResults) { ... } }
由于有了这个配置,在浏览器里访问这个应用程序的任何页面都需要通过一个基本的认证对话框来认证。用户名和密码的将会检查Tomcat域里的 UserDatabase,它使用tomcat-users.xml文件里配置的参数。最后TomEE会登陆的当事人和关联的角色传入EJB。只有用户有 经过允许的角色才能执行在EJB上方法。在远程客户端执行EJB上的方法也会要求发送用户名和密码,同时将在UserDatabase域中认证。