介绍


Apache TomEE音tommy), 多么熟悉的名字呀!不错,它是Tomcat的变身,融合JavaEE之特性而成: TomEE = Tomcat + java EE 。那么,它有何与众不同之处呢?

众多应用服务器使用 Tomcat 提供的 servlet 功能。 这是个不错的选择,Tomcat是一款优秀的 servlet 容器。与其它应用服务器相比, TomEE 不是把 Tomcat 嵌入到应用服务器中, 而是把 EJB, CDI 及 其它 Java EE特性融入到 Tomcat 中,给你一个以 Tomcat 为主、完全兼容的 Web Profile服务器。创建TomEE 很简单,只需将 Tomcat zip 文件解压缩,加入自己的 jar 包,并在 conf/server.xml 文件里添加一个单实例监听器(single listener),然后重新压缩打包就可以了。

TomEE 开发有三条很重要的指导性原则:

  • 保持小巧

  • 基于标准

  • 秉承Tomcat衣钵

尤其是第三条,我们是要在 Tomcat 中加入特性,而不是要去除 Tomcat 的特性。

发布应用和普通 Tomcat 无异,将war包放到 webapps目录下即可

也可以用 TomEE 的配置文件指向你的应用源代码,

定义在 server.xml 或 context.xml 文件中的 应用依旧可以工作。

之前难以做到的一些事性,诸如像 WebService 或 EJB 那样的所有Jave EE 安全概念,

现在都可以安全无缝地工作在 Tomcat 领域(Tomcat Realms)了。

这样的设计理念并不只是增强服务器本身而已,它也带来了其它好处。

既然,它是 Tomcat 的延伸版本,那么,能和 Tomcat 一起工作的所有工具,

如 EEclipse WTP 那样的集成开发工具(IDE tool),也能和 TomEE 和谐工作。

TomEE 的确是真正的 Tomcat 服务器,并且具备所有 Jave EE Web Profile 的特性,一点不漏。


版本

Apache TomEE 可配置三种不同的版本:Webprofile, JAX-RS, and Plus。Webprofile 提供了最小的发布版(仅27M),和Java EE Web Profile完全兼容; JAX-RS 在 Web Profie 基础上, 添加了 Apache CXF 的精简版来实现 JAX-RS 支持,依然基于 Web Profile 标准;Plus 包提供了包含 JMS, JAX-WS 和 JCA 等特性,但它不是基于 Java EE 标准的。

下表列出了三种版本所有不同的特性:

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 。
下载解压后,查看一下它的文件目录结构,你会发现它和 Tocmcat 多么相似。
bin/ 目录存放catalina shell/batch 脚本, 
conf/ 下存放 配置文件,
webapps/ 下 用于存放  war应用发布包.。
启动 Tomcat EE 只要到 bin 目录 下执行 ./catalin.sh 
(window 下 执行 catalina.bat)。
启动一般只需几秒,然后在浏览器地址栏中输入http://localhost:8080,不出问题,
就能看到自带的 Tomcat 应用。有一个按钮可以链接到 TomEE控制台,
可查看你的设置是否正常工作,
还能查看 JNDI 配置。

Eclipse 开发环境配置

启动 Eclipse ,我们来看一个简单的示例。其它的集成开发工具
如 Netbeans 和 IDEA 配置方法雷同。
Eclipse(含 WTP ) 可配置各种不同的应用服务器,可以发布代码,
在你修改代码后能自动重新发布。
配置 TomEE 和 配置 Tomcat 无异。

第一步,在 server 窗口选择 'New server' 链接 :

                               

第二步,选择 Apache Tomcat 7, 填上 server 名称:

下一步设置本机 TomEE 的安装目录:

要是你的 Eclipse 配置使用 workspace metadata, 而不是 TomEE 的安装目录,把 TomEE conf/ 目录下的配置文件拷贝到 workspace 是个不错的选择。这样,服务器直接从 workspace 的 conf/ 目录下读取配置。 WTP插件能自动将 Tomcat 配置拷贝到 workspace, 除了 tomee.xml 及 system.properties.

可选的调整
要拷贝必要的文件,右击 Server 项目的 localhost-config 文件夹, 然后 从 conf/ 目录下导入:

在 web.xml 中, TomEE 默认不设置JSP开发模式的自动编译参数,这意味着,即使你保存更改,JSP也不会编译更新。打开Servers->Tomcat v7.0 Server  -localhost-config 目录下的 web.xml 文件,更改 JSP servlet,将 development 值设为 true就可以了。

Listing 1: web.xml - configuring development mode for JSPs
  <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 自带了大量示例,可演示Java EE各种不同特性。当前有100多个。是学习Java EE 新特性极佳的入门资料。在TomEE Subversion 仓库可以下载到这些例子。此处引用 moviefun 例子。示例代码见http://tomee.apache.org/examples-trunk/moviefun/README.html。这是个简单的 web 应用,结合了 Web Profile 的一些特性。 应用定义了一个简单的 POJO 代表 Movie, 用 JPA 从数据库存取。代码如下:

Listing 2: Movie Entity 

@Entity
        public class Movie implements Serializable {

                @Id
                @GeneratedValue(strategy = GenerationType.AUTO)
                private long id;

                private String director;
                private String title;
                private int year;
                private String genre;
                private int rating;

                public Movie() {
                }

                // SNIP getter and setters below...
        }

 类中使用了anotation 标注了持久化上下文,以便注入简单的 EJB 或 CDI bean 实例,并通过 JPA 在数据库中存取Movie 对象。 

Apache TomEE 支持 EJB 1.0 到 3.1版本。 EJB 3.0 的创建较之前版本容易了很多,3.1 版本作了进一步简化。其中一个新特性叫"无接口"示图 ("no-interface" view), 意味着 EJB session bean不必再提供接口。

下列展示了一个简单的无状态 session EJB, 用 JPA2 管理数据存取。并标注了 anotation @Stateless (EJB 中所必需的),然后通过 TomEE 使用 @PersistenceContext annotation 注入实例。 

Listing 3: Stateless session bean using JPA2 

@Stateless
        public class MoviesBean {

                @PersistenceContext(unitName = "movie-unit")
                        private EntityManager entityManager;

                public Movie find(Long id) {
                        return entityManager.find(Movie.class, id);
                }

                public void addMovie(Movie movie) {
                        entityManager.persist(movie);
                }

                public void deleteMovie(Movie movie) {
                        entityManager.remove(movie);
                }

                public void deleteMovieId(long id) {
                        Movie movie = entityManager.find(Movie.class, id);
                        deleteMovie(movie);
                }

                public List<Movie> getMovies() {
                        CriteriaQuery<Movie> cq = entityManager.getCriteriaBuilder().createQuery(Movie.class);
                        cq.select(cq.from(Movie.class));
                        return entityManager.createQuery(cq).getResultList();
                }
        }

类还提供了简单的方法,如查找,删除对象等。getMovies() 从数据库中取出所有的 Movie 实例,这里没有例行的事务控制。EJB的方法会默认管理事务。TomEE 也已经为你省下功夫。

这个简单的 bean 提供了与数据库交互的 API。现在,我们可以用它了! 让我们来看一下 moviefun's user 接口。web 前端与这个 EJB 交互有很多方法,比如可以用 JSF ManagedBean 调用,或者其它 MVC 框架调用。为简单起见,本例使用一个 servlet 和这个 EJB 通讯,然后将结果转给 JSP 页面。

正如你所料, TomEE 以 Tomcat 7 为内核, 支持 Servlet 3.0 spec。 这允许我们创建一个类,来继承 javax.servlet.http.HttpServlet ,并标注 anotation @WebServlet。Servlets 和 managed bean  都可以给标注了 @EJB annotation 的字段注入值。 CDI beans 则通过@Inject annotation 。TomEE 同样支持@Inject 构造注入,该特性已加入到 JaveEE 7 中了。

Listing 4: Servlet with session bean injected 

  @WebServlet("/moviefun/*")
        public class ActionServlet extends HttpServlet {
                private static final long serialVersionUID = -5832176047021911038L;

                @EJB
                private MoviesBean moviesBean;

                @Override
                protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
                                List<Movie> range = moviesBean.getMovies();
                                request.setAttribute("movies", movies);
                        }

                        request.getRequestDispatcher("WEB-INF/moviefun.jsp").forward(request, response);
                }
        }

 我们可以用一个简单的 JSP 来展示数据

Listing 5: JSP to render movies 

 <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>

 OK,有了上面三个类和一个 JSP ,就可以开始了,另外,得再配置一个文件,META-INF/persistence.xml, 提供持久单元的基本信息方便注入EJB。

Listing 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>

文件定义了  @Entity 类 org.superbiz.moviefun.Movie ,指定了OpenJPA 自动创建数据库脚本schema, 要是数据库不存在对应的表。

部署应用

将此模块添加到服务器,启动服务器后,可以看到应用成功启动。URL中应用的地址取决于 .war 包名 或 Eclipse下项目名,假设为 example, 则发布到 http://localhost:8080/example/

瞧, 不用EARs!

你可以看到所有的东西都在一个模块里,被打包成单一的 WAR 文件,不用EJB 构建 EAR 文件,这一特性源于 TomEE 。 TomEE 在几年前就有了,如今为 JavaEE 所吸收。这是 TomEE 给 Java EE 所作的最重要贡献之一。

所有内存都是你的!

值得指出的一点是,TomEE 以默认的内存参数运行 - 跑在我的 Mac 64bit server JDK 1.6 上,默认最大是128MB。

TomEE 实际上是基于Amazon EC2 微实例标准,其任意一个只要613MB 的RAM空间--在现在的水平这不算多。但用 TomEE 作为你的应用服务器,那613Mb 中大部分都可以供你的应用使用。这为你的应用留下了足够的内存空间。我们曾经在一个只有256MB RAM 的 Raspberry Pi 机器运行 TomEE 和 moviefun 实例,TomEE 运行 moviefun 例子只用了约其中30Mb。那个只要 35 美元的成本,谁说 Java EE 笨重或花费昂贵来着?

数据库配置

你可能已经注意到我们使用和部署了一个使用数据库的应用,然而在 TomEE 中未作任何实际配置。若是在 Eclipse 中重启 TomEE 服务, 你可以看到所有的应用数据都已持久化了。那么数据到底存到了哪里?数据源又是如何配置的?

TomEE 使用了一个单一的XML配置文件,conf/目录下的 tomee.xml 。你可以看到与默认的 EJB pools 、数据源、资源适配器等设置有所不同。语法与你所见的其它应用的 XML配置文件也有差别。它的配置风格是基于Apache httpd 服务,保持尽可能的简单易读。比如时间设置,不是指定用毫秒,可以表达成任一时间单位的组合,如"1 hour and 10 minutes and 30 seconds"。

默认的数据源可以如下代码段添加到 tomee.xml 中。

Listing 7: Configuring a datasource in 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,JdbcUrl, UserName 和 Password,同时需要将 JDBC 驱动 jar 添加到 TomEE/lib 目录。

其中 Resource ID 要匹配 persistence.xml 文件中定义的<jta-data-source> 或<non-jta-data-source> 。 若在 tomee.xml 中没有找到相匹配的资源,则使用默认的数据库,正像上面的例子,默认的数据库是一个基于HSQLDB实例的文件,位于 data/ 目录。该文件也可以被配置成 JMS 队列和主题,和其它任一可供应用使用的资源形式。

配置也可以通过系统属性设定,上例 example 的数据源像下面那样以 JAVA_OPTS 参数指定:

-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 完全集成了 Java EE 的安全性,无缝、一致地运行于 web 层,EJBs 和 webservices 端。添加一个应用和任何一个 定制的Tomcat Realms 的实现都相当简单, 如 JDBC,LDAP 或 MongoDB 支持的 Realms, 不作任何修改,都可以运行在 TomEE 中。另外,也支持用 JAAS conf/目录下login.config定制的安全模块。可以通过以下步骤给 moviefun 示例 添加 基本的 HTTP 安全验证。
在 tomcat-users.xml 中定义一个 user 和 role:

<user username="movies" password="movies" roles="moviefun"/>

在 web.xml 中添加一些安全配置,如下:

Listing 8: web.xml security configuration 

  <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>

 

最后为 MoviesBean 类 添加 @RolesDeclared 和 @RolesAllowed anotation。或者添加在一个你想授权访问的单一方法上。如下

Listing 9: adding roles to session bean 

@Stateless
        @DeclareRoles(value = { "moviefun" })
        public class MoviesBean {

                @RolesAllowed(value = { "moviefun" })
                public List<Movie> findAll(int firstResult, int maxResults) {
                        ...
                }
        }

 以上配置,访问应用里的任何页面,都需提供验证,用户名和密码通过tomcat 的 UserDatabase realm 的 tomcat-users.xml 检验。最后, TomEE 将登录规则和相关权限传递给 EJB。只有授权的用户方可调用 EJB 的方法。远程客户端调用 EJB 的方法同样需要用户名和密码校验。

作者信息

Jon 是一个开源和Java EE的爱好者,任职于英国 Weatherbys Ltd公司。作为开源的使用者和贡献者,在过去的四年里, Jon 提交了 Apache TomEE and OpenEJB 项目, 并致力于多种不同的特性,包括早期集成 Tomcat 7 到TomEE 中,  EJB 3.1 compliance, TomEE Arquillian adapters 和 OpenEJB Eclipse 插件的部分工作.  当为 Apache TomEE 孜孜不倦,夜以继日地工作的同时, Jon 同样享受着生活,喜欢烹饪, 爱看Formula One 赛车, 偶尔还打打高尔夫球。

该文章发表于 JAX Magazine:TomEE - 可以这里下载。

翻译不当之处,可查看原文。 或参考 开源中国的另一翻译版本

感谢你的关注,愿你至少能够以陪伴电脑一样长的时间来陪伴你的亲人和朋友。愿你和你的亲朋健康快乐!


Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐