在Web应用中访问EJB组件

1.在Web应用中访问EJB组件

1.1 JavaEE系统结构简介

一个 JavaEE 应用由多种组件组合而成,这些组件安装在不同的机器上。

一个多层次的 JavaEE 应用结构如图1-1所示,它包含如下4个层次:

  • 客户层:运行在客户机器上。客户层可以是普通的应用程序,直接访问业务层的EJB组件;也可以是浏览器程序,访问Web层的JSP和Servlet组件。

  • Web层:运行在JavaEE服务器上(应用服务器)。Web层的组件主要包括JSP和Servlet,用于动态生成HTML页面。Web层的组件会访问业务层的EJB组件。

  • 业务层:运行在JavaEE服务器上。业务层的主要组件为EJB,它们负责实现业务逻辑。

  • Enterprise Infomation System(EIS)层:运行在数据库服务器上,用于存储业务数据。
    图1-1 JavaEE 的多层次软件

    图1-1 JavaEE 的多层次软件

1.2 安装和配置WildFly服务器

WildFly服务器的前身是JBoss,是一个免费的JavaEE服务器软件。WildFly服务器同时提供了Servlet容器和EJB容器,因此既能运行Java Web应用,又能运行EJB组件。

WildFly服务器的一个显著优点是需要比较小的内存和硬盘空间。安装和启动WildFly的步骤如下。

  1. WildFly是一个纯Java软件,它的运行需要JDK,因此在安装WildFly前应该先安装好JDK,并且在操作系统中加入加JAVA_HOME系统环境变量。
  2. WildFly的官方下载地址为:http://wildfly.org/downloads/。
  3. WildFly安装软件包是一个压缩文件,应该把这个压缩文件解压到本地磁盘(例如,把它解压到 C:/WildFly 目录),假定WildFly的根目录为<WILDFLY_HOME>。
  4. 接下来运行<WILDFLY_HOME>/bin/standalone.bat,这个命令启动WildFly服务器。WildFly服务器的Servlet容器默认情况下监听的 HTTP 端口为 8080。可以通过浏览器访问http://localhost:8080/。

WildFly的管理平台默认情况下监听9990端口,可以通过修改<WILDFLY_HOME>/standalone/configuration/standalone.xml配置文件中的端口配置代码:“${jboss.management.http.port:9990}”。

此外,此外,如果希望WildFly服务器的Servlet容器监听其他的HTTP服务器端口,只需修改以上配置文件中的“${jboss.http.port:8080}”端口号即可。

1.3 创建EJB组件

在本范例中,将创建一个遵循 EJB3 规范的无状态的会话Bean,名为 BookDBEJB。他将取代原来的 BookDB JavaBean ,负责操纵数据库。

一个 EJB 至少需要生成2个Java文件:Remote 接口和 Enterprise Bean类。

本例中 BookDBEJB 的2个Java 文件分别为:

  • BookDBEJB.java:Remote 接口
  • BookDBEJBImpl.java:Enterprise Bean 类

1.3.1 编写Remote接口

Remote接口中定义了客户可以调用的业务方法。这些业务方法在 Enterprise Bean 类中实现。以下是远程接口 BookDBEJB.java 的代码。@Remote标注用于声明 BookDBEJB 接口为远程接口:

package mypack;

import javax.ejb.Remote;
import java.util.*;

@Remote
public interface BookDBEJB{
  public BookDetails getBookDetails(String bookId)throws Exception ;
  public int getNumberOfBooks()throws Exception ;
  public Collection getBooks()throws Exception ;
  public void buyBooks(ShoppingCart cart)throws Exception ;
}

当客户程序访问 EJB 组件的业务方法时,这些方法的参数以及返回值都会在网络上传输,如图1-2所示。Oracle公司的 EJB 规范规定,如果在Remote接口中声明的方法的参数类型或返回类型为类,那么这个类必须实现 java.io.Serializable 接口。以上代码中,getBookDetails()方法返回类型为BookDetails 类,buyBooks()方法的参数类型为ShoppingCart 类。因此,必须修改BookDetails 和ShoppingCart 类的声明,确保他们都实现了Serializable 接口。此外,在一个ShoppingCart 对象中会包含多个ShoppingCartItem 对象。ShoppingCartItem 对象也会作为参数的一部分在网络上传输。因此,ShoppingCartItem 也必须实现Serializable 接口。
图1-2 客户程序访问EJB组件的业务方法

图1-2 客户程序访问EJB组件的业务方法

1.3.2 编写 Enterprise Java Bean类

本例中的Enterprise Java Bean 名为 BookDBEJBImpl,它实现了远程接口BookDBEJB中定义的业务方法。

package mypack;
import javax.ejb.Stateless;
import java.sql.*;
import javax.naming.*;
import javax.sql.*;
import java.util.*;

@Stateless(name="bookdb")
public class BookDBEJBImpl implements BookDBEJB {
  private String dbUrl =  "jdbc:mysql://localhost:3306/BookDB?useUnicode=true&characterEncoding=GB2312&useSSL=false";
  private String dbUser="dbuser";
  private String dbPwd="1234";
  private String driverName="com.mysql.jdbc.Driver";

  public BookDBEJBImpl () {
    try{
      Class.forName(driverName);
  
    }catch(Exception e){e.printStackTrace();}
  }

  public Connection getConnection()throws Exception{
      return java.sql.DriverManager.getConnection(dbUrl,dbUser,dbPwd);
  }

  public void closeConnection(Connection con){
    try{
        if(con!=null) con.close();
      }catch(Exception e){
        e.printStackTrace();
      }
  }

  public void closePrepStmt(PreparedStatement prepStmt){
    try{
        if(prepStmt!=null) prepStmt.close();
      }catch(Exception e){
        e.printStackTrace();
      }
  }

  public void closeResultSet(ResultSet rs){
    try{
        if(rs!=null) rs.close();
      }catch(Exception e){
        e.printStackTrace();
      }
  }

  public int getNumberOfBooks() throws Exception {
    Connection con=null;
    PreparedStatement prepStmt=null;
    ResultSet rs=null;
    int count=0;

    try {
      con=getConnection();
      String selectStatement = "select count(*) " + "from BOOKS";
      prepStmt = con.prepareStatement(selectStatement);
      rs = prepStmt.executeQuery();

      if (rs.next()) 
        count = rs.getInt(1);
 
    }finally{
      closeResultSet(rs);
      closePrepStmt(prepStmt);
      closeConnection(con);
    }
    return count;
  }


  public Collection getBooks()throws Exception{
    Connection con=null;
    PreparedStatement prepStmt=null;
    ResultSet rs =null;
    ArrayList<BookDetails> books = new ArrayList<BookDetails>();
    try {
      con=getConnection();
      String selectStatement = "select * " + "from BOOKS";
      prepStmt = con.prepareStatement(selectStatement);
      rs = prepStmt.executeQuery();

      while (rs.next()) {

        BookDetails bd = new BookDetails(rs.getString(1), rs.getString(2), rs.getString(3),
           rs.getFloat(4), rs.getInt(5), rs.getString(6),rs.getInt(7));
        books.add(bd);
      }

    }finally{
      closeResultSet(rs);
      closePrepStmt(prepStmt);
      closeConnection(con);
    }

    return books;
  }

  public BookDetails getBookDetails(String bookId) throws Exception {
    Connection con=null;
    PreparedStatement prepStmt=null;
    ResultSet rs =null;
    try {
      con=getConnection();
      String selectStatement = "select * " + "from BOOKS where ID = ? ";
      prepStmt = con.prepareStatement(selectStatement);
      prepStmt.setString(1, bookId);
      rs = prepStmt.executeQuery();

      if (rs.next()) {
        BookDetails bd = new BookDetails(rs.getString(1), rs.getString(2), rs.getString(3),
          rs.getFloat(4), rs.getInt(5), rs.getString(6),rs.getInt(7));
        prepStmt.close();

        return bd;
      }
      else {
        return null;
      }
    }finally{
      closeResultSet(rs);
      closePrepStmt(prepStmt);
      closeConnection(con);
    }
  }

  public void buyBooks(ShoppingCart cart)throws Exception {
    Connection con=null;
    Collection items = cart.getItems();
    Iterator i = items.iterator();
    try {
      con=getConnection();
      con.setAutoCommit(false);
      while (i.hasNext()) {
        ShoppingCartItem sci = (ShoppingCartItem)i.next();
        BookDetails bd = (BookDetails)sci.getItem();
        String id = bd.getBookId();
        int quantity = sci.getQuantity();
        buyBook(id, quantity,con);
      }
      con.commit();
      con.setAutoCommit(true);

    } catch (Exception ex) {
      con.rollback();
      throw ex;
    }finally{
       closeConnection(con);
    }
  }


  public void buyBook(String bookId, int quantity,Connection con) throws Exception {
    PreparedStatement prepStmt=null;
    ResultSet rs=null;
    try{
      String selectStatement = "select * " + "from BOOKS where ID = ? ";
      prepStmt = con.prepareStatement(selectStatement);
      prepStmt.setString(1, bookId);
      rs = prepStmt.executeQuery();

      if (rs.next()) {
          prepStmt.close();
          String updateStatement =
                  "update BOOKS set SALE_AMOUNT = SALE_AMOUNT + ? where ID = ?";
          prepStmt = con.prepareStatement(updateStatement);
          prepStmt.setInt(1, quantity);
          prepStmt.setString(2, bookId);
          prepStmt.executeUpdate();
          prepStmt.close();
       }

    }finally{
      closeResultSet(rs);
      closePrepStmt(prepStmt);
    }
  }
}

1.4 在 Web 应用中访问 EJB 组件

在原来的 bookstore 应用的 common.jsp 中定义了如下 JavaBean:

<jsp:useBean id="bookDB" scope="application" class="mypack.BookDB"/>

现在BookDBEJB 替换原来的 BookDB JavaBean。BookDBEJB 组件运行在 EJB 容器中,是一种 JNDI 资源。而 common.jsp 运行在 Servlet 容器中。common.jsp 无法直接创建或引用BookDBEJB 组件。而应该通过javax.naming.InitialContext类的 lookup() 方法来查找位于 EJB 容器中的BookDBEJB JNDI 资源,获得该资源的引用:

<%@ page import="mypack.*" %>
<%@ page import="java.util.Properties" %>
<%@ page errorPage="errorpage.jsp" %>
<%@ page import="javax.naming.*" %>
<%!
    private BookDBEJB bookDB;

    public void jspInit() {

      bookDB =
          (BookDBEJB)getServletContext().getAttribute("bookDB");

      if (bookDB == null) {
        try {

          Properties pro = new Properties();
          pro.setProperty("java.naming.factory.initial","org.wildfly.naming.client.WildFlyInitialContextFactory");
          pro.setProperty("java.naming.provider.url","http-remoting://localhost:8080");
          pro.setProperty("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
          Context context =new InitialContext(pro);
          bookDB=(BookDBEJB) context.lookup("ejb:bookstore/bookstore/bookdb!mypack.BookDBEJB");
/** 
以上代码先通过一个 Properties 对象来为 InitialContext 设置访问 WildFly 服务器的一些属性,接下来再调用 lookup()方法。lookup()方法中的参数是 BookDBEJB 的 JNDI 名字。
在编程时,如何检查BookDBEJB 的 JNDI 名字是否正确呢?当 bookstore 应用发布到 WildFly 服务器中时,WildFly 服务器会在 DOS 控制台显示 BookDBEJB 的 JNDI 名字信息。
**/
          getServletContext().setAttribute("bookDB", bookDB);
/**
common.jsp 将 BookDBEJB 组件保存到 Web 应用范围内,当其他的JSP组件调用 BookDBEJB 的业务方法时,可以直接在 ServletContext中获取。
**/

        } catch (Exception ex) {
            System.out.println("Couldn't create database bean." + ex.getMessage());
        } 
      }
    }

   public void jspDestroy() {
      bookDB = null;
   }
%>

common.jsp 将 BookDBEJB 组件保存到 Web 应用范围内,当其他的JSP组件调用 BookDBEJB 的业务方法时,可以直接在 ServletContext中获取。

<%
	String bookId = request.getParameter("bookId");
	if(bookId == null)bookId="201";
	BookDetails book = bookDB.getBookDetails(bookId);
%>

对于原来的Bookstore Web应用。bookDB 代表的是 JavaBean 组件。它运行在Servlet 容器中,所以getBookDetails() 方法在 Servlet 容器执行。在本例中,bookDB 代表 BookDBEJB 组件,它运行在 EJB 容器中,所以 getBookDetails()方法在 EJB 容器中执行。

1.5 发布 JavaEE 应用

如果发布一个 Web 应用时,可以把它包为 WAR 文件;如果单独发布一个 EJB 组件,应该把它打包为 JAR 文件。对于一个完整的 JavaEE 应用,在发布时,应该把它打包为 EAR 文件。

1.5.1 在WildFly上发布 EJB 组件

  1. EJB 组件相关文件

    需要将 JavaEE API 的类库文件加入到 classpath 中。在<WILDFLY_HOME>/modules目录的子目录下有一个 jboss-ejb-api_X_spec-X.Final.jar 文件,它就是 JavaEE API 的类库文件。

  2. 给 EJB 组件打包

    在DOS窗口中,转到组件根目录下,运行如下命令:

    jar cvf bookdbejb.jar *
    

    将在根目录下生成 bookdbejb.jar 文件。如果单独发布该 EJB 组件,只要把该 jar 文件复制到<WILDFLY_HOME>/standalone/deployments 下即可。

1.5.2 在WildFly上发布 Web 组件

  1. 给 Web 应用打包

    在DOS窗口中,转到应用根目录下,运行如下命令:

    jar cvf bookstore.war *
    

    发布方式同上EJB组件。

1.5.3 在WildFly上发布 JavaEE 组件

一个 JAVAEE 应用由 EJB 组件、Web应用、存放第三方类库的 lib 目录以及发布描述文件构成。他的目录结构如图1-3所示。
图1-3 JavaEE应用的目录结构

图1-3 JavaEE应用的目录结构
  1. application.xml文件

    在这个文件中声明JavaEE应用所包含的Web 应用以及EJB组件。以下是application.xml文件的代码:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <application>
        <display-name>Bookstore JavaEE Application</display-name>
    
        <module>
        <web>
            <web-uri>bookstore.war</web-uri>
            <context-root>/bookstore</context-root>
        </web>
        </module>
    
        <module>
            <ejb>bookdbejb.jar</ejb>
        </module>
    
        <library-directory>lib</library-directory>
    
    </application>
    

    以上代码指明在bookstore JavaEE 应用中包含一个 bookstore Web 应用,对应的 WAR 文件为 bookstore.war,它的URL路径为 /bookstore; 此外还声明了一个 EJB 组件,这个组件的JAR文件为bookdbejb.jar。

    通过<library-directory>元素指定了类库文件所在的目录为"lib"。

  2. 给 JavaEE 应用打包

    在DOS窗口中,转到 JavaEE 应用根目录下,运行如下命令:

    jar cvf bookstore.ear *
    

    发布方式同上EJB组件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值