CSCI398 Exercise 4: "Enterprise Java Beans" This exercise provides some practice in the creation of EJB applications in NetBeans and their deployment onto a Glassfish application. Netbeans 7 for Glassfish version 3.1 has slightly changed the way in which an application must be built; the examples should work in the lab but won’t work with an older version. There are many additional examples at the NetBeans site Stateless Session Bean The first example has a simple stateless session bean with web and “professional” client applications. It is, of course, a variation on the Guru example used elsewhere. 1 Create a new EJB project in NetBeans. New Java EE Enterprise Application Name the project Select Glassfish server 2 NetBeans creates several separate projects. There is an overall project, an EJB beans project, and a war project (the web client stuff). 3 The current Netbeans development setup now requires a separate “Java library” project to hold the “Remote Interface” components that will be generated for client (non-web) applications. This “Java library” project must be created before you start to define any EJB session beans. 4 The application client project must also be created: Should now have the following projects: 5 Within the Guru-ejb project, first create a new Java package and then add a stateless session bean to this package. Specify that it should support both local (web) and remote (application- client) interfaces. (Netbeans will offer a list of all “Java library” projects that it currently knows about – pick the project created for the remote interface). Should now have the following: 6 Write click in the code window for the bean, and pick the “Insert Code” option. It will provide a pop-up menu from which it is possible to select the “add business method” option. This will add a stub to the bean class and put the method signature into the local and remote interfaces. 7 The dialog that pops up for “Add Business Method” has fields for method name, return type, arguments, and exceptions: 8 The stub is added to the bean, and the method is declared in both interfaces. (A warning appears advising against having same method in both interfaces. This relates to fact that treatment of arguments does differ. If there are no arguments, or the arguments are effectively “const” then there are no problems.) (It’s rare to have to edit the “local” and “remote” interfaces directly; but if the methods of your bean use arguments or return results of specialized class types you may have to edit the generated interface files to add “import” statements.) 9 Define the typical implementation of the Guru’s enlightenMe() method: 10 Edit the application client. Again, right-click in the code and pick “Insert Code”/”Call Enterprise Bean”. A dialog will be displayed showing the beans that are known to NetBeans at this point; obviously, the only one currently defined should be the GuruBean. The application client will need to use this bean through its “Remote” interface. The code added is the declaration of a static variable referencing “the session bean” and an “annotation”. You can then add code that invokes a method of the “injected” bean. The code relyies on “resource injection” – just like the arrangement for servlets using JPA. But what is going to “inject” a reference to the bean? EJB clients are not run directly (java guruclient.Main). They are run using a “client container” program (“appclient”). When you run an EJB application client, it is actually the appclient program that starts. It examines the code generated for the client that you wrote. It finds the need to “inject” a bean. It identifies the glassfish server process where that bean exists. (How does it know the server? There is a deeply hidden configuration file in amongst the other files that were created when you first set up Netbeans. It has the address and port number of the glassfish server.) What bean does it use? Well, the startup code makes contact with the glassfish server asking for a reference to a stateless session bean – an instance of the GuruBean class. There isn’t one. But the server side code can create one and return a reference to the one that it created. 11 Build the Guru (EJB) project. 12 Make sure Glassfish is running. 13 Deploy the Guru project 14 Build the GuruClient 15 Run the client (Right-click on project in projects window). It should run (the output here is the line from Oscar Wilde concerning contradiction): 16 Web-application: Index.jsp – “welcome” page with a link to a servlet GuruServlet – “processRequest” method seeks enlightenment from an injected Gurubean; forwards string to JSP for display. Gurudisplay.jsp – page formatting response, embeds enlightenment into web page. Guru.gif – image for response page. 17 Edit the Guru-war project. The generated index.jsp page needs some content and a link to a servlet. 18 Add a package to the source files section for a new servlet class, and add the servlet. Again, write click in the code and pick “Insert Code”/ “Call Enterprise Bean” option: The GuruBean will again be selected, but here the local interface will be used: NetBeans will add the member for referencing the bean along with the @EJB annotation (that will in effect expand out to code that gets the application engine to “inject” the correct reference). 19 Define an implementation for the servlet that will use the bean to get some advice and will forward this advice to a JSP page where it will be displayed. 20 Define the Gurudisplay.jsp page and have it print the advice that gets passed as a String attachment to the request object: 21 Reuild the overall Guru project – it will build the ejb, war, and application client projects. 22 Deploy the overall Guru project. If there are problems, you may need to use Glassfish admin console to remove existing deployed versions of the Guru. Then you may need to restart Glassfish and again deploy the Guru project. (The current Netbeans/Glassfish combination doesn’t seem to like overwriting existing deployments.) 23 “Run” the project – the default configuration for “run” involves invoking the web project. It should display the “welcome” page (i.e. index.jsp): (Note, your appserver will almost certainly be using a different port.) 24 Seek advice from the Guru: . Stateful Session Bean (Grumble: it seems that every year I have to re-work all these examples because of some little change to Netbeans and/or Glassfish. Current (2012) scheme requires that any class – e.g. an application defined exception – that is used in both client and server will have to be a part of a separately built “Java library”. This library has to be compiled and added to other projects before any of their code gets defined.) The second example is a stateful session bean with web and “professional” client applications. It is the “HangMan” game illustrated in lectures – Game state, held in stateful bean, includes – Target word for this player – Number of guesses – Letters already used as guesses . 1 Create a new Enterprise project “Stateful”. This should result in Netbeans creating an EJB project Stateful; a Stateful-ejb project; and a Stateful-war project. Add a “Java library” project that will be used for the remote interface – HangmanRemote Add another “Java library” project – used to hold an exception class and a little struct class used in both clients and server. Finally, add a class for the client application. Should have something like: 2 The classes that go in the Auxclasses project (in an “others” package) are the MyException class and the “ResultOfGuess” class (as in lecture notes). 3 The Auxclasses project should be built and added to the other projects. 4 Next edit the Hangman-ejb project. This will need two packages – one for the session bean, and the other for auxiliary classes like the dictionary and the string images of hanged men. (The source for theses classes can be found in the /share/cs- pub/398/A4/exercises directory.) 5 Add the session bean: The stateful session bean holds the data that define the state of play of the game: The HangManBean has to supply three methods; so add these business methods (should appear in both local and remote interfaces): ResultOfGuess newGame(); boolean gameOverMan(); ResultOfGuess handleGuessedLetter( final String guessedLetter) throws MyException; Got to add parameters and exceptions. 6 NetBeans fills in stubs for these methods in the bean class (and adds them to the two interfaces): 7 Implement the business methods. OK – here is the code: public class HangmanBean implements HangmanRemote, HangmanLocal { private boolean gameInProgress; private int numGuesses; private int badGuessCount; private String guessedLetters; private String wordToGuess; private String knownLetters; private static final String letters = "abcdefghijklmnopqrstuvwxyz"; public ResultOfGuess newGame() { gameInProgress = true; numGuesses = 0; badGuessCount = 0; guessedLetters = ""; wordToGuess = Dictionary.pickAWord(); int wordlength = wordToGuess.length(); StringBuffer temp = new StringBuffer(); for (int i = 0; i < wordlength; i++) { temp.append('-'); } knownLetters = temp.toString(); ResultOfGuess rog = new ResultOfGuess(); rog.setWon(false); rog.setImageString(StringImages.getImage(0)); rog.setLettersGuessed(guessedLetters); rog.setKnowLetters(knownLetters); return rog; } public ResultOfGuess handleGuessedLetter( final String guessedLetter) throws MyException { if (!gameInProgress) { throw new MyException("No current game"); } if (badGuessCount >= StringImages.maxMoves()) { throw new MyException( "You have been hung. You are dead. You cannot keep guessing!"); } if (guessedLetter.length() != 1) { throw new MyException("Invalid Guess"); } char ch = guessedLetter.charAt(0); if (letters.indexOf(ch) < 0) { throw new MyException("Invalid Guess"); } if (guessedLetters.indexOf(ch) != -1) { throw new MyException("Letter already used"); } numGuesses++; guessedLetters = guessedLetters + guessedLetter; StringBuffer temp = new StringBuffer(); boolean goodGuess = false; for (int i = 0; i < wordToGuess.length(); i++) { if (ch == wordToGuess.charAt(i)) { temp.append(ch); goodGuess = true; } else { temp.append(knownLetters.charAt(i)); } } knownLetters = temp.toString(); if (!goodGuess) { badGuessCount++; } ResultOfGuess rog = new ResultOfGuess(); if (knownLetters.indexOf('-') == -1) { // All letters guessed. rog.setWon(true); rog.setImageString(null); rog.setLettersGuessed(guessedLetters); rog.setKnowLetters(knownLetters); } else { rog.setWon(false); rog.setImageString(StringImages.getImage(badGuessCount)); rog.setLettersGuessed(guessedLetters); rog.setKnowLetters(knownLetters); } return rog; } public boolean gameOverMan() { return (!gameInProgress) || (badGuessCount>=StringImages.maxMoves()); } } 8 Deploy the application. ? Yes – deploy the application. OK, there isn’t an application yet really as haven’t yet defined either a web client or an application client. But one needs to have the EJB bean deployed. When it is deployed, the Glassfish server prints out the JNDI name for the bean (well, really it is the name of the factory object that creates beans). This string will be needed when defining the web client. The name was something along the lines java:global/Stateful/Statful- ejb/Hangmanbean. 9 Next, edit the application client. As in the previous example, we can rely on the appclient container “injecting” a reference to the stateful bean. If you run several application clients concurrently, each will work with its own EJB bean reference. Each of these will refer to a different instance of the stateful bean that will get to be created in the server. 10 Write the client code: 11 Run the application: 12 If you are losing, you can always cheat. The EJB bean code prints the word to System.out so it will appear in the Glassfish console log: That makes it easier to win: 13 There are some extra issues with a web application. You cannot use resource injection to inject a reference to a stateful bean. A stateful bean holds user specific data. So each user of a servlet must have a reference to a different instance. Obviously, this reference cannot be a simple member of the servlet class. Servlet session state must be used. In the web app servlet you start a web session. You use an explicit JNDI lookup request to get a reference to a distinct instance of the stateful bean. You store this reference as an attribute of your session context. Also, if you want to access the web-app from another machine, you may have to open a port in your Ubuntu fire wall – but you probably won’t have permissions to do this. 14 Edit the default generated index.jsp to have a reference to the servlet that will handle game play. 15 Define a package and a servlet Its “doGet” method (as invoked via the link in the welcome page) should start a new game and respond with an initial form page that allows the user to select a first letter. The “doPost” method processes the user’s guess, and (if the game is not yet over) responds with another form page showing progress. 16 The Servlet has to use JNDI to get references to EJB beans: 17 The session context and bean reference will get created at the start of the doGet method: 18 Both doGet and doPost utilize the bean 19 Build and redeploy the application. 20 Try with multiple clients: Stateless session bean used to retrieve entities This simple example uses a table that you might have created on Oracle for an exercise in CSCI399: drop table soccerleague; drop sequence leagueseq; create sequence leagueseq increment by 1 start with 1; create table soccerleague ( gameid number primary key, played date, location varchar(64), team1 varchar(32), team2 varchar(32), score1 number, score2 number ); insert into soccerleague values ( leagueseq.nextval, TO_DATE('2007-08-26','YYYY-MM-DD'), 'Members Equity Stadium', 'Perth', 'Newcastle', 0, 0); ... The application retrieves details of soccer games, allowing a user to choose to receive all data, or just data for drawn games, or home wins, or away wins. (If you don’t have your Oracle table any longer, create something similar on MySQL.) The example only uses a single JPA Entity class (Soccerleague) which just has simple data members. There are no relationships amongst different entities, and there are no Collection data members representing one-to-many relations. This simplifies the set up slightly. The client application (and the “web” part of the application) do not need to link with a JPA implementation library EclipseLink. The “entity” part of the application uses only the Java Persistence jar file. The application consists of an “entities” Java class library project, and an Enterprise project with the usual ejb, client-app, remote interface, and web subprojects. Entity project (Java library) 1 Create the “entity” project 2 Define a package for the classes, and use “New entity class from database” to generate the JPA class from the soccerleague table definition in the database: 3 Add the JPA definitions from the Java Persistence library (the entity class library that is being created doesn’t need to define a persistence unit or link with a JPA implementation library): 4 If you generate the class from the Oracle table, it will end up using a BigDecimal for its primary key field, and BigInteger variables for scores (expecting a high scoring soccer match). Change these to Integer and int. 5 Generated class: 6 Build the entity project. NetBeans will create a .jar file for the entity class. Add this .jar file the other projects. Enterprise project and sub-projects 1 Create a new Enterprise application project; and a library project for the remote interface, and a client application project: Add the generated entities library to the projects: 2 Edit the ejb-subproject, adding first a package for the session bean and then the definition of the session bean with its remote and local interfaces. The ejb project needs the created entities library, along with the actual JPA implementation library. It also needs to define a persistence unit. The persistence unit definition needs to be edited to reference the entity classes already generated: The bean needs to support business methods that can be used to retrieve all games, drawn games, home wins, and away wins. 3 Create the application client. “Inject” a bean reference for the SoccerSessionBean and invoke its methods: 4 Build and deploy the EJB project. 5 Build and run the client application: 6 Create the web client. Modify the auto-generated index.jsp page so that it has a form that will allow a user to select games of interest. This form should submit data to a servlet. A reference to the bean gets injected into the servlet. The reference is used to invoke processing, with the results being forwarded to a JSP for final display. A final JSP is required to format the data. Of course, this will use JSTL (add the JSTL library to the war project): 7 Build, deploy, and test: