FeaturesPluginsDocs & SupportCommunityPartners

Photo archive end to end scenario

Author: Petr Blaha, Lukas Jungmann
Last update: $Date: 2006/07/14 19:35:56 $, $Revision: 1.5 $
Introduction:

This end to end scenario should test J2EE features in broader context. The scenario covers developing of message driven beans, entity bean with bean managed persistance, session bean and send JMS. The document is not intended as test specification for these features but describes developing J2EE application in NetBeans 5.5 (version for NetBeans 4.1 is here)

The application is used for managing and storing images in *.png format. User select image file in index.jsp, specify his/her e-mail address and click upoload button. The file is uploaded to server, where is processed in UploadServlet that uses FileUpload library from Jakarta Project for parsing HTTP request. Then the JMS message is constructed and sent to EJB tier. Message driven bean listens on JMS Queue. When the message is delivered to MQ then the message driven bean processes it. New entity bean is created and the image with other info is persisted in database. At the end of this process a e-mail notification about successful saving is sent to user. See sequence diagram:

sequence diagram

Table of Contents

Photo-archive sources are avalaible here, FileUpload library here and Commons IO library is here.

J2EE application

Photo archive project is three tier J2EE application with web and EJB tier. Web tier includes servlet and jsp that are simple view and all business logic is implemented in EJB module.

  • Go to File - New Project - Enterprise - Enterprise Application
  • Specify PhotoArchive as Project Name
  • Specify project's directory
  • Select Sun App server as target server
  • Choose J2EE 1.4 as J2EE version and
  • Click Finish

BMP bean

The entity represents PhotoItem business object that has three properties: id is primary key, email is e-mail when a notification about saving will be sent and img is array of bytes that represents image. The bean is implemented as Bean manage persistence (BMP). Therefore we should implement all methods that saves, load data manually. So the first thing we have to do is to create new table in new DB (this case will be shown) or in some existing one:

  • Create new DB using Tools -> Java DB Database -> Create Java DB Database, call it eg. PhotoAlbumDB and use "user" as user name as well as password
  • Connect to the new DB - right click on new connection in runtime tab and choose Connect...
  • Expand connection node, right click on it and choose Execute command
  • copy/paste following code into opened SQL editor and run it:
            create table photo
            (
                id INT not null generated always as identity primary key,
                email VARCHAR(50) not null,
                img BLOB
            );
            create index index_email on photo (email);
            

Now we can move on to the creation of entity bean itself:

  • Select PhotoArchive-ejb node in project tab and invoke New - Entity Bean
  • Type PhotoItem in Entity bean name text field
  • Specify org.photoarchive.ejb package
  • Select bean persistance type
  • Click Finish button
  • Open bean implementation class in editor
  • Create some helper logging method, eg. this one:
        private void print2log(String msg){
            Logger.getLogger("PhotoItem").log(Level.SEVERE,msg);
        }
                
  • Now right-click and invoke Enterprise Resources - Use Database and select connection to your database. If you don't see it, then choose New Data Source in Data Source combobox, call it eg. jdbc/PhotoAlbumDB, select your connection in Database connection combobox and press OK. Note, under Server Resources node you can see nodes that represent JDBC datasource and connection pool related to DB connection.
  • Add these private instance variables to the bean class: java.sql.Connection connection, Integer key, String eMail, byte[] img
  • Select key variable and invoke Refactore - Encapsulate fields and generate get method for key and get/set methods for eMail and img fields
  • Select generated set/get methods and invoke EJB Methods - Add To Local Interface
  • implement database methods:
        private void getConnection(){
            try {
                connection = getPhotoAlbumDB().getConnection();
            } catch (Exception ex) {
                print2log("Connection error: " + ex.getMessage());
                throw new EJBException("Connection error: " + ex.getMessage());
            }
        }
        
        private void releseConnection(){
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException ex) {
                    print2log("Release connection: " + ex.getMessage());
                    throw new EJBException("Release connection: " + ex.getMessage());
                }
            }
        }
        
        private int insertPhoto(String email, byte[] img) throws SQLException {
            getConnection();
            String insertStatement = "INSERT INTO photo (email, img) VALUES (?,?)";
            String selectStatement = "SELECT MAX(id) FROM photo";
            PreparedStatement prepStmt = connection.prepareStatement(insertStatement);
            prepStmt.setString(1, email);
            prepStmt.setBytes(2, img);
            
            Statement stmt = connection.createStatement();
            try {
                connection.setAutoCommit(false); //start transaction
                connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
                prepStmt.execute();
                ResultSet rs = stmt.executeQuery(selectStatement);
                if (rs.next()) {
                    return rs.getInt(1);
                }
                connection.commit();
            } catch (SQLException sqlEx) {
                connection.rollback();
                throw sqlEx;
            } finally {
                releseConnection();
            }
            return 0;
        }
        
        private boolean findByPrimaryKey(Integer key) throws SQLException {
            getConnection();
            String selectStmt = "SELECT id from photo WHERE id = ?";
            PreparedStatement prepStmt = connection.prepareStatement(selectStmt);
            prepStmt.setInt(1, key.intValue());
            ResultSet rs = prepStmt.executeQuery();
            boolean result =  rs.next();
            releseConnection();
            return result;
        }
        
        private void deletePhoto(int key) throws SQLException {
            getConnection();
            String deleteStmt = "DELETE FROM photo WHERE id = ?";
            PreparedStatement prepStmt = connection.prepareStatement(deleteStmt);
            prepStmt.setInt(1, key);
            prepStmt.execute();
            releseConnection();
        }
        
        private void loadPhoto() throws SQLException {
            getConnection();
            String selectStmt = "SELECT id,email,img FROM photo WHERE id = ?";
            PreparedStatement prepStmt = connection.prepareStatement(selectStmt);
            ResultSet rs = prepStmt.executeQuery();
            while(rs.next()) {
                key = new Integer(rs.getInt(1));
                eMail = rs.getString(2);
                img = rs.getBytes(3);
            }
            releseConnection();
        }
        
        private void storePhoto() throws SQLException {
            getConnection();
            String updateStmt = "UPDATE photo SET email = ?, img = ? WHERE id = ?";
            PreparedStatement prepStmt = connection.prepareStatement(updateStmt);
            prepStmt.setString(1, eMail);
            prepStmt.setBytes(2, img);
            prepStmt.setInt(3, key.intValue());
            prepStmt.execute();
            releseConnection();
        }
                
  • Select entity bean in project tab and invoke Add - Create Method
  • Specify email and img method parameters
  • Open ejb-jar.xml in editor and change PK class to java.lang.Integer
  • Invoke Refactor -> Change method paramaters on PhotoItemEB | Local Methods | findByPrimaryKey node in project view and change Long to Integer
  • Implement ejbCreate()/ejbRemove(), ejbLoad()/ejbStore(), ejbPassivate()/ejbActivate() and ejbFindByPrimaryKey() methods:
        public void ejbActivate() {
            key = (Integer)context.getPrimaryKey();
        }
        
        public void ejbPassivate() {
            key = null;
        }
        
        public void ejbRemove() {
            try {
                deletePhoto(this.key.intValue());
            } catch (SQLException ex) {
                print2log("Can not remove bean: " + ex.getMessage());
                throw new EJBException(ex.getMessage());
            }
        }
        
        public void ejbLoad() {
            // TODO add code to retrieve data
            try{
                loadPhoto();
            } catch (SQLException ex) {
                print2log("ejbLoad: " + ex.getMessage());
                throw new EJBException(ex.getMessage());
            }
        }
        
        public void ejbStore() {
            // TODO add code to persist data
            try {
                storePhoto();
            } catch (SQLException ex) {
                print2log("ejbStore: " + ex.getMessage());
                throw new EJBException(ex.getMessage());
            }
        }
        
        public Integer ejbFindByPrimaryKey(Integer aKey) throws FinderException {
            // TODO add code to locate aKey from persistent storage
            // throw javax.ejb.ObjectNotFoundException if aKey is not in
            // persistent storage.
            try{
                if (findByPrimaryKey(aKey)) {
                    throw new ObjectNotFoundException("Photo with id " + aKey + "isn't found.");
                }
            } catch (SQLException ex) {
                print2log("findByPrimaryKey: " + ex.getMessage());
                throw new EJBException(ex.getMessage());
            }
            return key;
        }
        
        public Integer ejbCreate(String eMail, byte[] img) throws CreateException {
            //TODO implement ejbCreate
            if (eMail == null) {
                throw new CreateException("email can't be null");
            }
            try {
                this.eMail = eMail;
                this.img = img;
                this.key = new Integer(insertPhoto(eMail, img));
            } catch (SQLException ex) {
                print2log("Can't create bean: " + ex.getMessage());
                throw new CreateException("Can't create bean: " + ex.getMessage());
            }
            return key;
        }
                    
  • Save and build project
  • Right-click PhotoArchive node in project tab and select Verify project. All test should pass.

E-mail notification

After storing user's image to database the e-mail notification is sent to user. E-mail informs him about image identification number. This number is used to find out the uploaded image through web console.

  • Select EJB module project node and invoke New - Session bean
  • Specify bean's name SubmitAction, package eg. org.photoarchive.ejb and press Finish
  • Right-click in the editor and invoke Enterprise resources -> Send E-mail, set JNDI name to mail/PhotoArchiveMail and press OK
  • Right-click in the editor and invoke EJB Methods -> Add Business Method... and create method sendMessage(String, int):void which will throw SubmitException
  • Implement new method:
        public void sendMessage(String eMail, int id) throws SubmitException {
            try {
                //TODO implement sendMessage
                sendMail(eMail, "Your photo was persisted",
                    "Hi,\nyour photo was succesfully saved with ID " + id + ".");
            } catch (MessagingException ex) {
                throw new SubmitException(ex.getMessage());
            } catch (NamingException ex) {
                throw new SubmitException(ex.getMessage());
            }
        }
                
  • Right-click EJB project node in project tab and invoke New - File - Sun Resources - JavaMail Resources
  • Type required data, e.g. JNDI name mail/SunMail, host, user
  • Finish wizard

Message Driven bean

PhotoArchiveBean is message queue listener. The bean listens on specified queue and when a new message is delivered that the onMessage() method is invoked. PhotoArchive bean supports only ByteMessages.

  • Select EJB module project node and invoke New - Message-Driven bean.
  • Specify new bean's name, eg. PhotoArchive, choose package, eg. org.photoarchive.ejb and finish this wizard. New Sun Resources will be automatically generated under Server Resources node in project tab.
  • Create some helper logging method, eg. this one:
        private void print2log(String msg){
            Logger.getLogger("PhotoArchiveBean").log(Level.INFO,msg);
        }
                
  • Now right-click and invoke Enterprise Resources - Call Enterpise Bean, choose SubmitActionSB and press OK. Repeat this step also for PhotoItemEB.
  • Create new private instance variables: PhotoItemLocalHome photoItem and SubmitActionLocal submitBean and init them in ejbCreate() method like this:
        public void ejbCreate() {
            // TODO Add code to acquire and use other enterprise resources (DataSource, JMS, enterprise bean, Web services)
            photoItem = lookupPhotoItemBean();
            submitBean = lookupSubmitActionBean();
        }
                    
    Note that lookup* methods were generated after Call EJB action.
  • Implement onMessage() method
        public void onMessage(Message aMessage) {
            // TODO handle incoming message
            // typical implementation will delegate to session bean or application service
            String eMail = "jungi@sun.com";
            try {
                if (aMessage instanceof BytesMessage) { // only Byte message is allowed
                    BytesMessage msg = (BytesMessage) aMessage;
                    byte[] data = new byte[new Long(msg.getBodyLength()).intValue()];
                    msg.readBytes(data);
                    eMail = msg.getStringProperty("email");
                    print2Log("Get message with e-mail: " + eMail+ " and size: " + msg.getBodyLength());
                    int id = photoItem.create(eMail, data).getKey().intValue();
                    print2Log("Message was persisted in DB with id: " + id);
                    submitBean.sendMessage(eMail, id);
                    print2Log("Send e-mail to " + eMail);
                }
            } catch (JMSException jmsex) {
                print2Log("JMSError " + jmsex.getMessage());
                jmsex.printStackTrace();
            } catch (CreateException ex) {
                print2Log("Create bean error: " + ex.getMessage());
                ex.printStackTrace();
            } catch (SubmitException ex) {
                print2Log("Submit Error: " + ex.getMessage());
                ex.printStackTrace();
            }
        }
                
  • Run verifier and deploy J2EE application to server

FileUpload library

For parsing HTTP requests which conform to RFC 1867, "Form-based File Upload in HTML" is used external library FileUpload from Jakarta Project.

  • Expand web module project in project tab
  • Select Libraries node and invoke Add Library...
  • Click Manage Libraries button
  • Create new FileUpload library which will include commons-fileupload-x.y.z.jar from Jakarta's FileUpload Library.
  • Select created FileUpload library and press Add Library
  • Repeat same steps with commons-io-x.y.jar from Jakarta's Commons IO Library.

Upload servlet

Servlet process HTTP request, extracts image file and creates and sends new Byte message.

  • Rigth-click web module node in project tab
  • Invoke New - Servlet
  • Set UploadServlet as new servlet name, org.photoarchive.web as package and finish New File wizard
  • Open servlet's code in editor
  • Right-click inside servlet class and invoke Enterprise Resources - Send JMS Message, select PhotoArchive destination from opened ejb project and press OK button
  • Write new methods that process file and change sendJMS method's parameters, so they will look like this:
        private Message createJMSMessageForPhotoArchiveDestination(Session session, byte[] messageData, String eMail) throws JMSException {
            // TODO create and populate message to send
            // javax.jms.TextMessage tm = session.createTextMessage();
            // tm.setText(messageData.toString());
            // return tm;
             BytesMessage tm = session.createBytesMessage();
             tm.setStringProperty("email", eMail);
             tm.writeBytes(messageData);
             return tm;
           
        }
        
        private void sendJMSMessageToPhotoArchiveDestination(byte[] messageData, String eMail) throws NamingException, JMSException {
            javax.naming.Context c = new javax.naming.InitialContext();
            javax.jms.ConnectionFactory cf = (javax.jms.ConnectionFactory) c.lookup("java:comp/env/jms/PhotoArchiveDestinationFactory");
            javax.jms.Connection conn = null;
            javax.jms.Session s = null;
            try {
                conn = cf.createConnection();
                s = conn.createSession(false,s.AUTO_ACKNOWLEDGE);
                javax.jms.Destination destination = (javax.jms.Destination) c.lookup("java:comp/env/jms/PhotoArchiveDestination");
                javax.jms.MessageProducer mp = s.createProducer(destination);
                mp.send(createJMSMessageForPhotoArchiveDestination(s,messageData, eMail));
            } finally {
                if (s != null) {
                    s.close();
                }
                if (conn != null) {
                    conn.close();
                }
            }
        }
                
    Full source code can be found here

ShowPhoto Servlet

  • Rigth-click web module node in project tab
  • Invoke New - Servlet
  • Set ShowPhotoServlet as new servlet name, org.photoarchive.web as package and finish New File wizard
  • Open servlet's code in editor
  • Right-click inside servlet class and invoke Enterprise Resources - Use Database, select jdbc/PhotoArchiveDB and press OK button
  • Write helper method which will retrieve photo from your DB:
        private byte[] readPhoto(int id) throws SQLException, NamingException{
            byte[] img = null;
            String sql = "SELECT img FROM photo WHERE id = ?";
            Connection conn = getPhotoAlbumDB().getConnection();
            PreparedStatement prepStmt = conn.prepareStatement(sql);
            prepStmt.setInt(1, id);
            ResultSet rs = prepStmt.executeQuery();
            if (rs.next()) {
                img = rs.getBytes(1);
            }
            conn.close();
            return img;
        }
                
    Full source code can be found here

Running application

If you have downloaded all sources:

  • Unzip project, create necessary libraries and resolve broken references
  • Create database as is described at the begining of this section
  • Double click on PhotoArchive-ejb | Server Resources | mail_PhotoArchiveMail.sun-resource node and change "Default Return Address", "Mail Host" and "Default User" fields there to use values you can use
  • Deploy/Run PhotoArchive EAR

If you have followed this scenario step by step:

  • Put some forms which pass input to servlets into index.jsp
  • Create some error.jsp
  • Deploy/Run PhotoArchive EAR
result
Companion
Projects:
MySQL Database Server   Open JDK: an Open SourceJDK   GlassFish Community: an Open Source Application Server    Mobile & Embedded Community    Open Solaris   java.net - The Source for Java Technology Collaboration   Virtual Box - full virtualizer  Open ESB - The Open Enterprise Service Bus Powered by