Development Guide
For Use with Red Hat JBoss Enterprise Application Platform 6
Abstract
Chapter 1. Get Started Developing Applications
1.1. Introduction
1.1.1. About Red Hat JBoss Enterprise Application Platform 6
1.2. Prerequisites
1.2.1. Become Familiar with Java Enterprise Edition 6
1.2.1.1. Overview of EE 6 Profiles
1.2.1.2. Java Enterprise Edition 6 Web Profile
Java EE 6 Web Profile Requirements
- Java Platform, Enterprise Edition 6
Java Web Technologies
- Servlet 3.0 (JSR 315)
- JSP 2.2 and Expression Language (EL) 1.2
- JavaServer Faces (JSF) 2.1 (JSR 314)
- Java Standard Tag Library (JSTL) for JSP 1.2
- Debugging Support for Other Languages 1.0 (JSR 45)
Enterprise Application Technologies
- Contexts and Dependency Injection (CDI) (JSR 299)
- Dependency Injection for Java (JSR 330)
- Enterprise JavaBeans 3.1 Lite (JSR 318)
- Java Persistence API 2.0 (JSR 317)
- Common Annotations for the Java Platform 1.1 (JSR 250)
- Java Transaction API (JTA) 1.1 (JSR 907)
- Bean Validation (JSR 303)
1.2.1.3. Java Enterprise Edition 6 Full Profile
Items Included in the EE 6 Full Profile
- EJB 3.1 (not Lite) (JSR 318)
- Java EE Connector Architecture 1.6 (JSR 322)
- Java Message Service (JMS) API 1.1 (JSR 914)
- JavaMail 1.4 (JSR 919)
Web Service Technologies
- Jax-RS RESTful Web Services 1.1 (JSR 311)
- Implementing Enterprise Web Services 1.3 (JSR 109)
- JAX-WS Java API for XML-Based Web Services 2.2 (JSR 224)
- Java Architecture for XML Binding (JAXB) 2.2 (JSR 222)
- Web Services Metadata for the Java Platform (JSR 181)
- Java APIs for XML-based RPC 1.1 (JSR 101)
- Java APIs for XML Messaging 1.3 (JSR 67)
- Java API for XML Registries (JAXR) 1.0 (JSR 93)
Management and Security Technologies
- Java Authentication Service Provider Interface for Containers 1.0 (JSR 196)
- Java Authentication Contract for Containers 1.3 (JSR 115)
- Java EE Application Deployment 1.2 (JSR 88)
- J2EE Management 1.1 (JSR 77)
1.2.2. About Modules and the New Modular Class Loading System used in JBoss EAP 6
1.2.2.1. Modules
- Static Modules
- Static Modules are predefined in the
EAP_HOME/modules/
directory of the application server. Each sub-directory represents one module and defines amain/
subdirectory that contains a configuration file (module.xml
) and any required JAR files. The name of the module is defined in themodule.xml
file. All the application server provided APIs are provided as static modules, including the Java EE APIs as well as other APIs such as JBoss Logging.Example 1.1. Example module.xml file
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="urn:jboss:module:1.0" name="com.mysql"> <resources> <resource-root path="mysql-connector-java-5.1.15.jar"/> </resources> <dependencies> <module name="javax.api"/> <module name="javax.transaction.api"/> </dependencies> </module>
The module name,com.mysql
, should match the directory structure for the module, excluding themain/
subdirectory name.The modules provided in JBoss EAP distributions are located in asystem
directory within theJBOSS_HOME/modules
directory. This keeps them separate from any modules provided by third parties.Any Red Hat provided layered products that layer on top of JBoss EAP 6.1 or later will also install their modules within thesystem
directory.Creating custom static modules can be useful if many applications are deployed on the same server that use the same third party libraries. Instead of bundling those libraries with each application, a module containing these libraries can be created and installed by the JBoss administrator. The applications can then declare an explicit dependency on the custom static modules.Users must ensure that custom modules are installed into theJBOSS_HOME/modules
directory, using a one directory per module layout. This ensures that custom versions of modules that already exist in thesystem
directory are loaded instead of the shipped versions. In this way, user provided modules will take precedence over system modules.If you use theJBOSS_MODULEPATH
environment variable to change the locations in which JBoss EAP searches for modules, then the product will look for asystem
subdirectory structure within one of the locations specified. Asystem
structure must exist somewhere in the locations specified withJBOSS_MODULEPATH
. - Dynamic Modules
- Dynamic Modules are created and loaded by the application server for each JAR or WAR deployment (or subdeployment in an EAR). The name of a dynamic module is derived from the name of the deployed archive. Because deployments are loaded as modules, they can configure dependencies and be used as dependencies by other deployments.
1.3. Set Up the Development Environment
1.3.1. Download and Install Red Hat JBoss Developer Studio
1.3.1.1. Setup Red Hat JBoss Developer Studio
1.3.1.2. Download Red Hat JBoss Developer Studio 7.1
- Go to https://access.redhat.com/.
- Selectfrom the menu at the top of the page.
- Find
Red Hat JBoss Developer Studio
in the list and click on it. - Select the appropriate version and click.
1.3.1.3. Install Red Hat JBoss Developer Studio 7.1
Procedure 1.1. Install Red Hat JBoss Developer Studio 7.1
- Open a terminal.
- Move into the directory containing the downloaded
.jar
file. - Run the following command to launch the GUI installer:
java -jar jbdevstudio-build_version.jar
- Clickto start the installation process.
- Select I accept the terms of this license agreement and click .
- Adjust the installation path and click.
Note
If the installation path folder does not exist, a prompt will appear. Clickto create the folder. - Choose a JVM, or leave the default JVM selected, and click.
- Add any application platforms available, and click.
- Review the installation details, and click.
- Clickwhen the installation process is complete.
- Configure the desktop shortcuts for Red Hat JBoss Developer Studio, and click.
- Click.
1.3.1.4. Start Red Hat JBoss Developer Studio
Procedure 1.2. Command to start Red Hat JBoss Developer Studio
- Open a terminal.
- Change into the installation directory.
- Run the following command to start Red Hat JBoss Developer Studio:
[localhost]$ ./jbdevstudio
1.3.1.5. Add the JBoss EAP Server Using Define New Server
Procedure 1.3. Add the server
- Open the Servers tab. If there is no Servers tab, add it to the panel as follows:
- Click→ → .
- Select Servers from the Server folder and click .
- Click on the link to create a new server or right-click within the blank Server panel and select → .
Figure 1.1. Add a new server - No servers available
- Expand JBoss Enterprise Middleware and choose . Click to create the JBoss runtime and define the server. The next time you define a new server, this dialog displays a Server runtime environment selection with the new runtime definition.
Figure 1.2. Define a New Server
- Enter a name, for example "JBoss EAP 6.3 Runtime". Under Home Directory, click and navigate to your JBoss EAP install location. Then click .
Figure 1.3. Add New Server Runtime Environment
Note
Some quickstarts require that you run the server with a different profile or additional arguments. To deploy a quickstart that requires thefull
profile, you must define a new server and add a Server Runtime Environment that specifiesstandalone-full.xml
for the Configuration file. Be sure to give the new server a descriptive name. - On this screen you define the server behavior. You can start the server manually or let Red Hat JBoss Developer Studio manage it for you. You can also define a remote server for deployment and determine if you want to expose the management port for that server, for example, if you need connect to it using JMX. In this example, we assume the server is local and you want Red Hat JBoss Developer Studio to manage your server so you do not need to check anything. Click.
Figure 1.4. Define the New JBoss Server Behavior
- This screen allows you to configure existing projects for the new server. Because you do not have any projects at this point, click.
Figure 1.5. Modify resources for the new JBoss server
The JBoss EAP Runtime Server is listed in the Servers tab.
Figure 1.6. Server appears in the server list
1.4. Run Your First Application
1.4.1. Download the Quickstart Code Examples
1.4.1.1. Access the Quickstarts
JBoss EAP 6 comes with a series of quickstart examples designed to help users begin writing applications using the Java EE 6 technologies.
Prerequisites
- Maven 3.0.0 or higher. For more information on installing Maven, refer to http://maven.apache.org/download.html.
- The JBoss EAP 6.3 Maven respository is available online, so it is not necessary to download and install it locally. If you plan to use the online repository, you can skip to the next step. If you prefer to install a local repository, see: Section 2.2.3, “Install the JBoss EAP 6 Maven Repository Locally”.
Procedure 1.4. Download the Quickstarts
- Open a web browser and access this URL: https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform.
- Find "Quickstarts" in the list.
- Click thebutton to download a Zip archive containing the examples.
- Unzip the archive in a directory of your choosing.
The JBoss EAP Quickstarts have been downloaded and unzipped. Refer to the README.md
file in the top-level directory of the Quickstart archive for instructions about deploying each quickstart.
1.4.2. Run the Quickstarts
1.4.2.1. Run the Quickstarts in Red Hat JBoss Developer Studio
Procedure 1.5. Import the quickstarts into Red Hat JBoss Developer Studio
Important
- If you have not yet done so, Section 2.3.2, “Configure the JBoss EAP 6 Maven Repository Using the Maven Settings”.
- Start Red Hat JBoss Developer Studio.
- From the menu, select→ .
- In the selection list, choose→ , then click .
Figure 1.7. Import Existing Maven Projects
- Browse to the directory of the quickstart you plan to test, for example the
helloworld
quickstart, and click . The Projects list box is populated with thepom.xml
file of the selected quickstart project.Figure 1.8. Select Maven Projects
- Click.
Procedure 1.6. Build and Deploy the helloworld
quickstart
helloworld
quickstart is one of the simplest quickstarts and is a good way to verify that the JBoss server is configured and running correctly.
- If you do not see a Servers tab or have not yet defined a server, follow the instructions here: Section 1.3.1.5, “Add the JBoss EAP Server Using Define New Server”. If you plan to deploy a quickstart that requires the
full
profile or additional startup arguments, be sure to create the server runtime environment as noted in the quickstart instructions. - Right-click on the
jboss-helloworld
project in the Project Explorer tab and select . You are provided with a list of choices. Select .Figure 1.9. Run As - Run on Server
- Select JBoss EAP 6.1+ Runtime Server from the server list and click .
Figure 1.10. Run on Server
- The next screen displays the resources that are configured on the server. The
jboss-helloworld
quickstart is configured for you. Click to deploy the quickstart.Figure 1.11. Modify Resources Configured on the Server
- Review the results.
- In the
Server
tab, the JBoss EAP 6.3 Runtime Server status changes to[Started, Republish]
. - The server Console tab shows messages detailing the JBoss EAP 6.3 server start and the helloworld quickstart deployment.
- A helloworld tab appears displaying the URL http://localhost:8080/jboss-helloworld/HelloWorld and the text "Hello World!".
- The following messages in the Console confirm deployment of the
jboss-helloworld.war
file:JBAS018210: Register web context: /jboss-helloworld JBAS018559: Deployed "jboss-helloworld.war" (runtime-name : "jboss-helloworld.war")
The registered web context is appended tohttp://localhost:8080
to provide the URL used to access the deployed application.
- To verify the
helloworld
quickstart deployed successfully to the JBoss server, open a web browser and access the application at this URL: http://localhost:8080/jboss-helloworld
Procedure 1.7. Run the bean-validation
quickstart Arquillian tests
bean-validation
quickstart is an example of a quickstart that provides Arquillian tests.
- Follow the procedure above to import the
bean-validation
quickstart into Red Hat JBoss Developer Studio. - If you do not see a Servers tab or have not yet defined a server, follow the instructions here: Section 1.3.1.5, “Add the JBoss EAP Server Using Define New Server”
- Right-click on the
jboss-bean-validation
project in the Project Explorer tab and select . You are provided with a list of choices. Select . - In the Goals input field of the Edit Configuration dialog, type:
clean test -Parq-jbossas-remote
Then click.Figure 1.12. Edit Configuration
- Review the results.The server Console tab shows messages detailing the JBoss EAP server start and the output of the
bean-validation
quickstart Arquillian tests.------------------------------------------------------- T E S T S ------------------------------------------------------- Running org.jboss.as.quickstarts.bean_validation.test.MemberValidationTest Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.189 sec Results : Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------
1.4.2.2. Run the Quickstarts Using a Command Line
Procedure 1.8. Build and Deploy the Quickstarts Using a Command Line
- If you have not yet done so, Section 2.3.2, “Configure the JBoss EAP 6 Maven Repository Using the Maven Settings”.
- Review the
README.html
file in the root directory of the quickstarts.This file contains general information about system requirements, how to configure Maven, how to add users, and how to run the Quickstarts. Be sure to read through it before you get started.It also contains a table listing the available quickstarts. The table lists each quickstart name and the technologies it demonstrates. It gives a brief description of each quickstart and the level of experience required to set it up. For more detailed information about a quickstart, click on the quickstart name.Some quickstarts are designed to enhance or extend other quickstarts. These are noted in thePrerequisites
column. If a quickstart lists prerequisites, you must install them first before working with the quickstart.Some quickstarts require the installation and configuration of optional components. Do not install these components unless the quickstart requires them. - Run the
helloworld
quickstart.Thehelloworld
quickstart is one of the simplest quickstarts and is a good way to verify that the JBoss server is configured and running correctly. Open theREADME.html
file in the root of thehelloworld
quickstart. It contains detailed instructions on how to build and deploy the quickstart and access the running application - Run the other quickstarts.Follow the instructions in the
README.html
file located in the root folder of each quickstart to run the example.
1.4.3. Review the Quickstart Tutorials
1.4.3.1. Explore the helloworld Quickstart
The helloworld quickstart shows you how to deploy a simple Servlet to JBoss EAP 6. The business logic is encapsulated in a service which is provided as a CDI (Contexts and Dependency Injection) bean and injected into the Servlet. This quickstart is very simple. All it does is print "Hello World" onto a web page. It is a good starting point to be sure you have configured and started your server properly.
helloworld
quickstart.
- Install Red Hat JBoss Developer Studio following the procedure here: Section 1.3.1.3, “Install Red Hat JBoss Developer Studio 7.1”.
- Configure Maven for use with Red Hat JBoss Developer Studio following the procedure here: Section 2.3.3, “Configure Maven for Use with Red Hat JBoss Developer Studio”.
- Follow the procedures here to import, build, and deploy the
helloworld
quickstart in Red Hat JBoss Developer Studio: Section 1.4.2.1, “Run the Quickstarts in Red Hat JBoss Developer Studio” - Verify the
helloworld
quickstart was deployed successfully to JBoss EAP by opening a web browser and accessing the application at this URL: http://localhost:8080/jboss-helloworld
Procedure 1.9. Examine the Directory Structure
QUICKSTART_HOME/helloworld
directory. The helloworld quickstart is comprised a Servlet and a CDI bean. It also includes an empty beans.xml file which tells JBoss EAP 6 to look for beans in this application and to activate the CDI.
- The
beans.xml
file is located in theWEB-INF/
folder in thesrc/main/webapp/
directory of the quickstart. - The
src/main/webapp/
directory also includes anindex.html
file which uses a simple meta refresh to redirect the user's browser to the Servlet, which is located at http://localhost:8080/jboss-helloworld/HelloWorld. - All the configuration files for this example are located in
WEB-INF/
, which can be found in thesrc/main/webapp/
directory of the example. - Notice that the quickstart doesn't even need a
web.xml
file!
Procedure 1.10. Examine the Code
Review the HelloWorldServlet code
TheHelloWorldServlet.java
file is located in thesrc/main/java/org/jboss/as/quickstarts/helloworld/
directory. This Servlet sends the information to the browser.42. @SuppressWarnings("serial") 43. @WebServlet("/HelloWorld") 44. public class HelloWorldServlet extends HttpServlet { 45. 46. static String PAGE_HEADER = "<html><head><title>helloworld</title></head><body>"; 47. 48. static String PAGE_FOOTER = "</body></html>"; 49. 50. @Inject 51. HelloService helloService; 52. 53. @Override 54. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 55. resp.setContentType("text/html"); 56. PrintWriter writer = resp.getWriter(); 57. writer.println(PAGE_HEADER); 58. writer.println("<h1>" + helloService.createHelloMessage("World") + "</h1>"); 59. writer.println(PAGE_FOOTER); 60. writer.close(); 61. } 62. 63. }
Table 1.1. HelloWorldServlet Details Line Note 43 Before Java EE 6, an XML file was used to register Servlets. It is now much cleaner. All you need to do is add the @WebServlet
annotation and provide a mapping to a URL used to access the servlet.46-48 Every web page needs correctly formed HTML. This quickstart uses static Strings to write the minimum header and footer output. 50-51 These lines inject the HelloService CDI bean which generates the actual message. As long as we don't alter the API of HelloService, this approach allows us to alter the implementation of HelloService at a later date without changing the view layer. 58 This line calls into the service to generate the message "Hello World", and write it out to the HTTP request. Review the HelloService code
TheHelloService.java
file is located in thesrc/main/java/org/jboss/as/quickstarts/helloworld/
directory. This service is very simple. It returns a message. No XML or annotation registration is required.public class HelloService { String createHelloMessage(String name) { return "Hello " + name + "!"; } }
1.4.3.2. Explore the numberguess Quickstart
This quickstart shows you how to create and deploy a simple application to JBoss EAP 6. This application does not persist any information. Information is displayed using a JSF view, and business logic is encapsulated in two CDI (Contexts and Dependency Injection) beans. In the numberguess quickstart, you get 10 attempts to guess a number between 1 and 100. After each attempt, you're told whether your guess was too high or too low.
QUICKSTART_HOME/numberguess
directory. The numberguess quickstart is comprised of a number of beans, configuration files and Facelets (JSF) views, packaged as a WAR module.
numberguess
quickstart.
- Install Red Hat JBoss Developer Studio following the procedure here: Section 1.3.1.3, “Install Red Hat JBoss Developer Studio 7.1”.
- Configure Maven for use with Red Hat JBoss Developer Studio following the procedure here: Section 2.3.3, “Configure Maven for Use with Red Hat JBoss Developer Studio”.
- Follow the procedures here to import, build, and deploy the
numberguess
quickstart in Red Hat JBoss Developer Studio: Section 1.4.2.1, “Run the Quickstarts in Red Hat JBoss Developer Studio” - Verify the
numberguess
quickstart was deployed successfully to JBoss EAP by opening a web browser and accessing the application at this URL: http://localhost:8080/jboss-numberguess
Procedure 1.11. Examine the Configuration Files
WEB-INF/
directory which can be found in the src/main/webapp/
directory of the quickstart.
- Examine the
faces-config.xml
file.This quickstart uses the JSF 2.0 version offaces-config.xml
filename. A standardized version of Facelets is the default view handler in JSF 2.0, so there's really nothing that you have to configure. JBoss EAP 6 goes above and beyond Java EE here. It will automatically configure the JSF for you if you include this configuration file. As a result, the configuration consists of only the root element:19. <faces-config version="2.0" 20. xmlns="http://java.sun.com/xml/ns/javaee" 21. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 22. xsi:schemaLocation=" 23. http://java.sun.com/xml/ns/javaee> 24. http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"> 25. 26. </faces-config>
- Examine the
beans.xml
file.There's also an emptybeans.xml
file, which tells JBoss EAP 6 to look for beans in this application and to activate the CDI. - There is no
web.xml
fileNotice that the quickstart doesn't even need aweb.xml
file!
Procedure 1.12. Examine the JSF Code
.xhtml
file extension for source files, but serves up the rendered views with the .jsf
extension.
- Examine the
home.xhtml
code.Thehome.xhtml
file is located in thesrc/main/webapp/
directory.19. <html xmlns="http://www.w3.org/1999/xhtml" 20. xmlns:ui="http://java.sun.com/jsf/facelets" 21. xmlns:h="http://java.sun.com/jsf/html" 22. xmlns:f="http://java.sun.com/jsf/core"> 23. 24. <head> 25. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> 26. <title>Numberguess</title> 27. </head> 28. 29. <body> 30. <div id="content"> 31. <h1>Guess a number...</h1> 32. <h:form id="numberGuess"> 33. 34. <!-- Feedback for the user on their guess --> 35. <div style="color: red"> 36. <h:messages id="messages" globalOnly="false" /> 37. <h:outputText id="Higher" value="Higher!" 38. rendered="#{game.number gt game.guess and game.guess ne 0}" /> 39. <h:outputText id="Lower" value="Lower!" 40. rendered="#{game.number lt game.guess and game.guess ne 0}" /> 41. </div> 42. 43. <!-- Instructions for the user --> 44. <div> 45. I'm thinking of a number between <span 46. id="numberGuess:smallest">#{game.smallest}</span> and <span 47. id="numberGuess:biggest">#{game.biggest}</span>. You have 48. #{game.remainingGuesses} guesses remaining. 49. </div> 50. 51. <!-- Input box for the users guess, plus a button to submit, and reset --> 52. <!-- These are bound using EL to our CDI beans --> 53. <div> 54. Your guess: 55. <h:inputText id="inputGuess" value="#{game.guess}" 56. required="true" size="3" 57. disabled="#{game.number eq game.guess}" 58. validator="#{game.validateNumberRange}" /> 59. <h:commandButton id="guessButton" value="Guess" 60. action="#{game.check}" 61. disabled="#{game.number eq game.guess}" /> 62. </div> 63. <div> 64. <h:commandButton id="restartButton" value="Reset" 65. action="#{game.reset}" immediate="true" /> 66. </div> 67. </h:form> 68. 69. </div> 70. 71. <br style="clear: both" /> 72. 73. </body> 74. </html>
Table 1.2. JSF Details Line Note 36-40 These are the messages which can be sent to the user: "Higher!" and "Lower!" 45-48 As the user guesses, the range of numbers they can guess gets smaller. This sentence changes to make sure they know the number range of a valid guess. 55-58 This input field is bound to a bean property using a value expression. 58 A validator binding is used to make sure the user does not accidentally input a number outside of the range in which they can guess. If the validator was not here, the user might use up a guess on an out of bounds number. 59-61 There must be a way for the user to send their guess to the server. Here we bind to an action method on the bean.
Procedure 1.13. Examine the Class Files
src/main/java/org/jboss/as/quickstarts/numberguess/
directory. The package declaration and imports have been excluded from these listings. The complete listing is available in the quickstart source code.
- Review the
Random.java
qualifier code.A qualifier is used to remove ambiguity between two beans, both of which are eligible for injection based on their type. For more information on qualifiers, refer to Section 10.2.3.3, “Use a Qualifier to Resolve an Ambiguous Injection”The@Random
qualifier is used for injecting a random number.@Target({ TYPE, METHOD, PARAMETER, FIELD }) @Retention(RUNTIME) @Documented @Qualifier public @interface Random { }
- Review the
MaxNumber.java
qualifier code.The@MaxNumber
qualifier
is used for injecting the maximum number allowed.@Target({ TYPE, METHOD, PARAMETER, FIELD }) @Retention(RUNTIME) @Documented @Qualifier public @interface MaxNumber { }
- Review the
Generator.java
code.TheGenerator
class is responsible for creating the random number via a producer method. It also exposes the maximum possible number via a producer method. This class is application scoped so you don't get a different random each time.@SuppressWarnings("serial") @ApplicationScoped public class Generator implements Serializable { private java.util.Random random = new java.util.Random(System.currentTimeMillis()); private int maxNumber = 100; java.util.Random getRandom() { return random; } @Produces @Random int next() { // a number between 1 and 100 return getRandom().nextInt(maxNumber - 1) + 1; } @Produces @MaxNumber int getMaxNumber() { return maxNumber; } }
- Review the
Game.java
code.The session scoped classGame
is the primary entry point of the application. It is responsible for setting up or resetting the game, capturing and validating the user's guess, and providing feedback to the user with aFacesMessage
. It uses the post-construct lifecycle method to initialize the game by retrieving a random number from the@Random Instance
<Integer>
bean.Notice the @Named annotation in the class. This annotation is only required when you want to make the bean accessible to a JSF view via Expression Language (EL), in this case#{game}
.@SuppressWarnings("serial") @Named @SessionScoped public class Game implements Serializable { /** * The number that the user needs to guess */ private int number; /** * The users latest guess */ private int guess; /** * The smallest number guessed so far (so we can track the valid guess range). */ private int smallest; /** * The largest number guessed so far */ private int biggest; /** * The number of guesses remaining */ private int remainingGuesses; /** * The maximum number we should ask them to guess */ @Inject @MaxNumber private int maxNumber; /** * The random number to guess */ @Inject @Random Instance<Integer> randomNumber; public Game() { } public int getNumber() { return number; } public int getGuess() { return guess; } public void setGuess(int guess) { this.guess = guess; } public int getSmallest() { return smallest; } public int getBiggest() { return biggest; } public int getRemainingGuesses() { return remainingGuesses; } /** * Check whether the current guess is correct, and update the biggest/smallest guesses as needed. Give feedback to the user * if they are correct. */ public void check() { if (guess > number) { biggest = guess - 1; } else if (guess < number) { smallest = guess + 1; } else if (guess == number) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!")); } remainingGuesses--; } /** * Reset the game, by putting all values back to their defaults, and getting a new random number. We also call this method * when the user starts playing for the first time using {@linkplain PostConstruct @PostConstruct} to set the initial * values. */ @PostConstruct public void reset() { this.smallest = 0; this.guess = 0; this.remainingGuesses = 10; this.biggest = maxNumber; this.number = randomNumber.get(); } /** * A JSF validation method which checks whether the guess is valid. It might not be valid because there are no guesses left, * or because the guess is not in range. * */ public void validateNumberRange(FacesContext context, UIComponent toValidate, Object value) { if (remainingGuesses <= 0) { FacesMessage message = new FacesMessage("No guesses left!"); context.addMessage(toValidate.getClientId(context), message); ((UIInput) toValidate).setValid(false); return; } int input = (Integer) value; if (input < smallest || input > biggest) { ((UIInput) toValidate).setValid(false); FacesMessage message = new FacesMessage("Invalid guess"); context.addMessage(toValidate.getClientId(context), message); } } }
1.4.4. Replace the Default Welcome Web Application
Procedure 1.14. Replace the Default Welcome Web Application With Your Own Web Application
Disable the Welcome application.
Use the Management CLI scriptEAP_HOME/bin/jboss-cli.sh
to run the following command. You may need to change the profile to modify a different managed domain profile, or remove the/profile=default
portion of the command for a standalone server./profile=default/subsystem=web/virtual-server=default-host:write-attribute(name=enable-welcome-root,value=false)
Configure your Web application to use the root context.
To configure your web application to use the root context (/) as its URL address, modify itsjboss-web.xml
, which is located in theMETA-INF/
orWEB-INF/
directory. Replace its<context-root>
directive with one that looks like the following.<jboss-web> <context-root>/</context-root> </jboss-web>
Deploy your application.
Deploy your application to the server group or server you modified in the first step. The application is now available onhttp://SERVER_URL:PORT/
.
Chapter 2. Maven Guide
2.1. Learn about Maven
2.1.1. About the Maven Repository
http://
for a repository on an HTTP server or file://
for a repository on a file server.
2.1.2. About the Maven POM File
pom.xml
file requires some configuration options and will default all others. See Section 2.1.3, “Minimum Requirements of a Maven POM File” for details.
pom.xml
file can be found at http://maven.apache.org/maven-v4_0_0.xsd.
2.1.3. Minimum Requirements of a Maven POM File
The minimum requirements of a pom.xml
file are as follows:
- project root
- modelVersion
- groupId - the id of the project's group
- artifactId - the id of the artifact (project)
- version - the version of the artifact under the specified group
A basic pom.xml
file might look like this:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.jboss.app</groupId> <artifactId>my-app</artifactId> <version>1</version> </project>
2.1.4. About the Maven Settings File
settings.xml
file contains user-specific configuration information for Maven. It contains information that should not be distributed with the pom.xml
file, such as developer identity, proxy information, local repository location, and other settings specific to a user.
settings.xml
can be found.
- In the Maven install
- The settings file can be found in the
M2_HOME/conf/
directory. These settings are referred to asglobal
settings. The default Maven settings file is a template that can be copied and used as a starting point for the user settings file. - In the user's install
- The settings file can be found in the
USER_HOME/.m2/
directory. If both the Maven and usersettings.xml
files exist, the contents are merged. Where there are overlaps, the user'ssettings.xml
file takes precedence.
settings.xml
file:
<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <profiles> <!-- Configure the JBoss EAP Maven repository --> <profile> <id>jboss-eap-maven-repository</id> <repositories> <repository> <id>jboss-eap</id> <url>file:///path/to/repo/jboss-eap-6.3-maven-repository</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>jboss-eap-maven-plugin-repository</id> <url>file:///path/to/repo/jboss-eap-6.3-maven-repository</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <activeProfiles> <!-- Optionally, make the repository active by default --> <activeProfile>jboss-eap-maven-repository</activeProfile> </activeProfiles> </settings>
settings.xml
file can be found at http://maven.apache.org/xsd/settings-1.0.0.xsd.
2.2. Install Maven and the JBoss Maven Repository
2.2.1. Download and Install Maven
- Go to Apache Maven Project - Download Maven and download the latest distribution for your operating system.
- See the Maven documentation for information on how to download and install Apache Maven for your operating system.
2.2.2. Install the JBoss EAP 6 Maven Repository
2.2.3. Install the JBoss EAP 6 Maven Repository Locally
The JBoss EAP 6.3 Maven repository is available online, so it is not necessary to download and install it locally. However, if you prefer to install the JBoss EAP Maven repository locally, there are three ways to do it: on your local file system, on Apache Web Server, or with a Maven repository manager. This example covers the steps to download the JBoss EAP 6 Maven Repository to the local file system. This option is easy to configure and allows you to get up and running quickly on your local machine. It can help you become familiar with using Maven for development but is not recommended for team production environments.
Procedure 2.1. Download and Install the JBoss EAP 6 Maven Repository to the Local File System
- Open a web browser and access this URL: https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform.
- Find "Red Hat JBoss Enterprise Application Platform 6.3.0 Maven Repository" in the list.
- Click thebutton to download a
.zip
file containing the repository. - Unzip the file on the local file system into a directory of your choosing.
This creates a Maven repository directory called jboss-eap-6.3.0.GA-maven-repository
.
Important
settings.xml
configuration file. Each local repository must be configured within its own <repository>
tag.
Important
repository/
subdirectory located under the .m2/
directory before attempting to use the new Maven repository.
2.2.4. Install the JBoss EAP 6 Maven Repository for Use with Apache httpd
You must configure Apache httpd. See Apache HTTP Server Project documentation for instructions.
Procedure 2.2. Download the JBoss EAP 6 Maven Repository ZIP archive
- Open a web browser and access this URL: https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform.
- Find "Red Hat JBoss Enterprise Application Platform 6.3.0 Maven Repository" in the list.
- Click thebutton to download a
.zip
file containing the repository. - Unzip the files in a directory that is web accessible on the Apache server.
- Configure Apache to allow read access and directory browsing in the created directory.
This allows a multi-user environment to access the Maven repository on Apache httpd.
Note
2.2.5. Install the JBoss EAP 6 Maven Repository Using Nexus Maven Repository Manager
Procedure 2.3. Download the JBoss EAP 6 Maven Repository ZIP archive
- Open a web browser and access this URL: https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=appplatform.
- Find "Red Hat JBoss Enterprise Application Platform 6.3.0 Maven Repository" in the list.
- Click thebutton to download a
.zip
file containing the repository. - Unzip the files into a directory of your choosing on the server hosting Nexus.
Procedure 2.4. Add the JBoss EAP 6 Maven Repository using Nexus Maven Repository Manager
- Log into Nexus as an Administrator.
- Select the Repositories section from the → menu to the left of your repository manager.
- Click the Add... dropdown, then select Hosted Repository.
- Give the new repository a name and ID.
- Enter the path on disk to the unzipped repository in the field Override Local Storage Location.
- Continue if you want the artifact to be available in a repository group. Do not continue with this procedure if this is not what you want.
- Select the repository group.
- Click on the Configure tab.
- Drag the new JBoss Maven repository from the Available Repositories list to the Ordered Group Repositories list on the left.
Note
Note that the order of this list determines the priority for searching Maven artifacts.
The repository is configured using Nexus Maven Repository Manager.
2.2.6. About Maven Repository Managers
- They provide the ability to configure proxies between your organization and remote Maven repositories. This provides a number of benefits, including faster and more efficient deployments and a better level of control over what is downloaded by Maven.
- They provide deployment destinations for your own generated artifacts, allowing collaboration between different development teams across an organization.
Commonly used Maven repository managers
- Sonatype Nexus
- See Sonatype Nexus: Manage Artifacts for more information about Nexus.
- Artifactory
- See Artifactory Open Source for more information about Artifactory.
- Apache Archiva
- See Apache Archiva: The Build Artifact Repository Manager for more information about Apache Archiva.
2.3. Use the Maven Repository
2.3.1. Configure the JBoss EAP Maven 6 Repository
There are two approaches to direct Maven to use the JBoss EAP 6 Maven Repository in your project:
- You can configure the repositories in the Maven global or user settings.
- You can configure the repositories in the project's POM file.
Procedure 2.5. Configure Maven Settings to Use the JBoss EAP 6 Maven Repository
Configure the Maven repository using Maven settings
This is the recommended approach. Maven settings used with a repository manager or repository on a shared server provide better control and manageability of projects. Settings also provide the ability to use an alternative mirror to redirect all lookup requests for a specific repository to your repository manager without changing the project files. For more information about mirrors, see http://maven.apache.org/guides/mini/guide-mirror-settings.html.This method of configuration applies across all Maven projects, as long as the project POM file does not contain repository configuration.Configure the Maven repository using the project POM
This method of configuration is generally not recommended. If you decide to configure repositories in your project POM file, plan carefully and be aware that it can slow down your build and you may even end up with artifacts that are not from the expected repository.Note
In an Enterprise environment, where a repository manager is usually used, Maven should query all artifacts for all projects using this manager. Because Maven uses all declared repositories to find missing artifacts, if it can't find what it's looking for, it will try and look for it in the repository central (defined in the built-in parent POM). To override this central location, you can add a definition withcentral
so that the default repository central is now your repository manager as well. This works well for established projects, but for clean or 'new' projects it causes a problem as it creates a cyclic dependency.Transitively included POMs are also an issue with this type of configuration. Maven has to query these external repositories for missing artifacts. This not only slows down your build, it also causes you to lose control over where your artifacts are coming from and likely to cause broken builds.This method of configuration overrides the global and user Maven settings for the configured project.
2.3.2. Configure the JBoss EAP 6 Maven Repository Using the Maven Settings
- You can modify the Maven settings. This directs Maven to use the configuration across all projects.
- You can configure the project's POM file. This limits the configuration to the specific project.
Note
- File System
- file:///path/to/repo/jboss-eap-6.x-maven-repository
- Apache Web Server
- http://intranet.acme.com/jboss-eap-6.x-maven-repository/
- Nexus Repository Manager
- https://intranet.acme.com/nexus/content/repositories/jboss-eap-6.x-maven-repository
Procedure 2.6. Configure Maven Using the Settings Shipped with the Quickstart Examples
settings.xml
file that is configured to use the online JBoss EAP 6 Maven repository. This is the simplest approach.
- This procedure overwrites the existing Maven settings file, so you must back up the existing Maven
settings.xml
file.- Locate the Maven install directory for your operating system. It is usually installed in
USER_HOME/.m2/
directory.- For Linux or Mac, this is:
~/.m2/
- For Windows, this is:
\Documents and Settings\USER_NAME\.m2\
or\Users\USER_NAME\.m2\
- If you have an existing
USER_HOME/.m2/settings.xml
file, rename it or make a backup copy so you can restore it later.
- Download and unzip the quickstart examples that ship with JBoss EAP 6. For more information, see Section 1.4.1.1, “Access the Quickstarts”
- Copy the
QUICKSTART_HOME/settings.xml
file to theUSER_HOME/.m2/
directory. - If you modify the
settings.xml
file while Red Hat JBoss Developer Studio is running, follow the procedure below entitled Refresh the Red Hat JBoss Developer Studio User Settings.
Procedure 2.7. Manually Edit and Configure the Maven Settings To Use the Online JBoss EAP 6 Maven Repository
- Locate the Maven install directory for your operating system. It is usually installed in
USER_HOME/.m2/
directory.- For Linux or Mac, this is
~/.m2/
- For Windows, this is
\Documents and Settings\USER_NAME\.m2\
or\Users\USER_NAME\.m2\
- If you do not find a
settings.xml
file, copy thesettings.xml
file from theUSER_HOME/.m2/conf/
directory into theUSER_HOME/.m2/
directory. - Copy the following XML into the
<profiles>
element of the file.<!-- Configure the JBoss GA Maven repository --> <profile> <id>jboss-ga-repository</id> <repositories> <repository> <id>jboss-ga-repository</id> <url>http://maven.repository.redhat.com/techpreview/all</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>jboss-ga-plugin-repository</id> <url>http://maven.repository.redhat.com/techpreview/all</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> <!-- Configure the JBoss Early Access Maven repository --> <profile> <id>jboss-earlyaccess-repository</id> <repositories> <repository> <id>jboss-earlyaccess-repository</id> <url>http://maven.repository.redhat.com/earlyaccess/all/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>jboss-earlyaccess-plugin-repository</id> <url>http://maven.repository.redhat.com/earlyaccess/all/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile>
Copy the following XML into the<activeProfiles>
element of thesettings.xml
file.<activeProfile>jboss-ga-repository</activeProfile> <activeProfile>jboss-earlyaccess-repository</activeProfile>
- If you modify the
settings.xml
file while Red Hat JBoss Developer Studio is running, follow the procedure below entitled Refresh the Red Hat JBoss Developer Studio User Settings.
Procedure 2.8. Configure the Settings to Use a Locally Installed JBoss EAP Repository
- Locate the Maven install directory for your operating system. It is usually installed in
USER_HOME/.m2/
directory.- For Linux or Mac, this is
~/.m2/
- For Windows, this is
\Documents and Settings\USER_NAME\.m2\
or\Users\USER_NAME\.m2\
- If you do not find a
settings.xml
file, copy thesettings.xml
file from theUSER_HOME/.m2/conf/
directory into theUSER_HOME/.m2/
directory. - Copy the following XML into the
<profiles>
element of thesettings.xml
file. Be sure to change the<url>
to the actual repository location.<profile> <id>jboss-eap-repository</id> <repositories> <repository> <id>jboss-eap-repository</id> <name>JBoss EAP Maven Repository</name> <url>file:///path/to/repo/jboss-eap-6.x-maven-repository</url> <layout>default</layout> <releases> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </releases> <snapshots> <enabled>false</enabled> <updatePolicy>never</updatePolicy> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>jboss-eap-repository-group</id> <name>JBoss EAP Maven Repository</name> <url> file:///path/to/repo/jboss-eap-6.x-maven-repository </url> <layout>default</layout> <releases> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </releases> <snapshots> <enabled>false</enabled> <updatePolicy>never</updatePolicy> </snapshots> </pluginRepository> </pluginRepositories> </profile>
Copy the following XML into the<activeProfiles>
element of thesettings.xml
file.<activeProfile>jboss-eap-repository</activeProfile>
- If you modify the
settings.xml
file while Red Hat JBoss Developer Studio is running, follow the procedure below entitled Refresh the Red Hat JBoss Developer Studio User Settings.
Procedure 2.9. Refresh the Red Hat JBoss Developer Studio User Settings
settings.xml
file while Red Hat JBoss Developer Studio is running, you must refresh the user settings.
- From the menu, choose→ .
- In the Preferences Window, expand Maven and choose User Settings.
- Click thebutton to refresh the Maven user settings in Red Hat JBoss Developer Studio.
Figure 2.1. Update Maven User Settings
Important
- Missing artifact ARTIFACT_NAME
- [ERROR] Failed to execute goal on project PROJECT_NAME; Could not resolve dependencies for PROJECT_NAME
~/.m2/repository/
subdirectory on Linux, or the %SystemDrive%\Users\USERNAME\.m2\repository\
subdirectory on Windows.
2.3.3. Configure Maven for Use with Red Hat JBoss Developer Studio
Procedure 2.10. Configure Maven in Red Hat JBoss Developer Studio
- Click JBoss Tools and select JBoss Maven Integration.→ , expand
Figure 2.2. JBoss Maven Integration Pane in the Preferences Window
- Click.
- Clickto configure the JBoss GA Tech Preview Maven repository. Complete the
Add Maven Repository
dialog as follows:- Set the Profile ID, Repository ID, and Repository Name values to
jboss-ga-repository
. - Set the Repository URL value to
http://maven.repository.redhat.com/techpreview/all
. - Click thecheckbox to enable the Maven repository.
- Click
Figure 2.3. Add Maven Repository - JBoss Tech Preview
- Clickto configure the JBoss Early Access Maven repository. Complete the
Add Maven Repository
dialog as follows:- Set the Profile ID, Repository ID, and Repository Name values to
jboss-earlyaccess-repository
. - Set the Repository URL value to
http://maven.repository.redhat.com/earlyaccess/all/
. - Click thecheckbox to enable the Maven repository.
- Click
Figure 2.4. Add Maven Repository - JBoss Early Access
- Review the repositories and click.
Figure 2.5. Review Maven Repositories
- You are prompted with the message "Are you sure you want to update the file 'MAVEN_HOME/settings.xml'?". Clickto update the settings. Click to close the dialog.The JBoss EAP Maven repository is now configured for use with Red Hat JBoss Developer Studio.
2.3.4. Configure the JBoss EAP 6 Maven Repository Using the Project POM
- You can modify the Maven settings.
- You can configure the project's POM file.
pom.xml
. This configuration method supercedes and overrides the global and user settings configurations.
Note
central
so that the default repository central is now your repository manager as well. This works well for established projects, but for clean or 'new' projects it causes a problem as it creates a cyclic dependency.
Note
- File System
- file:///path/to/repo/jboss-eap-6.x-maven-repository
- Apache Web Server
- http://intranet.acme.com/jboss-eap-6.x-maven-repository/
- Nexus Repository Manager
- https://intranet.acme.com/nexus/content/repositories/jboss-eap-6.x-maven-repository
- Open your project's
pom.xml
file in a text editor. - Add the following repository configuration. If there is already a
<repositories>
configuration in the file, then add the<repository>
element to it. Be sure to change the<url>
to the actual repository location.<repositories> <repository> <id>jboss-eap-repository-group</id> <name>JBoss EAP Maven Repository</name> <url>file:///path/to/repo/jboss-eap-6.3.0-maven-repository/</url> <layout>default</layout> <releases> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </snapshots> </repository> </repositories>
- Add the following plug-in repository configuration. If there is already a
<pluginRepositories>
configuration in the file, then add the<pluginRepository>
element to it.<pluginRepositories> <pluginRepository> <id>jboss-eap-repository-group</id> <name>JBoss EAP Maven Repository</name> <url>file:///path/to/repo/jboss-eap-6.3.0-maven-repository/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories>
2.3.5. Manage Project Dependencies
pom.xml
(POM) file that specifies the versions of all runtime dependencies for a given module. Version dependencies are listed in the dependency management section of the file.
groupId:artifactId:version
(GAV) to the dependency management section of the project pom.xml
file and specifying the <scope>import</scope>
and <type>pom</type>
element values.
Note
provided
scope. This is because these classes are provided by the application server at runtime and it is not necessary to package them with the user application.
Supported Maven Artifacts
-redhat
version qualifier, for example 1.0.0-redhat-1
.
pom.xml
file ensures that the build is using the correct binary artifact for local building and testing. Note that an artifact with a -redhat
version is not necessarily part of the supported public API, and may change in future revisions. For information about the public supported API, see the JavaDoc documentation included in the release.
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifact> <version>4.2.6.Final-redhat-1</version> <scope>provided</scope> </dependency>Notice that the above example includes a value for the
<version/>
field. However, it is recommended to use Maven dependency management for configuring dependency versions.
Dependency Management
<dependencyManagement> <dependencies> ... <dependency> <groupId>org.jboss.bom</groupId> <artifactId>eap6-supported-artifacts</artifactId> <version>6.3.0.GA</version> <type>pom</type> <scope>import</scope> </dependency> ... </dependencies> </dependencyManagement>
JBoss JavaEE Specs Bom
jboss-javaee-6.0
BOM contains the Java EE Specification API JARs used by JBoss EAP.
3.0.2.Final-redhat-x
version of the jboss-javaee-6.0
BOM.
<dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>3.0.2.Final-redhat-x</version> <type>pom</type> <scope>import</scope> </dependency> ... </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.spec.javax.servlet</groupId> <artifactId>jboss-servlet-api_3.0_spec</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.spec.javax.servlet.jsp</groupId> <artifactId>jboss-jsp-api_2.2_spec</artifactId> <scope>provided</scope> </dependency> ... </dependencies>
JBoss EAP BOMs and Quickstarts
Maven artifactId | Description |
---|---|
jboss-javaee-6.0-with-hibernate | This BOM builds on the Java EE full profile BOM, adding Hibernate Community projects including Hibernate ORM, Hibernate Search and Hibernate Validator. It also provides tool projects such as Hibernate JPA Model Gen and Hibernate Validator Annotation Processor. |
jboss-javaee-6.0-with-hibernate3 | This BOM builds on the Java EE full profile BOM, adding Hibernate Community projects including Hibernate 3 ORM, Hibernate Entity Manager (JPA 1.0) and Hibernate Validator. |
jboss-javaee-6.0-with-logging | This BOM builds on the Java EE full profile BOM, adding the JBoss Logging Tools and Log4j framework. |
jboss-javaee-6.0-with-osgi | This BOM builds on the Java EE full profile BOM, adding OSGI. |
jboss-javaee-6.0-with-resteasy | This BOM builds on the Java EE full profile BOM, adding RESTEasy |
jboss-javaee-6.0-with-security | This BOM builds on the Java EE full profile BOM, adding Picketlink. |
jboss-javaee-6.0-with-tools | This BOM builds on the Java EE full profile BOM, adding Arquillian to the mix. It also provides a version of JUnit and TestNG recommended for use with Arquillian. |
jboss-javaee-6.0-with-transactions | This BOM includes a world class transaction manager. Use the JBossTS APIs to access its full capabilities. |
6.3.0.GA
version of the jboss-javaee-6.0-with-hibernate
BOM.
<dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.bom.eap</groupId> <artifactId>jboss-javaee-6.0-with-hibernate</artifactId> <version>6.3.0.GA</version> <type>pom</type> <scope>import</scope> </dependency> ... </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <scope>provided</scope> </dependency> ... </dependencies>
JBoss Client BOMs
jboss-as-ejb-client-bom
and jboss-as-jms-client-bom
.
7.4.0.Final-redhat-x
version of the jboss-as-ejb-client-bom
client BOM.
<dependencies> <dependency> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-ejb-client-bom</artifactId> <version>7.4.0.Final-redhat-x</version> <type>pom</type> </dependency> ...l </dependencies>This example uses the
7.4.0.Final-redhat-x
version of the jboss-as-jms-client-bom
client BOM.
<dependencies> <dependency> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-jms-client-bom</artifactId> <version>7.4.0.Final-redhat-x</version> <type>pom</type> </dependency> ... </dependencies>
2.4. Upgrade the Maven Repository
2.4.1. Apply a Patch to the Local Maven Repository
A Maven repository stores Java libraries, plug-ins, and other artifacts required to build and deploy applications to JBoss EAP. The JBoss EAP repository is available online or as a downloaded ZIP file. If you use the publicly hosted repository, updates are applied automatically for you. However, if you download and install the Maven repository locally, you are responsible for applying any updates. Whenever a patch is available for JBoss EAP, a corresponding patch is provided for the JBoss EAP Maven repository. This patch is available in the form of an incremental ZIP file that is unzipped into the existing local repository. The ZIP file contains new JAR and POM files. It does not overwrite any existing JARs nor does it remove JARs, so there is no rollback requirement.
unzip
command.
Prerequisites
- Valid access and subscription to the Red Hat Customer Portal.
- The Red Hat JBoss Enterprise Application Platform 6.3.0 Maven Repository ZIP file, downloaded and installed locally.
Procedure 2.11. Update the Maven Repository
- Open a browser and log into https://access.redhat.com.
- Selectfrom the menu at the top of the page.
- Find
Red Hat JBoss Enterprise Application Platform
in the list and click on it. - Select the correct version of JBoss EAP from the Version drop-down menu that appears on this screen, then click on Patches.
- Find
Red Hat JBoss Enterprise Application Platform 6.3 CPx Incremental Maven Repository
in the list and click . - You are prompted to save the ZIP file to a directory of your choice. Choose a directory and save the file.
- Locate the path to JBoss EAP Maven repository, referred to in the commands below as EAP_MAVEN_REPOSITORY_PATH, for your operating system. For more information about how to install the Maven repository on the local file system, see Section 2.2.3, “Install the JBoss EAP 6 Maven Repository Locally”.
- Unzip the Maven patch file directly into the installation directory of the JBoss EAP 6.3.x Maven repository.
- For Linux, open a terminal and type the following command:
[standalone@localhost:9999 /]
unzip -o jboss-eap-6.3.x-incremental-maven-repository.zip -d
EAP_MAVEN_REPOSITORY_PATH
- For Windows, use the Windows extraction utility to extract the ZIP file into the root of the
EAP_MAVEN_REPOSITORY_PATH
directory.
The locally installed Maven repository is updated with the latest patch.
Chapter 3. Class Loading and Modules
3.1. Introduction
3.1.1. Overview of Class Loading and Modules
3.1.2. Class Loading
3.1.3. Modules
- Static Modules
- Static Modules are predefined in the
EAP_HOME/modules/
directory of the application server. Each sub-directory represents one module and defines amain/
subdirectory that contains a configuration file (module.xml
) and any required JAR files. The name of the module is defined in themodule.xml
file. All the application server provided APIs are provided as static modules, including the Java EE APIs as well as other APIs such as JBoss Logging.Example 3.1. Example module.xml file
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="urn:jboss:module:1.0" name="com.mysql"> <resources> <resource-root path="mysql-connector-java-5.1.15.jar"/> </resources> <dependencies> <module name="javax.api"/> <module name="javax.transaction.api"/> </dependencies> </module>
The module name,com.mysql
, should match the directory structure for the module, excluding themain/
subdirectory name.The modules provided in JBoss EAP distributions are located in asystem
directory within theJBOSS_HOME/modules
directory. This keeps them separate from any modules provided by third parties.Any Red Hat provided layered products that layer on top of JBoss EAP 6.1 or later will also install their modules within thesystem
directory.Creating custom static modules can be useful if many applications are deployed on the same server that use the same third party libraries. Instead of bundling those libraries with each application, a module containing these libraries can be created and installed by the JBoss administrator. The applications can then declare an explicit dependency on the custom static modules.Users must ensure that custom modules are installed into theJBOSS_HOME/modules
directory, using a one directory per module layout. This ensures that custom versions of modules that already exist in thesystem
directory are loaded instead of the shipped versions. In this way, user provided modules will take precedence over system modules.If you use theJBOSS_MODULEPATH
environment variable to change the locations in which JBoss EAP searches for modules, then the product will look for asystem
subdirectory structure within one of the locations specified. Asystem
structure must exist somewhere in the locations specified withJBOSS_MODULEPATH
. - Dynamic Modules
- Dynamic Modules are created and loaded by the application server for each JAR or WAR deployment (or subdeployment in an EAR). The name of a dynamic module is derived from the name of the deployed archive. Because deployments are loaded as modules, they can configure dependencies and be used as dependencies by other deployments.
3.1.4. Module Dependencies
Example 3.2. Module dependencies
- Module A declares an explicit dependency on Module C, or
- Module B exports its dependency on Module C.
3.1.5. Class Loading in Deployments
- WAR Deployment
- A WAR deployment is considered to be a single module. Classes in the
WEB-INF/lib
directory are treated the same as classes inWEB-INF/classes
directory. All classes packaged in the war will be loaded with the same class loader. - EAR Deployment
- EAR deployments are made up more than one module. The definition of these modules follows these rules:
- The
lib/
directory of the EAR is a single module called the parent module. - Each WAR deployment within the EAR is a single module.
- Each EJB JAR deployment within the EAR is a single module.
Subdeployment modules (the WAR and JAR deployments within the EAR) have an automatic dependency on the parent module. However they do not have automatic dependencies on each other. This is called subdeployment isolation and can be disabled on a per deployment basis or for the entire application server.Explicit dependencies between subdeployment modules can be added by the same means as any other module.
3.1.6. Class Loading Precedence
- Implicit dependencies.These are the dependencies that are added automatically by JBoss EAP 6, such as the JAVA EE APIs. These dependencies have the highest class loader precedence because they contain common functionality and APIs that are supplied by JBoss EAP 6.Refer to Section 3.8.1, “Implicit Module Dependencies” for complete details about each implicit dependency.
- Explicit dependencies.These are dependencies that are manually added in the application configuration. This can be done using the application's
MANIFEST.MF
file or the new optional JBoss deployment descriptorjboss-deployment-structure.xml
file.Refer to Section 3.2, “Add an Explicit Module Dependency to a Deployment” to learn how to add explicit dependencies. - Local resources.Class files packaged up inside the deployment itself, e.g. from the
WEB-INF/classes
orWEB-INF/lib
directories of a WAR file. - Inter-deployment dependencies.These are dependencies on other deployments in a EAR deployment. This can include classes in the
lib
directory of the EAR or classes defined in other EJB jars.
3.1.7. Dynamic Module Naming
- Deployments of WAR and JAR files are named with the following format:
deployment.DEPLOYMENT_NAME
For example,inventory.war
andstore.jar
will have the module names ofdeployment.inventory.war
anddeployment.store.jar
respectively. - Subdeployments within an Enterprise Archive are named with the following format:
deployment.EAR_NAME.SUBDEPLOYMENT_NAME
For example, the subdeployment ofreports.war
within the enterprise archiveaccounts.ear
will have the module name ofdeployment.accounts.ear.reports.war
.
3.1.8. jboss-deployment-structure.xml
jboss-deployment-structure.xml
is a new optional deployment descriptor for JBoss EAP 6. This deployment descriptor provides control over class loading in the deployment.
EAP_HOME/docs/schema/jboss-deployment-structure-1_2.xsd
3.2. Add an Explicit Module Dependency to a Deployment
Prerequisites
- You must already have a working software project that you want to add a module dependency to.
- You must know the name of the module being added as a dependency. See Section 3.8.2, “Included Modules” for the list of static modules included with JBoss EAP 6. If the module is another deployment then see Section 3.1.7, “Dynamic Module Naming” to determine the module name.
- Adding entries to the
MANIFEST.MF
file of the deployment. - Adding entries to the
jboss-deployment-structure.xml
deployment descriptor.
Procedure 3.1. Add dependency configuration to MANIFEST.MF
MANIFEST.MF
file. See Section 3.3, “Generate MANIFEST.MF entries using Maven”.
Add
MANIFEST.MF
fileIf the project has noMANIFEST.MF
file, create a file calledMANIFEST.MF
. For a web application (WAR) add this file to theMETA-INF
directory. For an EJB archive (JAR) add it to theMETA-INF
directory.Add dependencies entry
Add a dependencies entry to theMANIFEST.MF
file with a comma-separated list of dependency module names.Dependencies: org.javassist, org.apache.velocity
Optional: Make a dependency optional
A dependency can be made optional by appendingoptional
to the module name in the dependency entry.Dependencies: org.javassist optional, org.apache.velocity
Optional: Export a dependency
A dependency can be exported by appendingexport
to the module name in the dependency entry.Dependencies: org.javassist, org.apache.velocity export
Procedure 3.2. Add dependency configuration to jboss-deployment-structure.xml
Add
jboss-deployment-structure.xml
If the application has nojboss-deployment-structure.xml
file then create a new file calledjboss-deployment-structure.xml
and add it to the project. This file is an XML file with the root element of<jboss-deployment-structure>
.<jboss-deployment-structure> </jboss-deployment-structure>
For a web application (WAR) add this file to theWEB-INF
directory. For an EJB archive (JAR) add it to theMETA-INF
directory.Add dependencies section
Create a<deployment>
element within the document root and a<dependencies>
element within that.Add module elements
Within the dependencies node, add a module element for each module dependency. Set thename
attribute to the name of the module.<module name="org.javassist" />
Optional: Make a dependency optional
A dependency can be made optional by adding theoptional
attribute to the module entry with the value oftrue
. The default value for this attribute isfalse
.<module name="org.javassist" optional="true" />
Optional: Export a dependency
A dependency can be exported by adding theexport
attribute to the module entry with the value oftrue
. The default value for this attribute isfalse
.<module name="org.javassist" export="true" />
Example 3.3. jboss-deployment-structure.xml with two dependencies
<jboss-deployment-structure> <deployment> <dependencies> <module name="org.javassist" /> <module name="org.apache.velocity" export="true" /> </dependencies> </deployment> </jboss-deployment-structure>
3.3. Generate MANIFEST.MF entries using Maven
MANIFEST.MF
file with a Dependencies
entry. This does not automatically generate the list of dependencies, this process only creates the MANIFEST.MF
file with the details specified in the pom.xml
.
Prerequisites
- You must already have a working Maven project.
- The Maven project must be using one of the JAR, EJB, or WAR plug-ins (
maven-jar-plugin
,maven-ejb-plugin
,maven-war-plugin
). - You must know the name of the project's module dependencies. Refer to Section 3.8.2, “Included Modules” for the list of static modules included with JBoss EAP 6. If the module is another deployment , then refer to Section 3.1.7, “Dynamic Module Naming” to determine the module name.
Procedure 3.3. Generate a MANIFEST.MF file containing module dependencies
Add Configuration
Add the following configuration to the packaging plug-in configuration in the project'spom.xml
file.<configuration> <archive> <manifestEntries> <Dependencies></Dependencies> </manifestEntries> </archive> </configuration>
List Dependencies
Add the list of the module dependencies in the <Dependencies> element. Use the same format that is used when adding the dependencies to theMANIFEST.MF
. Refer to Section 3.2, “Add an Explicit Module Dependency to a Deployment” for details about that format.<Dependencies>org.javassist, org.apache.velocity</Dependencies>
Build the Project
Build the project using the Maven assembly goal.[Localhost ]$ mvn assembly:assembly
MANIFEST.MF
file with the specified module dependencies.
Example 3.4. Configured Module Dependencies in pom.xml
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <archive> <manifestEntries> <Dependencies>org.javassist, org.apache.velocity</Dependencies> </manifestEntries> </archive> </configuration> </plugin> </plugins>
3.4. Prevent a Module Being Implicitly Loaded
Prerequisites
- You must already have a working software project that you want to exclude an implicit dependency from.
- You must know the name of the module to exclude. Refer to Section 3.8.1, “Implicit Module Dependencies” for a list of implicit dependencies and their conditions.
Procedure 3.4. Add dependency exclusion configuration to jboss-deployment-structure.xml
- If the application has no
jboss-deployment-structure.xml
file, create a new file calledjboss-deployment-structure.xml
and add it to the project. This file is an XML file with the root element of<jboss-deployment-structure>
.<jboss-deployment-structure> </jboss-deployment-structure>
For a web application (WAR) add this file to theWEB-INF
directory. For an EJB archive (JAR) add it to theMETA-INF
directory. - Create a
<deployment>
element within the document root and an<exclusions>
element within that.<deployment> <exclusions> </exclusions> </deployment>
- Within the exclusions element, add a
<module>
element for each module to be excluded. Set thename
attribute to the name of the module.<module name="org.javassist" />
Example 3.5. Excluding two modules
<jboss-deployment-structure> <deployment> <exclusions> <module name="org.javassist" /> <module name="org.dom4j" /> </exclusions> </deployment> </jboss-deployment-structure>
3.5. Exclude a Subsystem from a Deployment
This topic covers the steps required to exclude a subsystem from a deployment. This is done by editing the jboss-deployment-structure.xml
configuration file. Excluding a subsystem provides the same effect as removing the subsystem, but it applies only to a single deployment.
Procedure 3.5. Exclude a Subsystem
- Open the
jboss-deployment-structure.xml
file in a text editor. - Add the following XML inside the <deployment> tags:
<exclude-subsystems> <subsystem name="SUBSYSTEM_NAME" /> </exclude-subsystems>
- Save the
jboss-deployment-structure.xml
file.
The subsystem has been successfully excluded. The subsystem's deployment unit processors will no longer run on the deployment.
Example 3.6. Example jboss-deployment-structure.xml
file.
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2"> <ear-subdeployments-isolated>true</ear-subdeployments-isolated> <deployment> <exclude-subsystems> <subsystem name="resteasy" /> </exclude-subsystems> <exclusions> <module name="org.javassist" /> </exclusions> <dependencies> <module name="deployment.javassist.proxy" /> <module name="deployment.myjavassist" /> <module name="myservicemodule" services="import"/> </dependencies> <resources> <resource-root path="my-library.jar" /> </resources> </deployment> <sub-deployment name="myapp.war"> <dependencies> <module name="deployment.myear.ear.myejbjar.jar" /> </dependencies> <local-last value="true" /> </sub-deployment> <module name="deployment.myjavassist" > <resources> <resource-root path="javassist.jar" > <filter> <exclude path="javassist/util/proxy" /> </filter> </resource-root> </resources> </module> <module name="deployment.javassist.proxy" > <dependencies> <module name="org.javassist" > <imports> <include path="javassist/util/proxy" /> <exclude path="/**" /> </imports> </module> </dependencies> </module> </jboss-deployment-structure>
3.6. Use the Class Loader Programmatically in a Deployment
3.6.1. Programmatically Load Classes and Resources in a Deployment
- Load a Class Using the Class.forName() Method
- You can use the
Class.forName()
method to programmatically load and initialize classes. This method has two signatures.The three argument signature is the recommended way to programmatically load a class. This signature allows you to control whether you want the target class to be initialized upon load. It is also more efficient to obtain and provide the class loader because the JVM does not need to examine the call stack to determine which class loader to use. Assuming the class containing the code is named- Class.forName(String className)
- This signature takes only one parameter, the name of the class you need to load. With this method signature, the class is loaded by the class loader of the current class and initializes the newly loaded class by default.
- Class.forName(String className, boolean initialize, ClassLoader loader)
- This signature expects three parameters: the class name, a boolean value that specifies whether to initialize the class, and the ClassLoader that should load the class.
CurrentClass
, you can obtain the class's class loader usingCurrentClass.class.getClassLoader()
method.The following example provides the class loader to load and initialize theTargetClass
class:Example 3.7. Provide a class loader to load and initialize the TargetClass.
Class<?> targetClass = Class.forName("com.myorg.util.TargetClass", true, CurrentClass.class.getClassLoader());
- Find All Resources with a Given Name
- If you know the name and path of a resource, the best way to load it directly is to use the standard JDK Class or ClassLoader API.
- Load a Single Resource
- To load a single resource located in the same directory as your class or another class in your deployment, you can use the
Class.getResourceAsStream()
method.Example 3.8. Load a single resource in your deployment.
InputStream inputStream = CurrentClass.class.getResourceAsStream("targetResourceName");
- Load All Instances of a Single Resource
- To load all instances of a single resource that are visible to your deployment's class loader, use the
Class.getClassLoader().getResources(String resourceName)
method, whereresourceName
is the fully qualified path of the resource. This method returns an Enumeration of allURL
objects for resources accessible by the class loader with the given name. You can then iterate through the array of URLs to open each stream using theopenStream()
method.Example 3.9. Load all instances of a resource and iterate through the result.
Enumeration<URL> urls = CurrentClass.class.getClassLoader().getResources("full/path/to/resource"); while (urls.hasMoreElements()) { URL url = urls.nextElement(); InputStream inputStream = null; try { inputStream = url.openStream(); // Process the inputStream ... } catch(IOException ioException) { // Handle the error } finally { if (inputStream != null) { try { inputStream.close(); } catch (Exception e) { // ignore } } } }
Note
Because the URL instances are loaded from local storage, it is not necessary to use theopenConnection()
or other related methods. Streams are much simpler to use and minimize the complexity of the code.
- Load a Class File From the Class Loader
- If a class has already been loaded, you can load the class file that corresponds to that class using the following syntax:If the class is not yet loaded, you must use the class loader and translate the path:
Example 3.10. Load a class file for a class that has been loaded.
InputStream inputStream = CurrentClass.class.getResourceAsStream(TargetClass.class.getSimpleName() + ".class");
Example 3.11. Load a class file for a class that has not been loaded.
String className = "com.myorg.util.TargetClass" InputStream inputStream = CurrentClass.class.getClassLoader().getResourceAsStream(className.replace('.', '/') + ".class");
3.6.2. Programmatically Iterate Resources in a Deployment
MANIFEST.MF
:
Dependencies: org.jboss.modulesIt is important to note that while these APIs provide increased flexibility, they will also run much more slowly than a direct path lookup.
- List Resources Within a Deployment and Within All Imports
- There are times when it is not possible to look up resources by the exact path. For example, the exact path may not be known or you may need to examine more than one file in a given path. In this case, the JBoss Modules library provides several APIs for iterating all deployment resources. You can iterate through resources in a deployment by utilizing one of two methods.
- Iterate All Resources Found in a Single Module
- The
ModuleClassLoader.iterateResources()
method iterates all the resources within this module class loader. This method takes two arguments: the starting directory name to search and a boolean that specifies whether it should recurse into subdirectories.The following example demonstrates how to obtain the ModuleClassLoader and obtain the iterator for resources in thebin/
directory, recursing into subdirectories.The resultant iterator may be used to examine each matching resource and query its name and size (if available), open a readable stream, or acquire a URL for the resource.Example 3.12. Find resources in the "bin" directory, recursing into subdirectories.
ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader(); Iterator<Resource> mclResources = moduleClassLoader.iterateResources("bin",true);
- Iterate All Resources Found in a Single Module and Imported Resources
- The
Module.iterateResources()
method iterates all the resources within this module class loader, including the resources that are imported into the module. This method returns a much larger set than the previous method. This method requires an argument, which is a filter that narrows the result to a specific pattern. Alternatively, PathFilters.acceptAll() can be supplied to return the entire set.Example 3.13. Find the entire set of resources in this module, including imports.
ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader(); Module module = moduleClassLoader.getModule(); Iterator<Resource> moduleResources = module.iterateResources(PathFilters.acceptAll());
- Find All Resources That Match a Pattern
- If you need to find only specific resources within your deployment or within your deployment's full import set, you need to filter the resource iteration. The JBoss Modules filtering APIs give you several tools to accomplish this.
- Examine the Full Set of Dependencies
- If you need to examine the full set of dependencies, you can use the
Module.iterateResources()
method'sPathFilter
parameter to check the name of each resource for a match. - Examine Deployment Dependencies
- If you need to look only within the deployment, use the
ModuleClassLoader.iterateResources()
method. However, you must use additional methods to filter the resultant iterator. ThePathFilters.filtered()
method can provide a filtered view of a resource iterator this case. ThePathFilters
class includes many static methods to create and compose filters that perform various functions, including finding child paths or exact matches, or matching an Ant-style "glob" pattern.
- Additional Code Examples For Filtering Resouces
- The following examples demonstrate how to filter resources based on different criteria.
Example 3.14. Find all files named "messages.properties" in your deployment.
ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader(); Iterator<Resource> mclResources = PathFilters.filtered(PathFilters.match("**/messages.properties"), moduleClassLoader.iterateResources("", true));
Example 3.15. Find all files named "messages.properties" in your deployment and imports.
ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader(); Module module = moduleClassLoader.getModule(); Iterator<Resource> moduleResources = module.iterateResources(PathFilters.match("**/message.properties));
Example 3.16. Find all files inside any directory named "my-resources" in your deployment.
ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader(); Iterator<Resource> mclResources = PathFilters.filtered(PathFilters.match("**/my-resources/**"), moduleClassLoader.iterateResources("", true));
Example 3.17. Find all files named "messages" or "errors" in your deployment and imports.
ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader(); Module module = moduleClassLoader.getModule(); Iterator<Resource> moduleResources = module.iterateResources(PathFilters.any(PathFilters.match("**/messages"), PathFilters.match("**/errors"));
Example 3.18. Find all files in a specific package in your deployment.
ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader(); Iterator<Resource> mclResources = moduleClassLoader.iterateResources("path/form/of/packagename", false);
3.7. Class Loading and Subdeployments
3.7.1. Modules and Class Loading in Enterprise Archives
- Each WAR and EJB JAR subdeployment is a module.
- The contents of the
lib/
directory in the root of the EAR archive is a module. This is called the parent module.
- WAR subdeployments have implicit dependencies on the parent module and any EJB JAR subdeployments.
- EJB JAR subdeployments have implicit dependencies on the parent module and any other EJB JAR subdeployments.
Important
Class-Path
entries in the MANIFEST.MF
file of each subdeployment.
3.7.2. Subdeployment Class Loader Isolation
3.7.3. Disable Subdeployment Class Loader Isolation Within a EAR
Important
Add the deployment descriptor file
Add thejboss-deployment-structure.xml
deployment descriptor file to theMETA-INF
directory of the EAR if it doesn't already exist and add the following content:<jboss-deployment-structure> </jboss-deployment-structure>
Add the
<ear-subdeployments-isolated>
elementAdd the<ear-subdeployments-isolated>
element to thejboss-deployment-structure.xml
file if it doesn't already exist with the content offalse
.<ear-subdeployments-isolated>false</ear-subdeployments-isolated>
Subdeployment class loader isolation will now be disabled for this EAR deployment. This means that the subdeployments of the EAR will have automatic dependencies on each of the non-WAR subdeployments.
3.8. Reference
3.8.1. Implicit Module Dependencies
Subsystem Responsible for Adding the Dependency | Dependencies That Are Always Added | Dependencies That Are Conditionally Added | Conditions That Trigger the Addition of the Dependency |
---|---|---|---|
Core Server |
| | |
EE subsystem |
| | |
EJB 3 subsystem | |
|
The presence of an
ejb-jar.xml file within a valid location in the deployment, as described in the Java EE 6 specification.
The presence of annotation-based EJBs, for example:
@Stateless , @Stateful , @MessageDriven
|
JAX-RS (RESTEasy) subsystem |
|
| The presence of JAX-RS annotations in the deployment. |
JCA subsystem |
|
| The deployment of a resource adapter (RAR) archive. |
JPA (Hibernate) subsystem |
|
|
The presence of an
@PersistenceUnit or @PersistenceContext annotation, or a <persistence-unit-ref> or <persistence-context-ref> element in a deployment descriptor.
JBoss EAP 6 maps persistence provider names to module names. If you name a specific provider in the
persistence.xml file, a dependency is added for the appropriate module. If this not the desired behavior, you can exclude it using a jboss-deployment-structure.xml file.
|
Logging subsystem |
| |
These dependencies are always added unless the
add-logging-api-dependencies attribute is set to false.
|
SAR subsystem | |
| The deployment of a SAR archive. |
Security subsystem |
| | |
Web subsystem | |
| The deployment of a WAR archive. JavaServer Faces (JSF) is added only if it is used. |
Web Services subsystem |
| | |
Weld (CDI) Subsystem | |
| The presence of a beans.xml file in the deployment. |
3.8.2. Included Modules
3.8.3. JBoss Deployment Structure Deployment Descriptor Reference
- Defining explicit module dependencies.
- Preventing specific implicit dependencies from loading.
- Defining additional modules from the resources of that deployment.
- Changing the subdeployment isolation behavior in that EAR deployment.
- Adding additional resource roots to a module in an EAR.
Chapter 4. Valves
4.1. About Valves
- Global Valves are configured at the server level and apply to all applications deployed to the server. Instructions to configure Global Valves are located in the Administration and Configuration Guide for JBoss EAP.
- Valves configured at the application level are packaged with the application deployment and only affect the specific application. Instructions to configure Valves at the application level are located in the Development Guide for JBoss EAP.
4.2. About Global Valves
4.3. About Authenticator Valves
org.apache.catalina.authenticator.AuthenticatorBase
and overrides the authenticate(Request request, Response response, LoginConfig config)
method.
4.4. Configure a Web Application to use a Valve
jboss-web.xml
deployment descriptor.
Important
Prerequisites
- The valve must be created and included in your application's classpath. This can be done by either including it in the application's WAR file or any module that is added as a dependency. Examples of such modules include a static module installed on the server or a JAR file in the
lib/
directory of an EAR archive if the WAR is deployed in an EAR. - The application must include a
jboss-web.xml
deployment descriptor.
Procedure 4.1. Configure an application for a local valve
Configure a Valve
Create avalve
element containing theclass-name
child element in the application'sjboss-web.xml
file. Theclass-name
is the name of the valve class.<valve> <class-name>VALVE_CLASS_NAME</class-name> </valve>
Example 4.1. Example of a valve element configured in the jboss-web.xml file
<valve> <class-name>org.jboss.security.negotiation.NegotiationAuthenticator</class-name> </valve>
Configure a Custom Valve
If the valve has configurable parameters, add aparam
child element to thevalve
element for each parameter, specifying theparam-name
andparam-value
for each.Example 4.2. Example of a custom valve element configured in the jboss-web.xml file
<valve> <class-name>org.jboss.web.tomcat.security.GenericHeaderAuthenticator</class-name> <param> <param-name>httpHeaderForSSOAuth</param-name> <param-value>sm_ssoid,ct-remote-user,HTTP_OBLIX_UID</param-value> </param> <param> <param-name>sessionCookieForSSOAuth</param-name> <param-value>SMSESSION,CTSESSION,ObSSOCookie</param-value> </param> </valve>
Example 4.3. jboss-web.xml valve configuration
<valve> <class-name>org.jboss.samplevalves.RestrictedUserAgentsValve</class-name> <param> <param-name>restrictedUserAgents</param-name> <param-value>^.*MS Web Services Client Protocol.*$</param-value> </param> </valve>
4.5. Configure a Web Application to use an Authenticator Valve
web.xml
deployment descriptor of the application to be configured. In the simplest case, the web.xml
configuration is the same as using BASIC
authentication except the auth-method
child element of login-config
is set to the name of the valve performing the configuration.
Prerequisites
- Authentication valve must already be created.
- If the authentication valve is a global valve then it must already be installed and configured, and you must know the name that it was configured as.
- You need to know the realm name of the security realm that the application will use.
Procedure 4.2. Configure an Application to use an Authenticator Valve
Configure the valve
When using a local valve, it must be configured in the applicationsjboss-web.xml
deployment descriptor. Refer to Section 4.4, “Configure a Web Application to use a Valve”.When using a global valve, this is unnecessary.Add security configuration to web.xml
Add the security configuration to the web.xml file for your application, using the standard elements such as security-constraint, login-config, and security-role. In the login-config element, set the value of auth-method to the name of the authenticator valve. The realm-name element also needs to be set to the name of the JBoss security realm being used by the application.<login-config> <auth-method>VALVE_NAME</auth-method> <realm-name>REALM_NAME</realm-name> </login-config>
4.6. Create a Custom Valve
Procedure 4.3. Create a Custom Valve
Create the Valve class
Create a subclass oforg.apache.catalina.valves.ValveBase
.package org.jboss.samplevalves; import org.apache.catalina.valves.ValveBase; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; public class RestrictedUserAgentsValve extends ValveBase { }
Implement the invoke method
Theinvoke()
method is called when this valve is executed in the pipeline. The request and response objects are passed as parameters. Perform any processing and modification of the request and response here.public void invoke(Request request, Response response) { }
Invoke the next pipeline step
The last thing the invoke method must do is invoke the next step of the pipeline and pass the modified request and response objects along. This is done using thegetNext().invoke()
methodgetNext().invoke(request, response);
Optional: Specify parameters
If the valve must be configurable, enable this by adding a parameter. Do this by adding an instance variable and a setter method for each parameter.private String restrictedUserAgents = null; public void setRestricteduserAgents(String mystring) { this.restrictedUserAgents = mystring; }
Example 4.4. Sample Custom Valve
package org.jboss.samplevalves; import java.io.IOException; import java.util.regex.Pattern; import javax.servlet.ServletException; import org.apache.catalina.valves.ValveBase; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; public class RestrictedUserAgentsValve extends ValveBase { private String restrictedUserAgents = null; public void setRestrictedUserAgents(String mystring) { this.restrictedUserAgents = mystring; } public void invoke(Request request, Response response) throws IOException, ServletException { String agent = request.getHeader("User-Agent"); System.out.println("user-agent: " + agent + " : " + restrictedUserAgents); if (Pattern.matches(restrictedUserAgents, agent)) { System.out.println("user-agent: " + agent + " matches: " + restrictedUserAgents); response.addHeader("Connection", "close"); } getNext().invoke(request, response); } }
Chapter 5. Logging for Developers
5.1. Introduction
5.1.1. About Logging
5.1.2. Application Logging Frameworks Supported By JBoss LogManager
- JBoss Logging - included with JBoss EAP 6
- Apache Commons Logging - http://commons.apache.org/logging/
- Simple Logging Facade for Java (SLF4J) - http://www.slf4j.org/
- Apache log4j - http://logging.apache.org/log4j/1.2/
- Java SE Logging (java.util.logging) - http://download.oracle.com/javase/6/docs/api/java/util/logging/package-summary.html
5.1.3. About Log Levels
TRACE
, DEBUG
, INFO
, WARN
, ERROR
and FATAL
.
WARN
will only record messages of the levels WARN
, ERROR
and FATAL
.
5.1.4. Supported Log Levels
Log Level | Value | Description |
---|---|---|
FINEST | 300 |
-
|
FINER | 400 |
-
|
TRACE | 400 |
Use for messages that provide detailed information about the running state of an application. Log messages of
TRACE are usually only captured when debugging an application.
|
DEBUG | 500 |
Use for messages that indicate the progress individual requests or activities of an application. Log messages of
DEBUG are usually only captured when debugging an application.
|
FINE | 500 |
-
|
CONFIG | 700 |
-
|
INFO | 800 |
Use for messages that indicate the overall progress of the application. Often used for application startup, shutdown and other major lifecycle events.
|
WARN | 900 |
Use to indicate a situation that is not in error but is not considered ideal. May indicate circumstances that may lead to errors in the future.
|
WARNING | 900 |
-
|
ERROR | 1000 |
Use to indicate an error that has occurred that could prevent the current activity or request from completing but will not prevent the application from running.
|
SEVERE | 1000 |
-
|
FATAL | 1100 |
Use to indicate events that could cause critical service failure and application shutdown and possibly cause JBoss EAP 6 to shutdown.
|
5.1.5. Default Log File Locations
Log File | Description |
---|---|
EAP_HOME/standalone/log/server.log |
Server Log. Contains all server log messages, including server startup messages.
|
EAP_HOME/standalone/log/gc.log |
Garbage collection log. Contains details of all garbage collection.
|
Log File | Description |
---|---|
EAP_HOME/domain/log/host-controller.log |
Host Controller boot log. Contains log messages related to the startup of the host controller.
|
EAP_HOME/domain/log/process-controller.log |
Process controller boot log. Contains log messages related to the startup of the process controller.
|
EAP_HOME/domain/servers/SERVERNAME/log/server.log |
The server log for the named server. Contains all log messages for that server, including server startup messages.
|
5.2. Logging with the JBoss Logging Framework
5.2.1. About JBoss Logging
5.2.2. Features of JBoss Logging
- Provides an innovative, easy to use "typed" logger.
- Full support for internationalization and localization. Translators work with message bundles in properties files while developers can work with interfaces and annotations.
- Build-time tooling to generate typed loggers for production, and runtime generation of typed loggers for development.
5.2.3. Add Logging to an Application with JBoss Logging
org.jboss.logging.Logger
) and call the appropriate methods of that object. This task describes the steps required to add support for this to your application.
Prerequisites
- If you are using Maven as your build system, the project must already be configured to include the JBoss Maven Repository. Refer to Section 2.3.2, “Configure the JBoss EAP 6 Maven Repository Using the Maven Settings”
- The JBoss Logging JAR files must be in the build path for your application. How you do this depends on whether you build your application using Red Hat JBoss Developer Studio or with Maven.
- When building using Red Hat JBoss Developer Studio this can be done selecting Project -> Properties from the Red Hat JBoss Developer Studio menu, selecting Targeted Runtimes and ensuring the runtime for JBoss EAP 6 is checked.
- When building using Maven this can be done by adding the following dependency configuration to your project's
pom.xml
file.<dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> <version>3.1.2.GA-redhat-1</version> <scope>provided</scope> </dependency>
You do not need to include the JARs in your built application because JBoss EAP 6 provides them to deployed applications.
Add imports
Add the import statements for the JBoss Logging class namespaces that you will be using. At a minimum you will need to importimport org.jboss.logging.Logger
.import org.jboss.logging.Logger;
Create a Logger object
Create an instance oforg.jboss.logging.Logger
and initialize it by calling the static methodLogger.getLogger(Class)
. Red Hat recommends creating this as a single instance variable for each class.private static final Logger LOGGER = Logger.getLogger(HelloWorld.class);
Add logging messages
Add calls to the methods of theLogger
object to your code where you want it to send log messages. TheLogger
object has many different methods with different parameters for different types of messages. The easiest to use are:debug(Object message)
info(Object message)
error(Object message)
trace(Object message)
fatal(Object message)
These methods send a log message with the corresponding log level and themessage
parameter as a string.LOGGER.error("Configuration file not found.");
For the complete list of JBoss Logging methods refer to theorg.jboss.logging
package in the JBoss EAP 6 API Documentation.
Example 5.1. Using JBoss Logging when opening a properties file
import org.jboss.logging.Logger; public class LocalSystemConfig { private static final Logger LOGGER = Logger.getLogger(LocalSystemConfig.class); public Properties openCustomProperties(String configname) throws CustomConfigFileNotFoundException { Properties props = new Properties(); try { LOGGER.info("Loading custom configuration from "+configname); props.load(new FileInputStream(configname)); } catch(IOException e) //catch exception in case properties file does not exist { LOGGER.error("Custom configuration file ("+configname+") not found. Using defaults."); throw new CustomConfigFileNotFoundException(configname); } return props; }
5.3. Per-deployment Logging
5.3.1. About Per-deployment Logging
5.3.2. Add Per-deployment Logging to an Application
logging.properties
into the deployment. This configuration file is recommended because it can be used with any logging facade as the JBoss Log Manager is the underlying log manager used.
Simple Logging Facade for Java (SLF4J)
or Apache log4j
, the logging.properties
configuration file is suitable. If you are using Apache log4j appenders then the configuration file log4j.properties
is required. The configuration file jboss-logging.properties
is supported only for legacy deployments.
Procedure 5.1. Add Configuration File to the Application
The directory into which the configuration file is added depends on the deployment method:
EAR
,WAR
orJAR
.EAR
deploymentCopy the logging configuration file to theMETA-INF
directory.WAR
orJAR
deploymentCopy the logging configuration file to either theMETA-INF
orWEB-INF/classes
directory.
5.3.3. Example logging.properties File
# Additional loggers to configure (the root logger is always configured) loggers= # Root logger configuration logger.level=INFO logger.handlers=FILE # A handler configuration handler.FILE=org.jboss.logmanager.handlers.FileHandler handler.FILE.level=ALL handler.FILE.formatter=PATTERN handler.FILE.properties=append,autoFlush,enabled,suffix,fileName handler.FILE.constructorProperties=fileName,append handler.FILE.append=true handler.FILE.autoFlush=true handler.FILE.enabled=true handler.FILE.fileName=${jboss.server.log.dir}/app.log # The formatter to use formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter formatter.PATTERN.properties=pattern formatter.PATTERN.constructorProperties=pattern formatter.PATTERN.pattern=%d %-5p %c: %m%n
5.4. Logging Profiles
5.4.1. About Logging Profiles
Important
- A unique name. This is required.
- Any number of log handlers.
- Any number of log categories.
- Up to one root logger.
MANIFEST.MF
file, using the logging-profile
attribute.
5.4.2. Specify a Logging Profile in an Application
MANIFEST.MF
file.
Prerequisites:
- You must know the name of the logging profile that has been setup on the server for this application to use. Ask your server administrator for the name of the profile to use.
Procedure 5.2. Add Logging Profile configuration to an Application
Edit
MANIFEST.MF
If your application does not have aMANIFEST.MF
file: create one with the following content, replacing NAME with the required profile name.Manifest-Version: 1.0 Logging-Profile: NAME
If your application already has aMANIFEST.MF
file: add the following line to it, replacing NAME with the required profile name.Logging-Profile: NAME
Note
maven-war-plugin
, you can put your MANIFEST.MF file in src/main/resources/META-INF/
and add the following configuration to your pom.xml
file.
<plugin> <artifactId>maven-war-plugin</artifactId> <configuration> <archive> <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile> </archive> </configuration> </plugin>
Chapter 6. Internationalization and Localization
6.1. Introduction
6.1.1. About Internationalization
6.1.2. About Localization
6.2. JBoss Logging Tools
6.2.1. Overview
6.2.1.1. JBoss Logging Tools Internationalization and Localization
org.jboss.logging
annotations. It is not necessary to implement the interfaces, JBoss Logging Tools does this at compile time. Once defined you can use these methods to log messages or obtain exception objects in your code.
6.2.1.2. JBoss Logging Tools Quickstart
logging-tools
, contains a simple Maven project that demonstrates the features of JBoss Logging Tools. It has been used extensively in this documentation for code samples.
6.2.1.3. Message Logger
@org.jboss.logging.MessageLogger
.
6.2.1.4. Message Bundle
@org.jboss.logging.MessageBundle
.
6.2.1.5. Internationalized Log Messages
@LogMessage
and @Message
annotations and specify the log message using the value attribute of @Message
. Internationalized log messages are localized by providing translations in a properties file.
6.2.1.6. Internationalized Exceptions
6.2.1.7. Internationalized Messages
6.2.1.8. Translation Properties Files
6.2.1.9. JBoss Logging Tools Project Codes
projectCode
attribute of the @MessageLogger
annotation.
6.2.1.10. JBoss Logging Tools Message Ids
id
attribute of the @Message
annotation.
6.2.2. Creating Internationalized Loggers, Messages and Exceptions
6.2.2.1. Create Internationalized Log Messages
logging-tools
quick start for a complete example.
Prerequisites:
- You must already have a working Maven project. Refer to Section 6.2.6.1, “JBoss Logging Tools Maven Configuration”.
- The project must have the required maven configuration for JBoss Logging Tools.
Procedure 6.1. Create an Internationalized Log Message Bundle
Create an Message Logger interface
Add a Java interface to your project to contain the log message definitions. Name the interface descriptively for the log messages that will be defined in it.The log message interface has the following requirements:- It must be annotated with
@org.jboss.logging.MessageLogger
. - It must extend
org.jboss.logging.BasicLogger
. - The interface must define a field of that is a typed logger that implements this interface. Do this with the
getMessageLogger()
method oforg.jboss.logging.Logger
.
package com.company.accounts.loggers; import org.jboss.logging.BasicLogger; import org.jboss.logging.Logger; import org.jboss.logging.MessageLogger; @MessageLogger(projectCode="") interface AccountsLogger extends BasicLogger { AccountsLogger LOGGER = Logger.getMessageLogger( AccountsLogger.class, AccountsLogger.class.getPackage().getName() ); }
Add method definitions
Add a method definition to the interface for each log message. Name each method descriptively for the log message that it represents.Each method has the following requirements:- The method must return
void
. - It must be annotated with the
@org.jboss.logging.LogMessage
annotation. - It must be annotated with the
@org.jboss.logging.Message
annotation. - The value attribute of
@org.jboss.logging.Message
contains the default log message. This is the message that is used if no translation is available.
@LogMessage @Message(value = "Customer query failed, Database not available.") void customerQueryFailDBClosed();
The default log level isINFO
.Invoke the methods
Add the calls to the interface methods in your code where the messages must be logged from. It is not necessary to create implementations of the interfaces, the annotation processor does this for you when the project is compiled.AccountsLogger.LOGGER.customerQueryFailDBClosed();
The custom loggers are sub-classed from BasicLogger so the logging methods ofBasicLogger
(debug()
,error()
etc) can also be used. It is not necessary to create other loggers to log non-internationalized messages.AccountsLogger.LOGGER.error("Invalid query syntax.");
6.2.2.2. Create and Use Internationalized Messages
logging-tools
quickstart for a complete example.
Prerequisites
- You have a working Maven project using the JBoss EAP 6 repository. Refer to Section 2.3.2, “Configure the JBoss EAP 6 Maven Repository Using the Maven Settings”.
- The required Maven configuration for JBoss Logging Tools has been added. Refer to Section 6.2.6.1, “JBoss Logging Tools Maven Configuration”.
Procedure 6.2. Create and Use Internationalized Messages
Create an interface for the exceptions
JBoss Logging Tools defines internationalized messages in interfaces. Name each interface descriptively for the messages that will be defined in it.The interface has the following requirements:- It must be declared as public
- It must be annotated with
@org.jboss.logging.MessageBundle
. - The interface must define a field that is a message bundle of the same type as the interface.
@MessageBundle(projectCode="") public interface GreetingMessageBundle { GreetingMessageBundle MESSAGES = Messages.getBundle(GreetingMessageBundle.class); }
Add method definitions
Add a method definition to the interface for each message. Name each method descriptively for the message that it represents.Each method has the following requirements:- It must return an object of type
String
. - It must be annotated with the
@org.jboss.logging.Message
annotation. - The value attribute of
@org.jboss.logging.Message
must be set to the default message. This is the message that is used if no translation is available.
@Message(value = "Hello world.") String helloworldString();
Invoke methods
Invoke the interface methods in your application where you need to obtain the message.System.console.out.println(helloworldString());
6.2.2.3. Create Internationalized Exceptions
logging-tools
quick start for a complete example.
Procedure 6.3. Create and use Internationalized Exceptions
Add JBoss Logging Tools configuration
Add the required project configuration to support JBoss Logging Tools. Refer to Section 6.2.6.1, “JBoss Logging Tools Maven Configuration”Create an interface for the exceptions
JBoss Logging Tools defines internationalized exceptions in interfaces. Name each interface descriptively for the exceptions that will be defined in it.The interface has the following requirements:- It must be declared as
public
. - It must be annotated with
@org.jboss.logging.MessageBundle
. - The interface must define a field that is a message bundle of the same type as the interface.
@MessageBundle(projectCode="") public interface ExceptionBundle { ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class); }
Add method definitions
Add a method definition to the interface for each exception. Name each method descriptively for the exception that it represents.Each method has the following requirements:- It must return an object of type
Exception
or a sub-type ofException
. - It must be annotated with the
@org.jboss.logging.Message
annotation. - The value attribute of
@org.jboss.logging.Message
must be set to the default exception message. This is the message that is used if no translation is available. - If the exception being returned has a constructor that requires parameters in addition to a message string, then those parameters must be supplied in the method definition using the
@Param
annotation. The parameters must be the same type and order as the constructor.
@Message(value = "The config file could not be opened.") IOException configFileAccessError(); @Message(id = 13230, value = "Date string '%s' was invalid.") ParseException dateWasInvalid(String dateString, @Param int errorOffset);
Invoke methods
Invoke the interface methods in your code where you need to obtain one of the exceptions. The methods do not throw the exceptions, they return the exception object which you can then throw.try { propsInFile=new File(configname); props.load(new FileInputStream(propsInFile)); } catch(IOException ioex) //in case props file does not exist { throw ExceptionBundle.EXCEPTIONS.configFileAccessError(); }
6.2.3. Localizing Internationalized Loggers, Messages and Exceptions
6.2.3.1. Generate New Translation Properties Files with Maven
logging-tools
quick start for a complete example.
Prerequisites:
- You must already have a working Maven project.
- The project must already be configured for JBoss Logging Tools.
- The project must contain one or more interfaces that define internationalized log messages or exceptions.
Procedure 6.4. Generate New Translation Properties Files with Maven
Add Maven configuration
Add the-AgenereatedTranslationFilePath
compiler argument to the Maven compiler plug-in configuration and assign it the path where the new files will be created.<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.6</source> <target>1.6</target> <compilerArgument> -AgeneratedTranslationFilesPath=${project.basedir}/target/generated-translation-files </compilerArgument> <showDeprecation>true</showDeprecation> </configuration> </plugin>
The above configuration will create the new files in thetarget/generated-translation-files
directory of your Maven project.Build the project
Build the project using Maven.[Localhost]$ mvn compile
@MessageBundle
or @MessageLogger
. The new files are created in a subdirectory corresponding to the Java package that each interface is declared in.
InterfaceName
is the name of the interface that this file was generated for: InterfaceName.i18n_locale_COUNTRY_VARIANT.properties
.
6.2.3.2. Translate an Internationalized Logger, Exception or Message
logging-tools
quick start for a complete example.
Prerequisites
- You must already have a working Maven project.
- The project must already be configured for JBoss Logging Tools.
- The project must contain one or interfaces that define internationalized log messages or exceptions.
- The project must be configured to generate template translation property files.
Procedure 6.5. Translate an internationalized logger, exception or message
Generate the template properties files
Run themvn compile
command to create the template translation properties files.Add the template file to your project
Copy the template for the interfaces that you want to translate from the directory where they were created into thesrc/main/resources
directory of your project. The properties files must be in the same package as the interfaces they are translating.Rename the copied template file
Rename the copy of the template file according to the translation it will contain. E.g.GreeterLogger.i18n_fr_FR.properties
.Translate the contents of the template.
Edit the new translation properties file to contain the appropriate translation.# Level: Logger.Level.INFO # Message: Hello message sent. logHelloMessageSent=Bonjour message envoyé.
Repeat steps two, three, and four for each translation of each bundle being performed.
target/generated-sources/annotations/
.
6.2.4. Customizing Internationalized Log Messages
6.2.4.1. Add Message Ids and Project Codes to Log Messages
logging-tools
quick start for a complete example.
Prerequisites
- You must already have a project with internationalized log messages. Refer to Section 6.2.2.1, “Create Internationalized Log Messages”.
- You need to know what the project code you will be using is. You can use a single project code, or define different ones for each interface.
Procedure 6.6. Add message Ids and Project Codes to Log Messages
Specify the project code for the interface.
Specify the project code using the projectCode attribute of the@MessageLogger
annotation attached to a custom logger interface. All messages that are defined in the interface will use that project code.@MessageLogger(projectCode="ACCNTS") interface AccountsLogger extends BasicLogger { }
Specify Message Ids
Specify a message ID for each message using theid
attribute of the@Message
annotation attached to the method that defines the message.@LogMessage @Message(id=43, value = "Customer query failed, Database not available.") void customerQueryFailDBClosed();
10:55:50,638 INFO [com.company.accounts.ejb] (MSC service thread 1-4) ACCNTS000043: Customer query failed, Database not available.
6.2.4.2. Specify the Log Level for a Message
INFO
. A different log level can be specified with the level
attribute of the @LogMessage
annotation attached to the logging method.
Procedure 6.7. Specify the log level for a message
Specify level attribute
Add thelevel
attribute to the@LogMessage
annotation of the log message method definition.Assign log level
Assign thelevel
attribute the value of the log level for this message. The valid values forlevel
are the six enumerated constants defined inorg.jboss.logging.Logger.Level
:DEBUG
,ERROR
,FATAL
,INFO
,TRACE
, andWARN
.Import org.jboss.logging.Logger.Level; @LogMessage(level=Level.ERROR) @Message(value = "Customer query failed, Database not available.") void customerQueryFailDBClosed();
ERROR
.
10:55:50,638 ERROR [com.company.app.Main] (MSC service thread 1-4) Customer query failed, Database not available.
6.2.4.3. Customize Log Messages with Parameters
Procedure 6.8. Customize log messages with parameters
Add parameters to method definition
Parameters of any type can be added to the method definition. Regardless of type, the String representation of the parameter is what is displayed in the message.Add parameter references to the log message
References can use explicit or ordinary indexes.- To use ordinary indexes, insert the characters
%s
in the message string where you want each parameter to appear. The first instance of%s
will insert the first parameter, the second instance will insert the second parameter, and so on. - To use explicit indexes, insert the characters
%{#}
in the message where # is the number of the parameter you want to appear.
Important
@Cause
annotation is not included in the number of parameters.
Example 6.1. Message parameters using ordinary indexes
@LogMessage(level=Logger.Level.DEBUG) @Message(id=2, value="Customer query failed, customerid:%s, user:%s") void customerLookupFailed(Long customerid, String username);
Example 6.2. Message parameters using explicit indexes
@LogMessage(level=Logger.Level.DEBUG) @Message(id=2, value="Customer query failed, customerid:%{1}, user:%{2}") void customerLookupFailed(Long customerid, String username);
6.2.4.4. Specify an Exception as the Cause of a Log Message
Throwable
or any of its sub-classes and is marked with the @Cause
annotation. This parameter cannot be referenced in the log message like other parameters and is displayed after the log message.
@Cause
parameter to indicate the "causing" exception. It is assumed that you have already created internationalized logging messages to which you want to add this functionality.
Procedure 6.9. Specify an exception as the cause of a log message
Add the parameter
Add a parameter of the typeThrowable
or a sub-class to the method.@LogMessage @Message(id=404, value="Loading configuration failed. Config file:%s") void loadConfigFailed(Exception ex, File file);
Add the annotation
Add the@Cause
annotation to the parameter.import org.jboss.logging.Cause @LogMessage @Message(value = "Loading configuration failed. Config file: %s") void loadConfigFailed(@Cause Exception ex, File file);
Invoke the method
When the method is invoked in your code, an object of the correct type must be passed and will be displayed after the log message.try { confFile=new File(filename); props.load(new FileInputStream(confFile)); } catch(Exception ex) //in case properties file cannot be read { ConfigLogger.LOGGER.loadConfigFailed(ex, filename); }
Below is the output of the above code samples if the code threw an exception of typeFileNotFoundException
.10:50:14,675 INFO [com.company.app.Main] (MSC service thread 1-3) Loading configuration failed. Config file: customised.properties java.io.FileNotFoundException: customised.properties (No such file or directory) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.<init>(FileInputStream.java:120) at com.company.app.demo.Main.openCustomProperties(Main.java:70) at com.company.app.Main.go(Main.java:53) at com.company.app.Main.main(Main.java:43)
6.2.5. Customizing Internationalized Exceptions
6.2.5.1. Add Message Ids and Project Codes to Exception Messages
Prerequisites
- You must already have a project with internationalized exceptions. Refer to Section 6.2.2.3, “Create Internationalized Exceptions”.
- You need to know what the project code you will be using is. You can use a single project code, or define different ones for each interface.
Procedure 6.10. Add message IDs and project codes to exception messages
Specify a project code
Specify the project code using theprojectCode
attribute of the@MessageBundle
annotation attached to a exception bundle interface. All messages that are defined in the interface will use that project code.@MessageBundle(projectCode="ACCTS") interface ExceptionBundle { ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class); }
Specify message IDs
Specify a message id for each exception using theid
attribute of the@Message
annotation attached to the method that defines the exception.@Message(id=143, value = "The config file could not be opened.") IOException configFileAccessError();
Important
Example 6.3. Creating internationalized exceptions
@MessageBundle(projectCode="ACCTS") interface ExceptionBundle { ExceptionBundle EXCEPTIONS = Messages.getBundle(ExceptionBundle.class); @Message(id=143, value = "The config file could not be opened.") IOException configFileAccessError(); }
throw ExceptionBundle.EXCEPTIONS.configFileAccessError();
Exception in thread "main" java.io.IOException: ACCTS000143: The config file could not be opened. at com.company.accounts.Main.openCustomProperties(Main.java:78) at com.company.accounts.Main.go(Main.java:53) at com.company.accounts.Main.main(Main.java:43)
6.2.5.2. Customize Exception Messages with Parameters
Procedure 6.11. Customize an exception message with parameters
Add parameters to method definition
Parameters of any type can be added to the method definition. Regardless of type, theString
representation of the parameter is what is displayed in the message.Add parameter references to the exception message
References can use explicit or ordinary indexes.- To use ordinary indexes, insert the characters
%s
in the message string where you want each parameter to appear. The first instance of%s
will insert the first parameter, the second instance will insert the second parameter, and so on. - To use explicit indexes, insert the characters
%{#}
in the message where#
is the number of the parameter you want to appear.
Using explicit indexes allows the parameter references in the message to be in a different order than they are defined in the method. This is important for translated messages which may require different ordering of parameters.
Important
@Cause
annotation is not included in the number of parameters.
Example 6.4. Using ordinary indexes
@Message(id=143, value = "The config file %s could not be opened.") IOException configFileAccessError(File config);
Example 6.5. Using explicit indexes
@Message(id=143, value = "The config file %{1} could not be opened.") IOException configFileAccessError(File config);
6.2.5.3. Specify One Exception as the Cause of Another Exception
@Cause
. This parameter is used to pass the causing exception. This parameter cannot be referenced in the exception message.
@Cause
parameter to indicate the causing exception. It is assumed that you have already created an exception bundle to which you want to add this functionality.
Procedure 6.12. Specify one exception as the cause of another exception
Add the parameter
Add the a parameter of the typeThrowable
or a sub-class to the method.@Message(id=328, value = "Error calculating: %s.") ArithmeticException calculationError(Throwable cause, String msg);
Add the annotation
Add the@Cause
annotation to the parameter.import org.jboss.logging.Cause @Message(id=328, value = "Error calculating: %s.") ArithmeticException calculationError(@Cause Throwable cause, String msg);
Invoke the method
Invoke the interface method to obtain an exception object. The most common use case is to throw a new exception from a catch block using the caught exception as the cause.try { ... } catch(Exception ex) { throw ExceptionBundle.EXCEPTIONS.calculationError( ex, "calculating payment due per day"); }
Example 6.6. Specify one exception as the cause of another exception
@MessageBundle(projectCode = "TPS") interface CalcExceptionBundle { CalcExceptionBundle EXCEPTIONS = Messages.getBundle(CalcExceptionBundle.class); @Message(id=328, value = "Error calculating: %s.") ArithmeticException calcError(@Cause Throwable cause, String value); }
int totalDue = 5; int daysToPay = 0; int amountPerDay; try { amountPerDay = totalDue/daysToPay; } catch (Exception ex) { throw CalcExceptionBundle.EXCEPTIONS.calcError(ex, "payments per day"); }
Exception in thread "main" java.lang.ArithmeticException: TPS000328: Error calculating: payments per day. at com.company.accounts.Main.go(Main.java:58) at com.company.accounts.Main.main(Main.java:43) Caused by: java.lang.ArithmeticException: / by zero at com.company.accounts.Main.go(Main.java:54) ... 1 more
6.2.6. Reference
6.2.6.1. JBoss Logging Tools Maven Configuration
pom.xml
file.
logging-tools
quick start for an example of a complete working pom.xml
file.
- JBoss Maven Repository must be enabled for the project. Refer to Section 2.3.2, “Configure the JBoss EAP 6 Maven Repository Using the Maven Settings”.
- The Maven dependencies for
jboss-logging
andjboss-logging-processor
must be added. Both of dependencies are available in JBoss EAP 6 so the scope element of each can be set toprovided
as shown.<dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging-processor</artifactId> <version>1.0.0.Final</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> <version>3.1.0.GA</version> <scope>provided</scope> </dependency>
- The
maven-compiler-plugin
must be at least version2.2
and be configured for target and generated sources of1.6
.<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin>
6.2.6.2. Translation Property File Format
key=value
pair format described in the documentation for the java.util.Properties
class, http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html.
InterfaceName.i18n_locale_COUNTRY_VARIANT.properties
InterfaceName
is the name of the interface that the translations apply to.locale
,COUNTRY
, andVARIANT
identify the regional settings that the translation applies to.locale
andCOUNTRY
specify the language and country using the ISO-639 and ISO-3166 Language and Country codes respectively.COUNTRY
is optional.VARIANT
is an optional identifier that can be used to identify translations that only apply to a specific operating system or browser.
Example 6.7. Sample Translation Properties File
GreeterService.i18n_fr_FR_POSIX.properties
.
# Level: Logger.Level.INFO # Message: Hello message sent. logHelloMessageSent=Bonjour message envoyé.
6.2.6.3. JBoss Logging Tools Annotations Reference
Annotation | Target | Description | Attributes |
---|---|---|---|
@MessageBundle | Interface |
Defines the interface as a Message Bundle.
| projectCode |
@MessageLogger | Interface |
Defines the interface as a Message Logger.
| projectCode |
@Message | Method |
Can be used in Message Bundles and Message Loggers. In a Message Logger it defines a method as being a localized logger. In a Message Bundle it defines the method as being one that returns a localized String or Exception object.
| value , id |
@LogMessage | Method |
Defines a method in a Message Logger as being a logging method.
| level (default INFO ) |
@Cause | Parameter |
Defines a parameter as being one that passes an Exception as the cause of either a Log message or another Exception.
| - |
@Param | Parameter |
Defines a parameter as being one that is passed to the constructor of the Exception.
| - |
Chapter 7. Enterprise JavaBeans
7.1. Introduction
7.1.1. Overview of Enterprise JavaBeans
7.1.2. EJB 3.1 Feature Set
- Session Beans
- Message Driven Beans
- No-interface views
- local interfaces
- remote interfaces
- JAX-WS web services
- JAX-RS web services
- Timer Service
- Asynchronous Calls
- Interceptors
- RMI/IIOP interoperability
- Transaction support
- Security
- Embeddable API
- Entity Beans (container and bean-managed persistence)
- EJB 2.1 Entity Bean client views
- EJB Query Language (EJB QL)
- JAX-RPC based Web Services (endpoints and client views)
7.1.3. EJB 3.1 Lite
- Only supporting the features that make sense for web-applications, and
- allowing EJBs to be deployed in the same WAR file as a web-application.
7.1.4. EJB 3.1 Lite Features
- Stateless, stateful, and singleton session beans
- Local business interfaces and "no interface" beans
- Interceptors
- Container-managed and bean-managed transactions
- Declarative and programmatic security
- Embeddable API
- Remote interfaces
- RMI-IIOP Interoperability
- JAX-WS Web Service Endpoints
- EJB Timer Service
- Asynchronous session bean invocations
- Message-driven beans
7.1.5. Enterprise Beans
Important
7.1.6. Overview of Writing Enterprise Beans
7.1.7. Session Bean Business Interfaces
7.1.7.1. Enterprise Bean Business Interfaces
7.1.7.2. EJB Local Business Interfaces
7.1.7.3. EJB Remote Business Interfaces
7.1.7.4. EJB No-interface Beans
7.2. Creating Enterprise Bean Projects
7.2.1. Create an EJB Archive Project Using Red Hat JBoss Developer Studio
Prerequisites
- A server and server runtime for JBoss EAP 6 has been set up.
Procedure 7.1. Create an EJB Project in Red Hat JBoss Developer Studio
Create new project
To open the New EJB Project wizard, navigate to themenu, select , and then .Figure 7.1. New EJB Project wizard
Specify Details
Supply the following details:- Project name.As well as the being the name of the project that appears in Red Hat JBoss Developer Studio this is also the default filename for the deployed JAR file.
- Project location.The directory where the project's files will be saved. The default is a directory in the current workspace.
- Target Runtime.This is the server runtime used for the project. This will need to be set to the same JBoss EAP 6 runtime used by the server that you will be deploying to.
- EJB module version. This is the version of the EJB specification that your enterprise beans will comply with. Red Hat recommends using
3.1
. - Configuration. This allows you to adjust the supported features in your project. Use the default configuration for your selected runtime.
Clickto continue.Java Build Configuration
This screen allows you to customize the directories will contain Java source files and the directory where the built output is placed.Leave this configuration unchanged and click.EJB Module settings
Check the Generate ejb-jar.xml deployment descriptor checkbox if a deployment descriptor is required. The deployment descriptor is optional in EJB 3.1 and can be added later if required.Clickand the project is created and will be displayed in the Project Explorer.Figure 7.2. Newly created EJB Project in the Project Explorer
Add Build Artifact to Server for Deployment
Open the Add and Remove dialog by right-clicking on the server you want to deploy the built artifact to in the server tab, and select "Add and Remove".Select the resource to deploy from the Available column and click the button. The resource will be moved to the Configured column. Click to close the dialog.Figure 7.3. Add and Remove dialog
You now have an EJB Project in Red Hat JBoss Developer Studio that can build and deploy to the specified server.
7.2.2. Create an EJB Archive Project in Maven
Prerequisites:
- Maven is already installed.
- You understand the basic usage of Maven.
Procedure 7.2. Create an EJB Archive project in Maven
Create the Maven project
An EJB project can be created using Maven's archetype system and theejb-javaee6
archetype. To do this run themvn
command with parameters as shown:mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=ejb-javaee6
Maven will prompt you for thegroupId
,artifactId
,version
andpackage
for your project.[localhost]$ mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=ejb-javaee6 [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Maven Stub Project (No POM) 1 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] >>> maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom >>> [INFO] [INFO] <<< maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom <<< [INFO] [INFO] --- maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom --- [INFO] Generating project in Interactive mode [INFO] Archetype [org.codehaus.mojo.archetypes:ejb-javaee6:1.5] found in catalog remote Define value for property 'groupId': : com.shinysparkly Define value for property 'artifactId': : payment-arrangments Define value for property 'version': 1.0-SNAPSHOT: : Define value for property 'package': com.shinysparkly: : Confirm properties configuration: groupId: com.company artifactId: payment-arrangments version: 1.0-SNAPSHOT package: com.company.collections Y: : [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 32.440s [INFO] Finished at: Mon Oct 31 10:11:12 EST 2011 [INFO] Final Memory: 7M/81M [INFO] ------------------------------------------------------------------------ [localhost]$
Add your enterprise beans
Write your enterprise beans and add them to the project under thesrc/main/java
directory in the appropriate sub-directory for the bean's package.Build the project
To build the project, run themvn package
command in the same directory as thepom.xml
file. This will compile the Java classes and package the JAR file. The built JAR file is namedartifactId-version.jar
and is placed in thetarget/
directory.
7.2.3. Create an EAR Project containing an EJB Project
Prerequisites
- A server and server runtime for JBoss EAP 6 has been set up. Refer to Section 1.3.1.5, “Add the JBoss EAP Server Using Define New Server”.
Procedure 7.3. Create an EAR Project containing an EJB Project
Open the New EAR Application Project Wizard
Navigate to the New Project wizard appears. Select and click .menu, select , then and theFigure 7.4. New EAR Application Project Wizard
Supply details
Supply the following details:- Project name.As well as the being the name of the project that appears in Red Hat JBoss Developer Studio this is also the default filename for the deployed EAR file.
- Project location.The directory where the project's files will be saved. The default is a directory in the current workspace.
- Target Runtime.This is the server runtime used for the project. This will need to be set to the same JBoss EAP 6 runtime used by the server that you will be deploying to.
- EAR version.This is the version of the Java Enterprise Edition specification that your project will comply with. Red Hat recommends using
6
. - Configuration. This allows you to adjust the supported features in your project. Use the default configuration for your selected runtime.
Clickto continue.Add a new EJB Module
New Modules can be added from the Enterprise Application page of the wizard. To add a new EJB Project as a module follow the steps below:Add new EJB Module
Click Create Default Modules checkbox, select the Enterprise Java Bean and click . The New EJB Project wizard appears., uncheckCreate EJB Project
New EJB Project wizard is the same as the wizard used to create new standalone EJB Projects and is described in Section 7.2.1, “Create an EJB Archive Project Using Red Hat JBoss Developer Studio”.The minimal details required to create the project are:- Project name
- Target Runtime
- EJB Module version
- Configuration
All the other steps of the wizard are optional. Clickto complete creating the EJB Project.
The newly created EJB project is listed in the Java EE module dependencies and the checkbox is checked.Optional: add an application.xml deployment descriptor
Check the Generate application.xml deployment descriptor checkbox if one is required.Click Finish
Two new project will appear, the EJB project and the EAR projectAdd Build Artifact to Server for Deployment
Open the Add and Remove dialog by right-clicking in the Servers tab on the server you want to deploy the built artifact to in the server tab, and select .Select the EAR resource to deploy from the Available column and click the button. The resource will be moved to the Configured column. Click to close the dialog.Figure 7.5. Add and Remove dialog
You now have an Enterprise Application Project with a member EJB Project. This will build and deploy to the specified server as a single EAR deployment containing an EJB subdeployment.
7.2.4. Add a Deployment Descriptor to an EJB Project
Perquisites:
- You have a EJB Project in Red Hat JBoss Developer Studio to which you want to add an EJB deployment descriptor.
Procedure 7.4. Add an Deployment Descriptor to an EJB Project
Open the Project
Open the project in Red Hat JBoss Developer Studio.Add Deployment Descriptor
Right-click on the Deployment Descriptor folder in the project view and select.Figure 7.6. Adding a Deployment Descriptor
ejb-jar.xml
, is created in ejbModule/META-INF/
. Double-clicking on the Deployment Descriptor folder in the project view will also open this file.
7.3. Session Beans
7.3.1. Session Beans
7.3.2. Stateless Session Beans
7.3.3. Stateful Session Beans
7.3.4. Singleton Session Beans
7.3.5. Add Session Beans to a Project in Red Hat JBoss Developer Studio
Prerequisites:
- You have a EJB or Dynamic Web Project in Red Hat JBoss Developer Studio to which you want to add one or more session beans.
Procedure 7.5. Add Session Beans to a Project in Red Hat JBoss Developer Studio
Open the Project
Open the project in Red Hat JBoss Developer Studio.Open the "Create EJB 3.x Session Bean" wizard
To open the Create EJB 3.x Session Bean wizard, navigate to the menu, select , and then .Figure 7.7. Create EJB 3.x Session Bean wizard
Specify class information
Supply the following details:- ProjectVerify the correct project is selected.
- Source folderThis is the folder that the Java source files will be created in. This should not usually need to be changed.
- PackageSpecify the package that the class belongs to.
- Class nameSpecify the name of the class that will be the session bean.
- SuperclassThe session bean class can inherit from a super class. Specify that here if your session has a super class.
- State typeSpecify the state type of the session bean: stateless, stateful, or singleton.
- Business InterfacesBy default the No-interface box is checked so no interfaces will be created. Check the boxes for the interfaces you wish to define and adjust the names if necessary.Remember that enterprise beans in a web archive (WAR) only support EJB 3.1 Lite and this does not include remote business interfaces.
Click.Session Bean Specific Information
You can enter in additional information here to further customize the session bean. It is not required to change any of the information here.Items that you can change are:- Bean name.
- Mapped name.
- Transaction type (Container managed or Bean managed).
- Additional interfaces can be supplied that the bean must implement.
- You can also specify EJB 2.x Home and Component interfaces if required.
Finish
Clickand the new session bean will be created and added to the project. The files for any new business interfaces will also be created if they were specified.
Figure 7.8. New Session Bean in Red Hat JBoss Developer Studio
7.4. Message-Driven Beans
7.4.1. Message-Driven Beans
7.4.2. Resource Adapters
7.4.3. Create a JMS-based Message-Driven Bean in Red Hat JBoss Developer Studio
Prerequisites:
- You must have an existing project open in Red Hat JBoss Developer Studio.
- You must know the name and type of the JMS destination that the bean will be listening to.
- Support for Java Messaging Service (JMS) must be enabled in the JBoss EAP 6 configuration to which this bean will be deployed.
Procedure 7.6. Add a JMS-based Message-Driven Bean in Red Hat JBoss Developer Studio
Open the Create EJB 3.x Message-Driven Bean Wizard
Go to EJB/Message-Driven Bean (EJB 3.x) and click the button.→ → . SelectFigure 7.9. Create EJB 3.x Message-Driven Bean Wizard
Specify class file destination details
There are three sets of details to specify for the bean class here: Project, Java class, and message destination.- Project
- If multiple projects exist in the Workspace, ensure that the correct one is selected in the menu.
- The folder where the source file for the new bean will be created is
ejbModule
under the selected project's directory. Only change this if you have a specific requirement.
- Java class
- The required fields are: Java package and class name.
- It is not necessary to supply a Superclass unless the business logic of your application requires it.
- Message Destination
- These are the details you must supply for a JMS-based Message-Driven Bean:
- Destination name. This is the queue or topic name that contains the messages that the bean will respond to.
- By default the JMS checkbox is selected. Do not change this.
- Set Destination type to Queue or Topic as required.
Click thebutton.Enter Message-Driven Bean specific information
The default values here are suitable for a JMS-based Message-Driven bean using Container-managed transactions.- Change the Transaction type to Bean if the Bean will use Bean-managed transactions.
- Change the Bean name if a different bean name than the class name is required.
- The JMS Message Listener interface will already be listed. You do not need to add or remove any interfaces unless they are specific to your applications business logic.
- Leave the checkboxes for creating method stubs selected.
Click thebutton.
onMessage()
method. A Red Hat JBoss Developer Studio editor window opened with the corresponding file.
7.4.4. Enable EJB and MDB Property Substitution in an Application
@ActivationConfigProperty
and @Resource
annotations. Property substitution requires the following configuration and code changes.
- You must enable property substitution in the JBoss EAP server configuration file.
- You must define the system properties in the server configuration file or pass them as arguments when you start the JBoss EAP server.
- You must modify the code to use the substitution variables.
Procedure 7.7. Implement Property Substitution in an MDB Application
helloworld-mdb
quickstart that ships with JBoss EAP 6.3 or later. This topic shows you how to modify that quickstart to enable property substitution.
Configure the JBoss EAP server to enable property substitution.
The JBoss EAP server must be configured to enable property substitution. To do this, set the<annotation-property-replacement>
attribute in theee
subsystem of the server configuration file totrue
.- Back up the server configuration file. The
helloworld-mdb
quickstart example requires the full profile for a standalone server, so this is thestandalone/configuration/standalone-full.xml
file. If you are running your server in a managed domain, this is thedomain/configuration/domain.xml
file. - Start the JBoss EAP server with the full profile.For Linux:
For Windows:EAP_HOME/bin/standalone.sh -c standalone-full.xml
EAP_HOMEbin\standalone.bat -c standalone-full.xml
- Launch the Management CLI using the command for your operating system.For Linux:
For Windows:EAP_HOME/bin/jboss-cli.sh --connect
EAP_HOME\bin\jboss-cli.bat --connect
- Type the following command to enable annotation property substitution.
/subsystem=ee:write-attribute(name=annotation-property-replacement,value=true)
- You should see the following result:
{"outcome" => "success"}
- Review the changes to the JBoss EAP server configuration file. The
ee
subsystem should now contain the following XML.<subsystem xmlns="urn:jboss:domain:ee:1.2"> <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement> <jboss-descriptor-property-replacement>true</jboss-descriptor-property-replacement> <annotation-property-replacement>true</annotation-property-replacement> </subsystem>
Define the system properties.
You can specify the system properties in the server configuration file or you can pass them as command line arguments when you start the JBoss EAP server. System properties defined in the server configuration file take precedence over those passed on the command line when you start the server.- Define the system properties in the server configuration file.
- Start the JBoss EAP server and Management API as described in the previous step.
- Use the following command syntax to configure a system property in the JBoss EAP server:
/system-property=PROPERTY_NAME:add(value=PROPERTY_VALUE)
For thehelloworld-mdb
quickstart, we configure the following system properties:/system-property=property.helloworldmdb.queue:add(value=java:/queue/HELLOWORLDMDBPropQueue)
/system-property=property.helloworldmdb.topic:add(value=java:/topic/HELLOWORLDMDBPropTopic)
/system-property=property.connection.factory:add(value=java:/ConnectionFactory)
- Review the changes to the JBoss EAP server configuration file. The following system properties should now appear in the after the
<extensions>
.<system-properties> <property name="property.helloworldmdb.queue" value="java:/queue/HELLOWORLDMDBPropQueue"/> <property name="property.helloworldmdb.topic" value="java:/topic/HELLOWORLDMDBPropTopic"/> <property name="property.connection.factory" value="java:/ConnectionFactory"/> </system-properties>
- Pass the system properties as arguments on the command line when you start the JBoss EAP server in the form of
-DPROPERTY_NAME=PROPERTY_VALUE
. The following is an example of how to pass the arguments for the system properties defined in the previous step.EAP_HOME/bin/standalone.sh -c standalone-full.xml -Dproperty.helloworldmdb.queuejava:/queue/HELLOWORLDMDBPropQueue -Dproperty.helloworldmdb.topic=java:/topic/HELLOWORLDMDBPropTopic -Dproperty.connection.factory=java:/ConnectionFactory
Modify the code to use the system property substitutions.
Replace hard-coded@ActivationConfigProperty
and@Resource
annotation values with substitutions for the newly defined system properties. The following are examples of how to change thehelloworld-mdb
quickstart to use the newly defined system property substitutions within the annotations in the source code.- Change the
@ActivationConfigProperty
destination
property value in theHelloWorldQueueMDB
class to use the substitution for the system property. The@MessageDriven
annotation should now look like this:@MessageDriven(name = "HelloWorldQueueMDB", activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "${property.helloworldmdb.queue}"), @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
- Change the
@ActivationConfigProperty
destination
property value in theHelloWorldTopicMDB
class to use the substitution for the system property. The@MessageDriven
annotation should now look like this:@MessageDriven(name = "HelloWorldQTopicMDB", activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "${property.helloworldmdb.topic}"), @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
- Change the
@Resource
annotations in theHelloWorldMDBServletClient
class to use the system property substitutions. The code should now look like this:@Resource(mappedName = "${property.connection.factory}") private ConnectionFactory connectionFactory; @Resource(mappedName = "${property.helloworldmdb.queue}") private Queue queue; @Resource(mappedName = "${property.helloworldmdb.topic}") private Topic topic;
- Modify the
hornetq-jms.xml
file to use the system property substitution values.<?xml version="1.0" encoding="UTF-8"?> <messaging-deployment xmlns="urn:jboss:messaging-deployment:1.0"> <hornetq-server> <jms-destinations> <jms-queue name="HELLOWORLDMDBQueue"> <entry name="${property.helloworldmdb.queue}"/> </jms-queue> <jms-topic name="HELLOWORLDMDBTopic"> <entry name="${property.helloworldmdb.topic}"/> </jms-topic> </jms-destinations> </hornetq-server> </messaging-deployment>
- Deploy the application. The application will now use the values specified by the system properties for the
@Resource
and@ActivationConfigProperty
property values.
7.5. Invoking Session Beans
7.5.1. Invoke a Session Bean Remotely using JNDI
ejb-remote
quickstart contains working Maven projects that demonstrate this functionality. The quickstart contains projects for both the session beans to deploy and the remote client. The code samples below are taken from the remote client project.
Warning
Prerequisites
- You must already have a Maven project created ready to use.
- Configuration for the JBoss EAP 6 Maven repository has already been added.
- The session beans that you want to invoke are already deployed.
- The deployed session beans implement remote business interfaces.
- The remote business interfaces of the session beans are available as a Maven dependency. If the remote business interfaces are only available as a JAR file then it is recommended to add the JAR to your Maven repository as an artifact. Refer to the Maven documentation for the
install:install-file
goal for directions, http://maven.apache.org/plugins/maven-install-plugin/usage.html - You need to know the hostname and JNDI port of the server hosting the session beans.
Procedure 7.8. Add Maven Project Configuration for Remote Invocation of Session Beans
Add the required project dependencies
Thepom.xml
for the project must be updated to include the necessary dependencies.Add the
jboss-ejb-client.properties
fileThe JBoss EJB client API expects to find a file in the root of the project namedjboss-ejb-client.properties
that contains the connection information for the JNDI service. Add this file to thesrc/main/resources/
directory of your project with the following content.# In the following line, set SSL_ENABLED to true for SSL remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false remote.connections=default # Uncomment the following line to set SSL_STARTTLS to true for SSL # remote.connection.default.connect.options.org.xnio.Options.SSL_STARTTLS=true remote.connection.default.host=localhost remote.connection.default.port = 4447 remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false # Add any of the following SASL options if required # remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false # remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT=false # remote.connection.default.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS=JBOSS-LOCAL-USER
Change the host name and port to match your server.4447
is the default port number. For a secure connection, set theSSL_ENABLED
line totrue
and uncomment theSSL_STARTTLS
line. The Remoting interface in the container supports secured and unsecured connections using the same port.Add dependencies for the remote business interfaces
Add the Maven dependencies to thepom.xml
for the remote business interfaces of the session beans.<dependency> <groupId>org.jboss.as.quickstarts</groupId> <artifactId>jboss-ejb-remote-server-side</artifactId> <type>ejb-client</type> <version>${project.version}</version> </dependency>
Procedure 7.9. Obtain a Bean Proxy using JNDI and Invoke Methods of the Bean
Handle checked exceptions
Two of the methods used in the following code (InitialContext()
andlookup()
) have a checked exception of typejavax.naming.NamingException
. These method calls must either be enclosed in a try/catch block that catchesNamingException
or in a method that is declared to throwNamingException
. Theejb-remote
quickstart uses the second technique.Create a JNDI Context
A JNDI Context object provides the mechanism for requesting resources from the server. Create a JNDI context using the following code:final Hashtable jndiProperties = new Hashtable(); jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); final Context context = new InitialContext(jndiProperties);
The connection properties for the JNDI service are read from thejboss-ejb-client.properties
file.Use the JNDI Context's lookup() method to obtain a bean proxy
Invoke thelookup()
method of the bean proxy and pass it the JNDI name of the session bean you require. This will return an object that must be cast to the type of the remote business interface that contains the methods you want to invoke.final RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup( "ejb:/jboss-ejb-remote-server-side//CalculatorBean!" + RemoteCalculator.class.getName());
Session bean JNDI names are defined using a special syntax. For more information, see Section 7.8.1, “EJB JNDI Naming Reference” .Invoke methods
Now that you have a proxy bean object you can invoke any of the methods contained in the remote business interface.int a = 204; int b = 340; System.out.println("Adding " + a + " and " + b + " via the remote stateless calculator deployed on the server"); int sum = statelessRemoteCalculator.add(a, b); System.out.println("Remote calculator returned sum = " + sum);
The proxy bean passes the method invocation request to the session bean on the server, where it is executed. The result is returned to the proxy bean which then returns it to the caller. The communication between the proxy bean and the remote session bean is transparent to the caller.
7.5.2. About EJB Client Contexts
- A remote client, which runs as a standalone Java application.
- A remote client, which runs within another JBoss EAP 6 instance.
7.5.3. Considerations When Using a Single EJB Context
You must consider your application requirements when using a single EJB client context with standalone remote clients. For more information about the different types of remote clients, refer to: Section 7.5.2, “About EJB Client Contexts” .
A remote standalone client typically has just one EJB client context backed by any number of EJB receivers. The following is an example of a standalone remote client application:
public class MyApplication { public static void main(String args[]) { final javax.naming.Context ctxOne = new javax.naming.InitialContext(); final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface"); beanOne.doSomething(); ... } }
jboss-ejb-client.properties
file, which is used to set up the EJB client context and the EJB receivers. This configuration also includes the security credentials, which are then used to create the EJB receiver that connects to the JBoss EAP 6 server. When the above code is invoked, the EJB client API looks for the EJB client context, which is then used to select the EJB receiver that will receive and process the EJB invocation request. In this case, there is just the single EJB client context, so that context is used by the above code to invoke the bean. The procedure to invoke a session bean remotely using JNDI is described in greater detail here: Section 7.5.1, “Invoke a Session Bean Remotely using JNDI” .
A user application may want to invoke a bean more than once, but connect to the JBoss EAP 6 server using different security credentials. The following is an example of a standalone remote client application that invokes the same bean twice:
public class MyApplication { public static void main(String args[]) { // Use the "foo" security credential connect to the server and invoke this bean instance final javax.naming.Context ctxOne = new javax.naming.InitialContext(); final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface"); beanOne.doSomething(); ... // Use the "bar" security credential to connect to the server and invoke this bean instance final javax.naming.Context ctxTwo = new javax.naming.InitialContext(); final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface"); beanTwo.doSomething(); ... } }
Scoped EJB client contexts offer a solution to this issue. They provide a way to have more control over the EJB client contexts and their associated JNDI contexts, which are typically used for EJB invocations. For more information about scoped EJB client contexts, refer to Section 7.5.4, “Using Scoped EJB Client Contexts” and Section 7.5.5, “Configure EJBs Using a Scoped EJB Client Context” .
7.5.4. Using Scoped EJB Client Contexts
To invoke an EJB In earlier versions of JBoss EAP 6, you would typically create a JNDI context and pass it the PROVIDER_URL, which would point to the target server. Any invocations done on EJB proxies that were looked up using that JNDI context, would end up on that server. With scoped EJB client contexts, user applications have control over which EJB receiver is used for a specific invocation.
Prior to the introduction of scoped EJB client contexts, the context was typically scoped to the client application. Scoped client contexts now allow the EJB client contexts to be scoped with the JNDI contexts. The following is an example of a standalone remote client application that invokes the same bean twice using a scoped EJB client context:
public class MyApplication { public static void main(String args[]) { // Use the "foo" security credential connect to the server and invoke this bean instance final Properties ejbClientContextPropsOne = getPropsForEJBClientContextOne(): final javax.naming.Context ctxOne = new javax.naming.InitialContext(ejbClientContextPropsOne); final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface"); beanOne.doSomething(); ... ctxOne.close(); // Use the "bar" security credential to connect to the server and invoke this bean instance final Properties ejbClientContextPropsTwo = getPropsForEJBClientContextTwo(): final javax.naming.Context ctxTwo = new javax.naming.InitialContext(ejbClientContextPropsTwo); final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface"); beanTwo.doSomething(); ... ctxTwo.close(); } }
jboss-ejb-client.properties
file. To scope the EJB client context to the JNDI context, you must also specify the org.jboss.ejb.client.scoped.context
property and set its value to true
. This property notifies the EJB client API that it must create an EJB client context, which is backed by EJB receivers, and that the created context is then scoped or visible only to the JNDI context that created it. Any EJB proxies looked up or invoked using this JNDI context will only know of the EJB client context associated with this JNDI context. Other JNDI contexts used by the application to lookup and invoke EJBs will not know about the other scoped EJB client contexts.
org.jboss.ejb.client.scoped.context
property and aren't scoped to an EJB client context will use the default behavior, which is to use the existing EJB client context that is typically tied to the entire application.
Note
InitialContext
when it is no longer needed. When the InitialContext
is closed, the resources are released immediately. The proxies that are bound to it are no longer valid and any invocation will throw an Exception. Failure to close the InitialContext
may result in resource and performance issues.
7.5.5. Configure EJBs Using a Scoped EJB Client Context
EJBs can be configured using a map-based scoped context. This is achieved by programmatically populating a Properties
map using the standard properties found in the jboss-ejb-client.properties
, specifying true
for the org.jboss.ejb.client.scoped.context
property, and passing the properties on the InitialContext
creation.
Procedure 7.10. Configure an EJB Using a Map-Based Scoped Context
Set the Properties
Configure the EJB client properties programmatically, specifying the same set of properties that are used in the standardjboss-ejb-client.properties
file. To enable the scoped context, you must specify theorg.jboss.ejb.client.scoped.context
property and set its value totrue
. The following is an example that configures the properties programmatically.// Configure EJB Client properties for the InitialContext Properties ejbClientContextProps = new Properties(); ejbClientContextProps.put(“remote.connections”,”name1”); ejbClientContextProps.put(“remote.connection.name1.host”,”localhost”); ejbClientContextProps.put(“remote.connection.name1.port”,”4447”); // Property to enable scoped EJB client context which will be tied to the JNDI context ejbClientContextProps.put("org.jboss.ejb.client.scoped.context", “true”);
Pass the Properties on the Context Creation
// Create the context using the configured properties InitialContext ic = new InitialContext(ejbClientContextProps); MySLSB bean = ic.lookup("ejb:myapp/ejb//MySLSBBean!" + MySLSB.class.getName());
- Contexts generated by lookup EJB proxies are bound by this scoped context and use only the relevant connection parameters. This makes it possible to create different contexts to access data within a client application or to independently access servers using different logins.
- In the client, both the scoped
InitialContext
and the scoped proxy are passed to threads, allowing each thread to work with the given context. It is also possible to pass the proxy to multiple threads that can use it concurrently. - The scoped context EJB proxy is serialized on the remote call and then deserialized on the server. When it is deserialized, the scoped context information is removed and it returns to its default state. If the deserialized proxy is used on the remote server, because it no longer has the scoped context that was used when it was created, this can result in an
EJBCLIENT000025
error or possibly call an unwanted target by using the EJB name.
7.5.6. EJB Client Properties
The following tables list properties that can be configured programmatically or in the jboss-ejb-client.properties
file.
The following table lists properties that are valid for the whole library within the same scope.
Property Name | Description |
---|---|
endpoint.name |
Name of the client endpoint. If not set, the default value is
client-endpoint
This can be helpful to distinguish different endpoint settings because the thread name contains this property.
|
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED |
Boolean value that specifies whether the SSL protocol is enabled for all connections.
Warning
Red Hat recommends that you explicitly disable SSL in favor of TLSv1.1 or TLSv1.2 in all affected packages.
|
deployment.node.selector |
The fully qualified name of the implementation of
org.jboss.ejb.client.DeploymentNodeSelector .
This is used to load balance the invocation for the EJBs.
|
invocation.timeout |
The timeout for the EJB handshake or method invocation request/response cycle. The value is in milliseconds.
The invocation of any method throws a
java.util.concurrent.TimeoutException if the execution takes longer than the timeout period. The execution completes and the server is not interrupted.
|
reconnect.tasks.timeout |
The timeout for the background reconnect tasks. The value is in milliseconds.
If a number of connections are down, the next client EJB invocation will use an algorithm to decide if a reconnect is necessary to find the right node.
|
org.jboss.ejb.client.scoped.context |
Boolean value that specifies whether to enable the scoped EJB client context. The default value is
false .
If set to
true , the EJB Client will use the scoped context that is tied to the JNDI context. Otherwise the EJB client context will use the global selector in the JVM to determine the properties used to call the remote EJB and host.
|
The connection properties start with the prefix remote.connection.CONNECTION_NAME
where the CONNECTION_NAME is a local identifier only used to uniquely identify the connection.
Property Name | Description |
---|---|
remote.connections |
A comma-separated list of active
connection-names . Each connection is configured by using this name.
|
|
The host name or IP for the connection.
|
|
The port for the connection. The default value is 4447.
|
|
The user name used to authenticate connection security.
|
|
The password used to authenticate the user.
|
|
The timeout period for the initial connection. After that, the reconnect task will periodically check whether the connection can be established. The value is in milliseconds.
|
|
Full qualified name of the
CallbackHandler class. It will be used to establish the connection and can not changed as long as the connection is open.
|
channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES
|
Integer value specifying the maximum number of outbound requests. The default is 80.
There is only one connection from the client (JVM) to the server to handle all invocations.
|
connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS
|
Boolean value that determines whether credentials must be provided by the client to connect successfully. The default value is
true .
If set to
true , the client must provide credentials. If set to false , invocation is allowed as long as the remoting connector does not request a security realm.
|
connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS
|
Disables certain SASL mechanisms used for authenticating during connection creation.
JBOSS_LOCAL_USER means the silent authentication mechanism, used when the client and server are on the same machine, is disabled.
|
connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT
|
Boolean value that enables or disables the use of plain text messages during the authentication. If using JAAS, it must be set to false to allow a plain text password.
|
connect.options.org.xnio.Options.SSL_ENABLED
|
Boolean value that specifies whether the SSL protocol is enabled for this connection.
Warning
Red Hat recommends that you explicitly disable SSL in favor of TLSv1.1 or TLSv1.2 in all affected packages.
|
connect.options.org.jboss.remoting3.RemotingOptions.HEARTBEAT_INTERVAL
|
t automatic close, for example, in the case of a firewall. The value is in milliseconds.
|
If the initial connection connects to a clustered environment, the topology of the cluster is received automatically and asynchronously. These properties are used to connect to each received member. Each property starts with the prefix remote.cluster.CLUSTER_NAME
where the CLUSTER_NAME refers to the related to the servers Infinispan subsystem configuration.
Property Name | Description |
---|---|
clusternode.selector
|
The fully qualified name of the implementation of
org.jboss.ejb.client.ClusterNodeSelector .
This class, rather than
org.jboss.ejb.clientDeploymentNodeSelector , is used to load balance EJB invocations in a clustered environment. If the cluster is completely down, the invocation will fail with No ejb receiver available .
|
channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES
|
Integer value specifying the maximum number of outbound requests that can be made to the entire cluster.
|
node.NODE_NAME. channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES
|
Integer value specifying the maximum number of outbound requests that can be made to this specific cluster-node.
|
7.6. Container Interceptors
7.6.1. About Container Interceptors
ejb-jar.xml
file for the 3.1 version of the ejb-jar deployment descriptor.
The container interceptors configured for an EJB are guaranteed to be run before the JBoss EAP provided security interceptors, transaction management interceptors, and other server provided interceptors. This allows specific application container interceptors to process or configure relevant context data before the invocation proceeds.
Although container interceptors are modeled to be similar to Java EE interceptors, there are some differences in the semantics of the API. For example, it is illegal for container interceptors to invoke the javax.interceptor.InvocationContext.getTarget()
method because these interceptors are invoked long before the EJB components are setup or instantiated.
7.6.2. Create a Container Interceptor Class
Container interceptor classes are simple Plain Old Java Objects (POJOs). They use the @javax.annotation.AroundInvoke
to mark the method that is invoked during the invocation on the bean.
iAmAround
method for invocation:
Example 7.1. Container Interceptor Class Example
public class ClassLevelContainerInterceptor { @AroundInvoke private Object iAmAround(final InvocationContext invocationContext) throws Exception { return this.getClass().getName() + " " + invocationContext.proceed(); } }
jboss-ejb3.xml
file described here: Section 7.6.3, “Configure a Container Interceptor”.
7.6.3. Configure a Container Interceptor
Container interceptors use the standard Java EE interceptor libraries, meaning they use the same XSD elements that are allowed in ejb-jar.xml
file for the 3.1 version of the ejb-jar deployment descriptor. Because they are based on the standard Jave EE interceptor libraries, container interceptors may only be configured using deployment descriptors. This was done by design so applications would not require any JBoss specific annotation or other library dependencies. For more information about container interceptors, refer to: Section 7.6.1, “About Container Interceptors”.
Procedure 7.11. Create the Descriptor File to Configure the Container Interceptor
- Create a
jboss-ejb3.xml
file in theMETA-INF
directory of the EJB deployment. - Configure the container interceptor elements in the descriptor file.
- Use the
urn:container-interceptors:1.0
namespace to specify configuration of container interceptor elements. - Use the
<container-interceptors>
element to specify the container interceptors. - Use the
<interceptor-binding>
elements to bind the container interceptor to the EJBs. The interceptors can be bound in either of the following ways:- Bind the interceptor to all the EJBs in the deployment using the
*
wildcard. - Bind the interceptor at the individual bean level using the specific EJB name.
- Bind the interceptor at the specific method level for the EJBs.
Note
These elements are configured using the EJB 3.1 XSD in the same way it is done for Java EE interceptors.
- Review the following descriptor file for examples of the above elements.
Example 7.2. jboss-ejb3.xml
<jboss xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:jee="http://java.sun.com/xml/ns/javaee" xmlns:ci ="urn:container-interceptors:1.0"> <jee:assembly-descriptor> <ci:container-interceptors> <!-- Default interceptor --> <jee:interceptor-binding> <ejb-name>*</ejb-name> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ContainerInterceptorOne</interceptor-class> </jee:interceptor-binding> <!-- Class level container-interceptor --> <jee:interceptor-binding> <ejb-name>AnotherFlowTrackingBean</ejb-name> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ClassLevelContainerInterceptor</interceptor-class> </jee:interceptor-binding> <!-- Method specific container-interceptor --> <jee:interceptor-binding> <ejb-name>AnotherFlowTrackingBean</ejb-name> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.MethodSpecificContainerInterceptor</interceptor-class> <method> <method-name>echoWithMethodSpecificContainerInterceptor</method-name> </method> </jee:interceptor-binding> <!-- container interceptors in a specific order --> <jee:interceptor-binding> <ejb-name>AnotherFlowTrackingBean</ejb-name> <interceptor-order> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ClassLevelContainerInterceptor</interceptor-class> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.MethodSpecificContainerInterceptor</interceptor-class> <interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ContainerInterceptorOne</interceptor-class> </interceptor-order> <method> <method-name>echoInSpecificOrderOfContainerInterceptors</method-name> </method> </jee:interceptor-binding> </ci:container-interceptors> </jee:assembly-descriptor> </jboss>
The XSD for theurn:container-interceptors:1.0
namespace is available atEAP_HOME/docs/schema/jboss-ejb-container-interceptors_1_0.xsd
.
7.6.4. Change the Security Context Identity
By default, when you make a remote call to an EJB deployed to the application server, the connection to the server is authenticated and any request received over this connection is executed as the identity that authenticated the connection. This is true for both client-to-server and server-to-server calls. If you need to use different identities from the same client, you normally need to open multiple connections to the server so that each one is authenticated as a different identity. Rather than open multiple client connections, you can give permission to the authenticated user to execute a request as a different user.
ejb-security-interceptors
quickstart for a complete working example.
Procedure 7.12. Change the Identity of the Security Context
Create the client side interceptor
The client side interceptor must implement theorg.jboss.ejb.client.EJBClientInterceptor
interface. The interceptor must pass the requested identity through the context data map, which can be obtained via a call toEJBClientInvocationContext.getContextData()
. The following is an example of client side interceptor code:public class ClientSecurityInterceptor implements EJBClientInterceptor { public void handleInvocation(EJBClientInvocationContext context) throws Exception { Principal currentPrincipal = SecurityActions.securityContextGetPrincipal(); if (currentPrincipal != null) { Map<String, Object> contextData = context.getContextData(); contextData.put(ServerSecurityInterceptor.DELEGATED_USER_KEY, currentPrincipal.getName()); } context.sendRequest(); } public Object handleInvocationResult(EJBClientInvocationContext context) throws Exception { return context.getResult(); } }
User applications can insert the interceptor into the interceptor chain in theEJBClientContext
in one of the following ways:Programmatically
With this approach, you call theorg.jboss.ejb.client.EJBClientContext.registerInterceptor(int order, EJBClientInterceptor interceptor)
method and pass theorder
and theinterceptor
instance. Theorder
determines where this client interceptor is placed in the interceptor chain.ServiceLoader Mechanism
With this approach, you create aMETA-INF/services/org.jboss.ejb.client.EJBClientInterceptor
file and place or package it in the classpath of the client application. The rules for the file are dictated by the Java ServiceLoader Mechanism. This file is expected to contain a separate line for each fully qualified class name of the EJB client interceptor implementation. The EJB client interceptor classes must be available in the classpath. EJB client interceptors added using theServiceLoader
mechanism are added to the end of the client interceptor chain, in the order they are found in the classpath. Theejb-security-interceptors
quickstart uses this approach.
Create and configure the server side container interceptor
Container interceptor classes are simple Plain Old Java Objects (POJOs). They use the@javax.annotation.AroundInvoke
to mark the method that will be invoked during the invocation on the bean. For more information about container interceptors, refer to: Section 7.6.1, “About Container Interceptors”.Create the container interceptor
This interceptor receives theInvocationContext
with the identity and requests the switch to that new identity. The following is an abridged version of the actual code example:public class ServerSecurityInterceptor { private static final Logger logger = Logger.getLogger(ServerSecurityInterceptor.class); static final String DELEGATED_USER_KEY = ServerSecurityInterceptor.class.getName() + ".DelegationUser"; @AroundInvoke public Object aroundInvoke(final InvocationContext invocationContext) throws Exception { Principal desiredUser = null; UserPrincipal connectionUser = null; Map<String, Object> contextData = invocationContext.getContextData(); if (contextData.containsKey(DELEGATED_USER_KEY)) { desiredUser = new SimplePrincipal((String) contextData.get(DELEGATED_USER_KEY)); Collection<Principal> connectionPrincipals = SecurityActions.getConnectionPrincipals(); if (connectionPrincipals != null) { for (Principal current : connectionPrincipals) { if (current instanceof UserPrincipal) { connectionUser = (UserPrincipal) current; break; } } } else { throw new IllegalStateException("Delegation user requested but no user on connection found."); } } ContextStateCache stateCache = null; try { if (desiredUser != null && connectionUser != null && (desiredUser.getName().equals(connectionUser.getName()) == false)) { // The final part of this check is to verify that the change does actually indicate a change in user. try { // We have been requested to use an authentication token // so now we attempt the switch. stateCache = SecurityActions.pushIdentity(desiredUser, new OuterUserCredential(connectionUser)); } catch (Exception e) { logger.error("Failed to switch security context for user", e); // Don't propagate the exception stacktrace back to the client for security reasons throw new EJBAccessException("Unable to attempt switching of user."); } } return invocationContext.proceed(); } finally { // switch back to original context if (stateCache != null) { SecurityActions.popIdentity(stateCache);; } } }
Configure the container interceptor
For information on how to configure server side container interceptors, refer to: Section 7.6.3, “Configure a Container Interceptor”.
Create the JAAS LoginModule
This component is responsible for verifying that user is allowed to execute requests as the requested identity. The following abridged code examples show the methods that peform the login and validation:@SuppressWarnings("unchecked") @Override public boolean login() throws LoginException { if (super.login() == true) { log.debug("super.login()==true"); return true; } // Time to see if this is a delegation request. NameCallback ncb = new NameCallback("Username:"); ObjectCallback ocb = new ObjectCallback("Password:"); try { callbackHandler.handle(new Callback[] { ncb, ocb }); } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } return false; // If the CallbackHandler can not handle the required callbacks then no chance. } String name = ncb.getName(); Object credential = ocb.getCredential(); if (credential instanceof OuterUserCredential) { // This credential type will only be seen for a delegation request, if not seen then the request is not for us. if (delegationAcceptable(name, (OuterUserCredential) credential)) { identity = new SimplePrincipal(name); if (getUseFirstPass()) { String userName = identity.getName(); if (log.isDebugEnabled()) log.debug("Storing username '" + userName + "' and empty password"); // Add the username and an empty password to the shared state map sharedState.put("javax.security.auth.login.name", identity); sharedState.put("javax.security.auth.login.password", ""); } loginOk = true; return true; } } return false; // Attempted login but not successful. } protected boolean delegationAcceptable(String requestedUser, OuterUserCredential connectionUser) { if (delegationMappings == null) { return false; } String[] allowedMappings = loadPropertyValue(connectionUser.getName(), connectionUser.getRealm()); if (allowedMappings.length == 1 && "*".equals(allowedMappings[1])) { // A wild card mapping was found. return true; } for (String current : allowedMappings) { if (requestedUser.equals(current)) { return true; } } return false; }
ejb-security-interceptors
quickstart README.html
file for complete instructions and more detailed information about the code.
7.6.5. Pass Additional Security For EJB Authentication
By default, when you make a remote call to an EJB deployed to the application server, the connection to the server is authenticated and any request received over this connection is executed using the credentials that authenticated the connection. Authentication at the connection level is dependent on the capabilities of the underlying SASL (Simple Authentication and Security Layer) mechanisms. Rather than write custom SASL mechanisms, you can open and authenticate a connection to the server, then later add additional security tokens prior to invoking an EJB. This topic describes how to pass additional information on the existing client connection for EJB authentication.
Procedure 7.13. Pass Security Information for EJB Authentication
Create the client side interceptor
This interceptor must implement theorg.jboss.ejb.client.EJBClientInterceptor
. The interceptor is expected to pass the additional security token through the context data map, which can be obtained via a call toEJBClientInvocationContext.getContextData()
. The following is an example of client side interceptor code that creates an additional security token:public class ClientSecurityInterceptor implements EJBClientInterceptor { public void handleInvocation(EJBClientInvocationContext context) throws Exception { Principal currentPrincipal = SecurityActions.securityContextGetPrincipal(); if (currentPrincipal != null) { Map<String, Object> contextData = context.getContextData(); contextData.put(ServerSecurityInterceptor.DELEGATED_USER_KEY, currentPrincipal.getName()); } context.sendRequest(); } public Object handleInvocationResult(EJBClientInvocationContext context) throws Exception { return context.getResult(); } }
For information on how to plug the client interceptor into an application, refer to Section 7.6.6, “Use a Client Side Interceptor in an Application”.Create and configure the server side container interceptor
Container interceptor classes are simple Plain Old Java Objects (POJOs). They use the@javax.annotation.AroundInvoke
to mark the method that is invoked during the invocation on the bean. For more information about container interceptors, refer to: Section 7.6.1, “About Container Interceptors”.Create the container interceptor
This interceptor retrieves the security authentication token from the context and passes it to the JAAS (Java Authentication and Authorization Service) domain for verification. The following is an example of container interceptor code:public class ServerSecurityInterceptor { private static final Logger logger = Logger.getLogger(ServerSecurityInterceptor.class); static final String DELEGATED_USER_KEY = ServerSecurityInterceptor.class.getName() + ".DelegationUser"; @AroundInvoke public Object aroundInvoke(final InvocationContext invocationContext) throws Exception { Principal desiredUser = null; UserPrincipal connectionUser = null; Map<String, Object> contextData = invocationContext.getContextData(); if (contextData.containsKey(DELEGATED_USER_KEY)) { desiredUser = new SimplePrincipal((String) contextData.get(DELEGATED_USER_KEY)); Collection<Principal> connectionPrincipals = SecurityActions.getConnectionPrincipals(); if (connectionPrincipals != null) { for (Principal current : connectionPrincipals) { if (current instanceof UserPrincipal) { connectionUser = (UserPrincipal) current; break; } } } else { throw new IllegalStateException("Delegation user requested but no user on connection found."); } } ContextStateCache stateCache = null; try { if (desiredUser != null && connectionUser != null && (desiredUser.getName().equals(connectionUser.getName()) == false)) { // The final part of this check is to verify that the change does actually indicate a change in user. try { // We have been requested to use an authentication token // so now we attempt the switch. stateCache = SecurityActions.pushIdentity(desiredUser, new OuterUserCredential(connectionUser)); } catch (Exception e) { logger.error("Failed to switch security context for user", e); // Don't propagate the exception stacktrace back to the client for security reasons throw new EJBAccessException("Unable to attempt switching of user."); } } return invocationContext.proceed(); } finally { // switch back to original context if (stateCache != null) { SecurityActions.popIdentity(stateCache);; } } }
Configure the container interceptor
For information on how to configure server side container interceptors, refer to: Section 7.6.3, “Configure a Container Interceptor”.
Create the JAAS LoginModule
This custom module performs the authentication using the existing authenticated connection information plus any additional security token. The following is a shortened example of code that uses the additional security token and performs the authentication. The complete code example can be viewed in theejb-security-interceptors
quickstart that ships with JBoss EAP 6.3 or later.public class DelegationLoginModule extends AbstractServerLoginModule { private static final String DELEGATION_PROPERTIES = "delegationProperties"; private static final String DEFAULT_DELEGATION_PROPERTIES = "delegation-mapping.properties"; private Properties delegationMappings; private Principal identity; @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { addValidOptions(new String[] { DELEGATION_PROPERTIES }); super.initialize(subject, callbackHandler, sharedState, options); String propertiesName; if (options.containsKey(DELEGATION_PROPERTIES)) { propertiesName = (String) options.get(DELEGATION_PROPERTIES); } else { propertiesName = DEFAULT_DELEGATION_PROPERTIES; } try { delegationMappings = loadProperties(propertiesName); } catch (IOException e) { throw new IllegalArgumentException(String.format("Unable to load properties '%s'", propertiesName), e); } } @SuppressWarnings("unchecked") @Override public boolean login() throws LoginException { if (super.login() == true) { log.debug("super.login()==true"); return true; } // Time to see if this is a delegation request. NameCallback ncb = new NameCallback("Username:"); ObjectCallback ocb = new ObjectCallback("Password:"); try { callbackHandler.handle(new Callback[] { ncb, ocb }); } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } return false; // If the CallbackHandler can not handle the required callbacks then no chance. } String name = ncb.getName(); Object credential = ocb.getCredential(); if (credential instanceof OuterUserCredential) { // This credential type will only be seen for a delegation request, if not seen then the request is not for us. if (delegationAcceptable(name, (OuterUserCredential) credential)) { identity = new SimplePrincipal(name); if (getUseFirstPass()) { String userName = identity.getName(); if (log.isDebugEnabled()) log.debug("Storing username '" + userName + "' and empty password"); // Add the username and an empty password to the shared state map sharedState.put("javax.security.auth.login.name", identity); sharedState.put("javax.security.auth.login.password", ""); } loginOk = true; return true; } } return false; // Attempted login but not successful. }
Add the Custom LoginModule to the Chain
You must add the new custom LoginModule to the correct location the chain so that it is invoked in the correct order. In this example, theSaslPlusLoginModule
must be chained before the LoginModule that loads the roles with thepassword-stacking
option set.Configure the LoginModule Order using the Management CLI
The following is an example of Management CLI commands that chain the customSaslPlusLoginModule
before theRealmDirect
LoginModule that sets thepassword-stacking
option./subsystem=security/security-domain=quickstart-domain:add(cache-type=default)
/subsystem=security/security-domain=quickstart-domain/authentication=classic:add
/subsystem=security/security-domain=quickstart-domain/authentication=classic/login-module=DelegationLoginModule:add(code=org.jboss.as.quickstarts.ejb_security_plus.SaslPlusLoginModule,flag=optional,module-options={password-stacking=useFirstPass})
/subsystem=security/security-domain=quickstart-domain/authentication=classic/login-module=RealmDirect:add(code=RealmDirect,flag=required,module-options={password-stacking=useFirstPass})
Configure the LoginModule Order Manually
The following is an example of XML that configures the LoginModule order in thesecurity
subsystem of the server configuration file. The customSaslPlusLoginModule
must precede theRealmDirect
LoginModule so that it can verify the remote user before the user roles are loaded and thepassword-stacking
option is set.<security-domain name="quickstart-domain" cache-type="default"> <authentication> <login-module code="org.jboss.as.quickstarts.ejb_security_plus.SaslPlusLoginModule" flag="required"> <module-option name="password-stacking" value="useFirstPass"/> </login-module> <login-module code="RealmDirect" flag="required"> <module-option name="password-stacking" value="useFirstPass"/> </login-module> </authentication> </security-domain>
Create the Remote Client
In the following code example, assume theadditional-secret.properties
file accessed by the JAAS LoginModule above contains the following property:quickstartUser=7f5cc521-5061-4a5b-b814-bdc37f021acc
The following code demonstrates how create the security token and set it before the EJB call. The secret token is hard-coded for demonstration purposes only. This client simply prints the results to the console.import static org.jboss.as.quickstarts.ejb_security_plus.EJBUtil.lookupSecuredEJB; public class RemoteClient { /** * @param args */ public static void main(String[] args) throws Exception { SimplePrincipal principal = new SimplePrincipal("quickstartUser"); Object credential = new PasswordPlusCredential("quickstartPwd1!".toCharArray(), "7f5cc521-5061-4a5b-b814-bdc37f021acc"); SecurityActions.securityContextSetPrincipalCredential(principal, credential); SecuredEJBRemote secured = lookupSecuredEJB(); System.out.println(secured.getPrincipalInformation()); } }
7.6.6. Use a Client Side Interceptor in an Application
You can plug a client-side interceptor into an application programmatically or using a ServiceLoader mechanism. The following procedure describes the two methods.
Procedure 7.14. Plug the Interceptor into
Programmatically
With this approach, you call theorg.jboss.ejb.client.EJBClientContext.registerInterceptor(int order, EJBClientInterceptor interceptor)
API and pass theorder
and theinterceptor
instance. Theorder
is used to determine where exactly in the client interceptor chain thisinterceptor
is placed.ServiceLoader Mechanism
With this approach, you create aMETA-INF/services/org.jboss.ejb.client.EJBClientInterceptor
file and place or package it in the classpath of the client application. The rules for the file are dictated by the Java ServiceLoader Mechanism. This file is expected to contain a separate line for each fully qualified class name of the EJB client interceptor implementation. The EJB client interceptor classes must be available in the classpath. EJB client interceptors added using theServiceLoader
mechanism are added to the end of the client interceptor chain, in the order they are found in the classpath. Theejb-security-interceptors
quickstart uses this approach.
7.7. Clustered Enterprise JavaBeans
7.7.1. About Clustered Enterprise JavaBeans (EJBs)
Note
7.7.2. Standalone and In-server Client Configuration
jboss-ejb-client.properties
for standalone EJB client, or even jboss-ejb-client.xml
file for a server-side application must be expanded to include a cluster configuration.
jboss-ejb-client.xml
file) shows the complete configuration:
Example 7.3. Standalone client with jboss-ejb-client.properties
configuration
remote.clusters=ejb remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED=false remote.cluster.ejb.username=test remote.cluster.ejb.password=password
jboss-ejb-client.xml
file and add cluster configuration as shown in the following example:
Example 7.4. Client application which is deployed in another EAP 6 instance (Configuring jboss-ejb-client.xml file)
<jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.2" xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_2.xsd"> <client-context> <ejb-receivers> <!-- this is the connection to access the app-one --> <remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection-1" /> <!-- this is the connection to access the app-two --> <remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection-2" /> </ejb-receivers> <!-- if an outbound connection connects to a cluster; a list of members is provided after successful connection. To connect to this node this cluster element must be defined. --> <clusters> <!-- cluster of remote-ejb-connection-1 --> <cluster name="ejb" security-realm="ejb-security-realm-1" username="quickuser1"> <connection-creation-options> <property name="org.xnio.Options.SSL_ENABLED" value="false" /> <property name="org.xnio.Options.SASL_POLICY_NOANONYMOUS" value="false" /> </connection-creation-options> </cluster> </clusters> </client-context> </jboss-ejb-client>
Note
7.7.3. Implementing a Custom Load Balancing Policy for EJB Calls
AllClusterNodeSelector
for EJB calls. The node selection behavior of AllClusterNodeSelector
is similar to default selector except that AllClusterNodeSelector
uses all available cluster nodes even in case of a large cluster (number of nodes>20). If an unconnected cluster node is returned it is opened automatically. The following example shows AllClusterNodeSelector
implementation:
package org.jboss.as.quickstarts.ejb.clients.selector; import java.util.Arrays; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; import org.jboss.ejb.client.ClusterNodeSelector; public class AllClusterNodeSelector implements ClusterNodeSelector { private static final Logger LOGGER = Logger.getLogger(AllClusterNodeSelector.class.getName()); @Override public String selectNode(final String clusterName, final String[] connectedNodes, final String[] availableNodes) { if(LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("INSTANCE "+this+ " : cluster:"+clusterName+" connected:"+Arrays.deepToString(connectedNodes)+" available:"+Arrays.deepToString(availableNodes)); } if (availableNodes.length == 1) { return availableNodes[0]; } final Random random = new Random(); final int randomSelection = random.nextInt(availableNodes.length); return availableNodes[randomSelection]; } }You can also implement the
SimpleLoadFactorNodeSelector
for EJB calls. Load balancing in SimpleLoadFactorNodeSelector
happens based on a load factor. The load factor (2/3/4) is calculated based on the names of nodes (A/B/C) irrespective of the load on each node. The following example shows SimpleLoadFactorNodeSelector
implementation:
package org.jboss.as.quickstarts.ejb.clients.selector; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.jboss.ejb.client.DeploymentNodeSelector; public class SimpleLoadFactorNodeSelector implements DeploymentNodeSelector { private static final Logger LOGGER = Logger.getLogger(SimpleLoadFactorNodeSelector.class.getName()); private final Map<String, List<String>[]> nodes = new HashMap<String, List<String>[]>(); private final Map<String, Integer> cursor = new HashMap<String, Integer>(); private ArrayList<String> calculateNodes(Collection<String> eligibleNodes) { ArrayList<String> nodeList = new ArrayList<String>(); for (String string : eligibleNodes) { if(string.contains("A") || string.contains("2")) { nodeList.add(string); nodeList.add(string); } else if(string.contains("B") || string.contains("3")) { nodeList.add(string); nodeList.add(string); nodeList.add(string); } else if(string.contains("C") || string.contains("4")) { nodeList.add(string); nodeList.add(string); nodeList.add(string); nodeList.add(string); } } return nodeList; } @SuppressWarnings("unchecked") private void checkNodeNames(String[] eligibleNodes, String key) { if(!nodes.containsKey(key) || nodes.get(key)[0].size() != eligibleNodes.length || !nodes.get(key)[0].containsAll(Arrays.asList(eligibleNodes))) { // must be synchronized as the client might call it concurrent synchronized (nodes) { if(!nodes.containsKey(key) || nodes.get(key)[0].size() != eligibleNodes.length || !nodes.get(key)[0].containsAll(Arrays.asList(eligibleNodes))) { ArrayList<String> nodeList = new ArrayList<String>(); nodeList.addAll(Arrays.asList(eligibleNodes)); nodes.put(key, new List[] { nodeList, calculateNodes(nodeList) }); } } } } private synchronized String nextNode(String key) { Integer c = cursor.get(key); List<String> nodeList = nodes.get(key)[1]; if(c == null || c >= nodeList.size()) { c = Integer.valueOf(0); } String node = nodeList.get(c); cursor.put(key, Integer.valueOf(c + 1)); return node; } @Override public String selectNode(String[] eligibleNodes, String appName, String moduleName, String distinctName) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("INSTANCE " + this + " : nodes:" + Arrays.deepToString(eligibleNodes) + " appName:" + appName + " moduleName:" + moduleName + " distinctName:" + distinctName); } // if there is only one there is no sense to choice if (eligibleNodes.length == 1) { return eligibleNodes[0]; } final String key = appName + "|" + moduleName + "|" + distinctName; checkNodeNames(eligibleNodes, key); return nextNode(key); } }
jboss-ejb-client.properties
You need to add the property remote.cluster.ejb.clusternode.selector
with the name of your implementation class (AllClusterNodeSelector
or SimpleLoadFactorNodeSelector
). The selector will see all configured servers which are available at the invocation time. The following example uses AllClusterNodeSelector
as the deployment node selector:
remote.clusters=ejb remote.cluster.ejb.clusternode.selector=org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED=false remote.cluster.ejb.username=test remote.cluster.ejb.password=password remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false remote.connections=one,two remote.connection.one.host=localhost remote.connection.one.port = 4447 remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false remote.connection.one.username=user remote.connection.one.password=user123 remote.connection.two.host=localhost remote.connection.two.port = 4547 remote.connection.two.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
You need to add the property remote.cluster.ejb.clusternode.selector
to the list for the PropertiesBasedEJBClientConfiguration
constructor. The following example uses AllClusterNodeSelector
as the deployment node selector:
Properties p = new Properties(); p.put("remote.clusters", "ejb"); p.put("remote.cluster.ejb.clusternode.selector", "org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector"); p.put("remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false"); p.put("remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED", "false"); p.put("remote.cluster.ejb.username", "test"); p.put("remote.cluster.ejb.password", "password"); p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false"); p.put("remote.connections", "one,two"); p.put("remote.connection.one.port", "4447"); p.put("remote.connection.one.host", "localhost"); p.put("remote.connection.two.port", "4547"); p.put("remote.connection.two.host", "localhost"); EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(p); ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc); EJBClientContext.setSelector(selector); p = new Properties(); p.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); InitialContext context = new InitialContext(p);
jboss-ejb-client.xml
To use the load balancing policy for server to server communication; package the class together with the application and configure it within the jboss-ejb-client.xml
settings(located in META-INF
folder). The following example uses AllClusterNodeSelector
as the deployment node selector:
<jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.2" xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_2.xsd"> <client-context> <ejb-receivers> <!-- this is the connection to access the app --> <remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection-1" /> </ejb-receivers> <!-- if an outbound connection connect to a cluster a list of members is provided after successful connection. To connect to this node this cluster element must be defined. --> <clusters> <!-- cluster of remote-ejb-connection-1 --> <cluster name="ejb" security-realm="ejb-security-realm-1" username="test" cluster-node-selector="org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector"> <connection-creation-options> <property name="org.xnio.Options.SSL_ENABLED" value="false" /> <property name="org.xnio.Options.SASL_POLICY_NOANONYMOUS" value="false" /> </connection-creation-options> </cluster> </clusters> </client-context> </jboss-ejb-client>To use the above configuration with security, you will need to add
ejb-security-realm-1
to client-server configuration. The following example shows the CLI commands for adding security realm (ejb-security-realm-1
) the value is the base64 encoded password for the user "test"":
core-service=management/security-realm=ejb-security-realm-1:add() core-service=management/security-realm=ejb-security-realm-1/server-identity=secret:add(value=cXVpY2sxMjMr)
Note
-Djboss.node.name=
or the server configuration file standalone.xml
to configure the server name (server name=""). Ensure that the server name is unique. In domain mode, the controller automatically validates that the names are unique.
7.7.4. Transaction Behavior of EJB Invocations
Transaction attributes for distributed EAP applications need to be handled in a way as if the application is called on the same server. To discontinue a transaction, the destination method must be marked REQUIRES_NEW
using different interfaces.
Note
To invoke EJB session beans with an EAP 6 standalone client, the client must have a reference to the InitialContext
object while the EJB proxies or UserTransaction
are used. It is also important to keep the InitialContext
object open while EJB proxies or UserTransaction
are being used. Control of the connections will be inside the classes created by the InitialContext
with the properties.
InitialContext
object:
package org.jboss.as.quickstarts.ejb.multi.server; import java.util.Date; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import javax.naming.Context; import javax.naming.InitialContext; import org.jboss.as.quickstarts.ejb.multi.server.app.MainApp; import org.jboss.ejb.client.ContextSelector; import org.jboss.ejb.client.EJBClientConfiguration; import org.jboss.ejb.client.EJBClientContext; import org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration; import org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector; public class Client { /** * @param args no args needed * @throws Exception */ public static void main(String[] args) throws Exception { // suppress output of client messages Logger.getLogger("org.jboss").setLevel(Level.OFF); Logger.getLogger("org.xnio").setLevel(Level.OFF); Properties p = new Properties(); p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false"); p.put("remote.connections", "one"); p.put("remote.connection.one.port", "4447"); p.put("remote.connection.one.host", "localhost"); p.put("remote.connection.one.username", "quickuser"); p.put("remote.connection.one.password", "quick-123"); EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(p); ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc); EJBClientContext.setSelector(selector); Properties props = new Properties(); props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); InitialContext context = new InitialContext(props); final String rcal = "ejb:jboss-ejb-multi-server-app-main/ejb//" + ("MainAppBean") + "!" + MainApp.class.getName(); final MainApp remote = (MainApp) context.lookup(rcal); final String result = remote.invokeAll("Client call at "+new Date()); System.out.println("InvokeAll succeed: "+result); } }
Note
UserTransaction
reference on the client is unsupported for scenarios with a scoped EJB client context and for invocations which use the remote-naming
protocol. This is because in these scenarios, InitialContext
encapsulates its own EJB client context instance; which cannot be accessed using the static methods of the EJBClient
class. When EJBClient.getUserTransaction()
is called, it returns a transaction from default (global) EJB client context (which might not be initialized) and not from the desired one.
The following example shows how to get UserTransaction
reference on a standalone client:
import org.jboss.ejb.client.EJBClient; import javax.transaction.UserTransaction; . . Context context=null; UserTransaction tx=null; try { Properties props = new Properties(); // REMEMBER: there must be a jboss-ejb-client.properties with the connection parameter // in the clients classpath props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); context = new InitialContext(props); System.out.println("\n\tGot initial Context: "+context); tx=EJBClient.getUserTransaction("yourServerName"); System.out.println("UserTransaction = "+tx.getStatus()); tx.begin(); // do some work ... }catch (Exception e) { e.printStackTrace(); tx.rollback(); }finally{ if(context != null) { context.close(); } }
Note
UserTransaction
reference on the client side; start your server with the following system property -Djboss.node.name=yourServerName
and then use it on client side as following:
tx=EJBClient.getUserTransaction("yourServerName");Replace "yourServerName" with the name of your server. If a user transaction is started on a node all invocations are sticky on the node and the node must have all the needed EJBs. It is not possible to use
UserTransaction
with remote-naming protocol and scoped-context.
7.8. Reference
7.8.1. EJB JNDI Naming Reference
ejb:<appName>/<moduleName>/<distinctName>/<beanName>!<viewClassName>?stateful
<appName>
- If the session bean's JAR file has been deployed within an enterprise archive (EAR) then this is the name of that EAR. By default, the name of an EAR is its filename without the
.ear
suffix. The application name can also be overridden in itsapplication.xml
file. If the session bean is not deployed in an EAR then leave this blank. <moduleName>
- The module name is the name of the JAR file that the session bean is deployed in. By the default, the name of the JAR file is its filename without the
.jar
suffix. The module name can also be overridden in the JAR'sejb-jar.xml
file. <distinctName>
- JBoss EAP 6 allows each deployment to specify an optional distinct name. If the deployment does not have a distinct name then leave this blank.
<beanName>
- The bean name is the classname of the session bean to be invoked.
<viewClassName>
- The view class name is the fully qualified classname of the remote interface. This includes the package name of the interface.
?stateful
- The
?stateful
suffix is required when the JNDI name refers to a stateful session bean. It is not included for other bean types.
7.8.2. EJB Reference Resolution
@EJB
and @Resource
. Please note that XML always overrides annotations but the same rules apply.
Rules for the @EJB annotation
- The
@EJB
annotation also has amappedName()
attribute. The specification leaves this as vendor specific metadata, but JBoss recognizesmappedName()
as the global JNDI name of the EJB you are referencing. If you have specified amappedName()
, then all other attributes are ignored and this global JNDI name is used for binding. - If you specify
@EJB
with no attributes defined:@EJB ProcessPayment myEjbref;
Then the following rules apply:- The EJB jar of the referencing bean is searched for an EJB with the interface used in the
@EJB
injection. If there are more than one EJB that publishes same business interface, then an exception is thrown. If there is only one bean with that interface then that one is used. - Search the EAR for EJBs that publish that interface. If there are duplicates, then an exception is thrown. Otherwise the matching bean is returned.
- Search globally in JBoss runtime for an EJB of that interface. Again, if duplicates are found, an exception is thrown.
@EJB.beanName()
corresponds to<ejb-link>
. If thebeanName()
is defined, then use the same algorithm as@EJB
with no attributes defined except use thebeanName()
as a key in the search. An exception to this rule is if you use the ejb-link '#' syntax. The '#' syntax allows you to put a relative path to a jar in the EAR where the EJB you are referencing is located. Refer to the EJB 3.1 specification for more details.
7.8.3. Project dependencies for Remote EJB Clients
GroupID | ArtifactID |
---|---|
org.jboss.spec | jboss-javaee-6.0 |
org.jboss.as | jboss-as-ejb-client-bom |
org.jboss.spec.javax.transaction | jboss-transaction-api_1.1_spec |
org.jboss.spec.javax.ejb | jboss-ejb-api_3.1_spec |
org.jboss | jboss-ejb-client |
org.jboss.xnio | xnio-api |
org.jboss.xnio | xnio-nio |
org.jboss.remoting3 | jboss-remoting |
org.jboss.sasl | jboss-sasl |
org.jboss.marshalling | jboss-marshalling-river |
jboss-javaee-6.0
and jboss-as-ejb-client-bom
, these dependencies must be added to the <dependencies>
section of the pom.xml
file.
jboss-javaee-6.0
and jboss-as-ejb-client-bom
dependencies should be added to the <dependencyManagement>
section of your pom.xml
with the scope of import
.
Note
artifactID
's versions are subject to change. Refer to the Maven repository for the relevant version.
<dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>3.0.0.Final-redhat-1</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-ejb-client-bom</artifactId> <version>7.1.1.Final-redhat-1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
ejb-remote/client/pom.xml
in the quickstart files for a complete example of dependency configuration for remote session bean invocation.
7.8.4. jboss-ejb3.xml Deployment Descriptor Reference
jboss-ejb3.xml
is a custom deployment descriptor that can be used in either EJB JAR or WAR archives. In an EJB JAR archive it must be located in the META-INF/
directory. In a WAR archive it must be located in the WEB-INF/
directory.
ejb-jar.xml
, using some of the same namespaces and providing some other additional namespaces. The contents of jboss-ejb3.xml
are merged with the contents of ejb-jar.xml
, with the jboss-ejb3.xml
items taking precedence.
jboss-ejb3.xml
. Refer to http://java.sun.com/xml/ns/javaee/ for documentation on the standard namespaces.
http://www.jboss.com/xml/ns/javaee
.
Assembly descriptor namespaces
<assembly-descriptor>
element. They can be used to apply their configuration to a single bean, or to all beans in the deployment by using \*
as the ejb-name
.
- The clustering namespace:
urn:clustering:1.0
xmlns:c="urn:clustering:1.0"
This allows you to mark EJB's as clustered. It is the deployment descriptor equivalent to@org.jboss.ejb3.annotation.Clustered
.<c:clustering> <ejb-name>DDBasedClusteredSFSB</ejb-name> <c:clustered>true</c:clustered> </c:clustering>
- The security namespace (
urn:security
) xmlns:s="urn:security"
This allows you to set the security domain and the run-as principal for an EJB.<s:security> <ejb-name>*</ejb-name> <s:security-domain>myDomain</s:security-domain> <s:run-as-principal>myPrincipal</s:run-as-principal> </s:security>
- The resource adapter namespace:
urn:resource-adapter-binding
xmlns:r="urn:resource-adapter-binding"
This allows you to set the resource adapter for a Message-Driven Bean.<r:resource-adapter-binding> <ejb-name>*</ejb-name> <r:resource-adapter-name>myResourceAdapter</r:resource-adapter-name> </r:resource-adapter-binding>
- The IIOP namespace:
urn:iiop
xmlns:u="urn:iiop"
The IIOP namespace is where IIOP settings are configured.- The pool namespace:
urn:ejb-pool:1.0
xmlns:p="urn:ejb-pool:1.0"
This allows you to select the pool that is used by the included stateless session beans or Message-Driven Beans. Pools are defined in the server configuration.<p:pool> <ejb-name>*</ejb-name> <p:bean-instance-pool-ref>my-pool</p:bean-instance-pool-ref> </p:pool>
- The cache namespace:
urn:ejb-cache:1.0
xmlns:c="urn:ejb-cache:1.0"
This allows you to select the cache that is used by the included stateful session beans. Caches are defined in the server configuration.<c:cache> <ejb-name>*</ejb-name> <c:cache-ref>my-cache</c:cache-ref> </c:cache>
Example 7.5. Example jboss-ejb3.xml file
<?xml version="1.1" encoding="UTF-8"?> <jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="urn:clustering:1.0" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd" version="3.1" impl-version="2.0"> <enterprise-beans> <message-driven> <ejb-name>ReplyingMDB</ejb-name> <ejb-class>org.jboss.as.test.integration.ejb.mdb.messagedestination.ReplyingMDB</ejb-class> <activation-config> <activation-config-property> <activation-config-property-name>destination</activation-config-property-name> <activation-config-property-value>java:jboss/mdbtest/messageDestinationQueue </activation-config-property-value> </activation-config-property> </activation-config> </message-driven> </enterprise-beans> <assembly-descriptor> <c:clustering> <ejb-name>DDBasedClusteredSFSB</ejb-name> <c:clustered>true</c:clustered> </c:clustering> </assembly-descriptor> </jboss:ejb-jar>
Chapter 8. JBoss MBean Services
8.1. Writing JBoss MBean Services
create
, start
, stop
, and destroy
itself.
- If you want specific methods to be called on your MBean, declare those methods in your MBean interface. This approach allows your MBean implementation to avoid dependencies on JBoss specific classes
- If you are not bothered about dependencies on JBoss specific classes then you may have your MBean interface extend the
ServiceMBean
interface andServiceMBeanSupport
class. TheServiceMBeanSupport
class provides implementations of the service lifecycle methods likecreate
,start
andstop
. To handle a specific event like thestart()
event, you need to overridestartService()
method provided by the ServiceMBeanSupport class.
8.2. A Standard MBean Example
.sar
).
ConfigServiceMBean
interface declares specific methods like the start
, getTimeout
and stop
methods to start
, hold
and stop
the MBean correctly without using any JBoss specific classes. ConfigService
class implements ConfigServiceMBean
interface and consequently implements the methods used within that interface.
PlainThread
class extends ServiceMBeanSupport
class and implements PlainThreadMBean
interface. PlainThread
starts a thread and uses ConfigServiceMBean.getTimeout()
to determine how long the thread should sleep.
Example 8.1. Sample MBean services
package org.jboss.example.mbean.support; public interface ConfigServiceMBean { int getTimeout(); void start(); void stop(); } package org.jboss.example.mbean.support; public class ConfigService implements ConfigServiceMBean { int timeout; @Override public int getTimeout() { return timeout; } @Override public void start() { //Create a random number between 3000 and 6000 milliseconds timeout = (int)Math.round(Math.random() * 3000) + 3000; System.out.println("Random timeout set to " + timeout + " seconds"); } @Override public void stop() { timeout = 0; } } package org.jboss.example.mbean.support; import org.jboss.system.ServiceMBean; public interface PlainThreadMBean extends ServiceMBean { void setConfigService(ConfigServiceMBean configServiceMBean); } package org.jboss.example.mbean.support; import org.jboss.system.ServiceMBeanSupport; public class PlainThread extends ServiceMBeanSupport implements PlainThreadMBean { private ConfigServiceMBean configService; private Thread thread; private volatile boolean done; @Override public void setConfigService(ConfigServiceMBean configService) { this.configService = configService; } @Override protected void startService() throws Exception { System.out.println("Starting Plain Thread MBean"); done = false; thread = new Thread(new Runnable() { @Override public void run() { try { while (!done) { System.out.println("Sleeping...."); Thread.sleep(configService.getTimeout()); System.out.println("Slept!"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); thread.start(); } @Override protected void stopService() throws Exception { System.out.println("Stopping Plain Thread MBean"); done = true; } }
jboss-service.xml
descriptor shows how ConfigService
class is injected into PlainThread
class using inject
tag. The inject
tag establishes a dependency between PlainThreadMBean
and ConfigServiceMBean
and thus allows PlainThreadMBean
use ConfigServiceMBean
easily.
Example 8.2. JBoss-service.xml Service Descriptor
<server xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:service:7.0 jboss-service_7_0.xsd" xmlns="urn:jboss:service:7.0"> <mbean code="org.jboss.example.mbean.support.ConfigService" name="jboss.support:name=ConfigBean"/> <mbean code="org.jboss.example.mbean.support.PlainThread" name="jboss.support:name=ThreadBean"> <attribute name="configService"> <inject bean="jboss.support:name=ConfigBean"/> </attribute> </mbean> </server>
jboss-service.xml
descriptor in the META-INF
folder of a service archive (.sar
).
8.3. Deploying JBoss MBean Services
ServiceMBeanTest.sar
) in Domain
mode use the following commands:
[domain@localhost:9999 /] deploy ~/Desktop/ServiceMBeanTest.sar
[domain@localhost:9999 /] deploy ~/Desktop/ServiceMBeanTest.sar --all-server-groups
ServiceMBeanTest.sar
) in Standalone
mode use the following command:
[standalone@localhost:9999 /] deploy ~/Desktop/ServiceMBeanTest.sar
[standalone@localhost:9999 /] undeploy ServiceMBeanTest.sar
Chapter 9. Clustering in Web Applications
9.1. Session Replication
9.1.1. About HTTP Session Replication
9.1.2. About the Web Session Cache
standalone-ha.xml
profile, or the managed domain profiles ha
or full-ha
. The most commonly configured elements are the cache mode and the number of cache owners for a distributed cache.
The cache mode can either be REPL
(the default) or DIST
.
- REPL
- The
REPL
mode replicates the entire cache to every other node in the cluster. This is the safest option, but introduces more overhead. - DIST
- The
DIST
mode is similar to the buddy mode provided in previous implementations. It reduces overhead by distributing the cache to the number of nodes specified in theowners
parameter. This number of owners defaults to2
.
The owners
parameter controls how many cluster nodes hold replicated copies of the session. The default is 2
.
9.1.3. Configure the Web Session Cache
REPL
. If you wish to use DIST
mode, run the following two commands in the Management CLI. If you use a different profile, change the profile name in the commands. If you use a standalone server, remove the /profile=ha
portion of the commands.
Procedure 9.1. Configure the Web Session Cache
Change the default cache mode to
DIST
./profile=ha/subsystem=infinispan/cache-container=web/:write-attribute(name=default-cache,value=dist)
Set the number of owners for a distributed cache.
The following command sets5
owners. The default is2
./profile=ha/subsystem=infinispan/cache-container=web/distributed-cache=dist/:write-attribute(name=owners,value=5)
Change the default cache mode back to
REPL
./profile=ha/subsystem=infinispan/cache-container=web/:write-attribute(name=default-cache,value=repl)
Restart the Server
After changing the web cache mode, you must restart the server.
Your server is configured for session replication. To use session replication in your own applications, refer to the following topic: Section 9.1.4, “Enable Session Replication in Your Application”.
9.1.4. Enable Session Replication in Your Application
To take advantage of JBoss EAP 6 High Availability (HA) features, you must configure your application to be distributable. This procedure shows how to do that, and then explains some of the advanced configuration options you can use.
Procedure 9.2. Make your Application Distributable
Required: Indicate that your application is distributable.
If your application is not marked as distributable, its sessions will never be distributed. Add the<distributable/>
element inside the<web-app>
tag of your application'sweb.xml
descriptor file. Here is an example.Example 9.1. Minimum Configuration for a Distributable Application
<?xml version="1.0"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <distributable/> </web-app>
Modify the default replication behavior if desired.
If you want to change any of the values affecting session replication, you can override them inside a<replication-config>
element which is a child element of the<jboss-web>
element of your application'sjboss-web.xml
file. For a given element, only include it if you want to override the defaults. The following example lists all of the default settings, and is followed by a table which explains the most commonly changed options.Example 9.2. Default
<replication-config>
Values<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 5.0//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd"> <jboss-web> <replication-config> <cache-name>custom-session-cache</cache-name> <replication-trigger>SET</replication-trigger> <replication-granularity>ATTRIBUTE</replication-granularity> <use-jk>false</use-jk> <max-unreplicated-interval>30</max-unreplicated-interval> <snapshot-mode>INSTANT</snapshot-mode> <snapshot-interval>1000</snapshot-interval> <session-notification-policy>com.example.CustomSessionNotificationPolicy</session-notification-policy> </replication-config> </jboss-web>
Option
|
Description
|
---|---|
<replication-trigger>
|
Controls which conditions should trigger session data replication across the cluster. This option is necessary because after a mutable object stored as a session attribute is accessed from the session, the container has no clear way to know if the object has been modified and needs to be replicated, unless method
setAttribute() is called directly.
Regardless of the setting, you can always trigger session replication by calling
setAttribute() .
|
<replication-granularity>
|
Determines the granularity of data that is replicated. It defaults to
SESSION , but can be set to ATTRIBUTE instead, to increase performance on sessions where most attributes remain unchanged.
|
Option
|
Description
|
---|---|
<use-jk>
|
Whether to assume that a load balancer such as
mod_cluster , mod_jk , or mod_proxy is in use. The default is false . If set to true , the container examines the session ID associated with each request and replaces the jvmRoute portion of the session ID if there is a failover.
|
<max-unreplicated-interval>
|
The maximum interval (in seconds) to wait after a session was accessed before triggering a replication of a session's timestamp, even if it is considered to be unchanged. This ensures that cluster nodes are aware of each session's timestamp and that an unreplicated session will not expire incorrectly during a failover. It also ensures that you can rely on a correct value for calls to method
HttpSession.getLastAccessedTime() during a failover.
By default, no value is specified. A value of
0 causes the timestamp to be replicated whenever the session is accessed. A value of -1 causes the timestamp to be replicated only if other activity during the request triggers a replication. A positive value greater than HttpSession.getMaxInactiveInterval() is treated as a misconfiguration and converted to 0 .
|
<snapshot-mode>
|
Specifies when sessions are replicated to other nodes. The default is
INSTANT and the other possible value is INTERVAL .
In
INSTANT mode, changes are replicated at the end of a request, by means of the request processing thread. The <snapshot-interval> option is ignored.
In
INTERVAL mode, a background task runs at the interval specified by <snapshot-interval> , and replicates modified sessions.
|
<snapshot-interval>
|
The interval, in milliseconds, at which modified sessions should be replicated when using
INTERVAL for the value of <snapshot-mode> .
|
<session-notification-policy>
|
The fully-qualified class name of the implementation of interface
ClusteredSessionNotificationPolicy which governs whether servlet specification notifications are emitted to any registered HttpSessionListener , HttpSessionAttributeListener , or HttpSessionBindingListener .
|
9.2. HttpSession Passivation and Activation
9.2.1. About HTTP Session Passivation and Activation
- When the container requests the creation of a new session, if the number of currently active session exceeds a configurable limit, the server attempts to passivate some sessions to make room for the new one.
- Periodically, at a configured interval, a background task checks to see if sessions should be passivated.
- When a web application is deployed and a backup copy of sessions active on other servers is acquired by the newly deploying web application's session manager, sessions may be passivated.
- The session has not been in use for longer than a configurable maximum idle time.
- The number of active sessions exceeds a configurable maximum and the session has not been in use for longer than a configurable minimum idle time.
9.2.2. Configure HttpSession Passivation in Your Application
HttpSession passivation is configured in your application's WEB_INF/jboss-web.xml
or META_INF/jboss-web.xml
file.
Example 9.3. Example jboss-web.xml
File
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 5.0//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd"> <jboss-web version="6.0" xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_6_0.xsd"> <max-active-sessions>20</max-active-sessions> <passivation-config> <use-session-passivation>true</use-session-passivation> <passivation-min-idle-time>60</passivation-min-idle-time> <passivation-max-idle-time>600</passivation-max-idle-time> </passivation-config> </jboss-web>
Passivation Configuration Elements
<max-active-sessions>
- The maximum number of active sessions allowed. If the number of sessions managed by the session manager exceeds this value and passivation is enabled, the excess will be passivated based on the configured
<passivation-min-idle-time>
. Then, if the number of active sessions still exceeds this limit, attempts to create new sessions will fail. The default value of-1
sets no limit on the maximum number of active sessions. <passivation-config>
- This element holds the rest of the passivation configuration parameters, as child elements.
<passivation-config>
Child Elements
<use-session-passivation>
- Whether or not to use session passivation. The default value is
false
. <passivation-min-idle-time>
- The minimum time, in seconds, that a session must be inactive before the container will consider passivating it in order to reduce the active session count to conform to value defined by max-active-sessions. The default value of
-1
disables passivating sessions before<passivation-max-idle-time>
has elapsed. Neither a value of -1 nor a high value are recommended if<max-active-sessions>
is set. <passivation-max-idle-time>
- The maximum time, in seconds, that a session can be inactive before the container attempts to passivate it to save memory. Passivation of such sessions takes place regardless of whether the active session count exceeds
<max-active-sessions>
. This value should be less than the<session-timeout>
setting in theweb.xml
. The default value of-1
disables passivation based on maximum inactivity.
Note
<max-active-sessions>
. The number of sessions replicated from other nodes also depends on whether REPL
or DIST
cache mode is enabled. In REPL
cache mode, each session is replicated to each node. In DIST
cache mode, each session is replicated only to the number of nodes specified by the owners
parameter. See Section 9.1.2, “About the Web Session Cache” and Section 9.1.3, “Configure the Web Session Cache” for information on configuring session cache modes.
REPL
cache mode, each node would store 800 sessions in memory. With DIST
cache mode enabled, and the default owners
setting of 2
, each node stores 200 sessions in memory.
9.3. Cookie Domain
9.3.1. About the Cookie Domain
/
. This means that only the issuing host can read the contents of a cookie. Setting a specific cookie domain makes the contents of the cookie available to a wider range of hosts. To set the cookie domain, refer to Section 9.3.2, “Configure the Cookie Domain”.
9.3.2. Configure the Cookie Domain
http://app1.xyz.com
and http://app2.xyz.com
to share an SSO context, even if these applications run on different servers in a cluster or the virtual host with which they are associated has multiple aliases.
Example 9.4. Example Cookie Domain Configuration
<Valve className="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn" cookieDomain="xyz.com" />
9.4. Implement an HA Singleton
The following procedure demonstrates how to deploy of a Service that is wrapped with the SingletonService decorator and used as a cluster-wide singleton service. The service activates a scheduled timer, which is started only once in the cluster.
Procedure 9.3. Implement an HA Singleton Service
Write the HA singleton service application.
The following is a simple example of aService
that is wrapped with theSingletonService
decorator to be deployed as a singleton service. A complete example can be found in thecluster-ha-singleton
quickstart that ships with Red Hat JBoss Enterprise Application Platform 6. This quickstart contains all the instructions to build and deploy the application.Create a service.
The following listing is an example of a service:package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; import java.util.Date; import java.util.concurrent.atomic.AtomicBoolean; import javax.naming.InitialContext; import javax.naming.NamingException; import org.jboss.logging.Logger; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; /** * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a> */ public class HATimerService implements Service<String> { private static final Logger LOGGER = Logger.getLogger(HATimerService.class); public static final ServiceName SINGLETON_SERVICE_NAME = ServiceName.JBOSS.append("quickstart", "ha", "singleton", "timer"); /** * A flag whether the service is started. */ private final AtomicBoolean started = new AtomicBoolean(false); /** * @return the name of the server node */ public String getValue() throws IllegalStateException, IllegalArgumentException { LOGGER.infof("%s is %s at %s", HATimerService.class.getSimpleName(), (started.get() ? "started" : "not started"), System.getProperty("jboss.node.name")); return ""; } public void start(StartContext arg0) throws StartException { if (!started.compareAndSet(false, true)) { throw new StartException("The service is still started!"); } LOGGER.info("Start HASingleton timer service '" + this.getClass().getName() + "'"); final String node = System.getProperty("jboss.node.name"); try { InitialContext ic = new InitialContext(); ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).initialize("HASingleton timer @" + node + " " + new Date()); } catch (NamingException e) { throw new StartException("Could not initialize timer", e); } } public void stop(StopContext arg0) { if (!started.compareAndSet(true, false)) { LOGGER.warn("The service '" + this.getClass().getName() + "' is not active!"); } else { LOGGER.info("Stop HASingleton timer service '" + this.getClass().getName() + "'"); try { InitialContext ic = new InitialContext(); ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).stop(); } catch (NamingException e) { LOGGER.error("Could not stop timer", e); } } } }
Create an activator that installs the
Service
as a clustered singleton.The following listing is an example of a Service activator that installs theHATimerService
as a clustered singleton service:package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; import org.jboss.as.clustering.singleton.SingletonService; import org.jboss.logging.Logger; import org.jboss.msc.service.DelegatingServiceContainer; import org.jboss.msc.service.ServiceActivator; import org.jboss.msc.service.ServiceActivatorContext; import org.jboss.msc.service.ServiceController; /** * Service activator that installs the HATimerService as a clustered singleton service * during deployment. * * @author Paul Ferraro */ public class HATimerServiceActivator implements ServiceActivator { private final Logger log = Logger.getLogger(this.getClass()); @Override public void activate(ServiceActivatorContext context) { log.info("HATimerService will be installed!"); HATimerService service = new HATimerService(); SingletonService<String> singleton = new SingletonService<String>(service, HATimerService.SINGLETON_SERVICE_NAME); /* * To pass a chain of election policies to the singleton, for example, * to tell JGroups to prefer running the singleton on a node with a * particular name, uncomment the following line: */ // singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node2/cluster"))); singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry())) .setInitialMode(ServiceController.Mode.ACTIVE) .install() ; } }
Note
The above code example uses a class,org.jboss.as.clustering.singleton.SingletonService
, that is part of the JBoss EAP private API. A public API will become available in the EAP 7 release and the private class will be deprecated, but this classes will be maintained and available for the duration of the EAP 6.x release cycle.Create a ServiceActivator File
Create a file namedorg.jboss.msc.service.ServiceActivator
in the application'sresources/META-INF/services/
directory. Add a line containing the fully qualified name of the ServiceActivator class created in the previous step.org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.HATimerServiceActivator
Create a Singleton bean that implements a timer to be used as a cluster-wide singleton timer.
This Singleton bean must not have a remote interface and you must not reference its local interface from another EJB in any application. This prevents a lookup by a client or other component and ensures the SingletonService has total control of the Singleton.Create the Scheduler interface
package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; /** * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a> */ public interface Scheduler { void initialize(String info); void stop(); }
Create the Singleton bean that implements the cluster-wide singleton timer.
package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb; import javax.annotation.Resource; import javax.ejb.ScheduleExpression; import javax.ejb.Singleton; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.ejb.TimerConfig; import javax.ejb.TimerService; import org.jboss.logging.Logger; /** * A simple example to demonstrate a implementation of a cluster-wide singleton timer. * * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a> */ @Singleton public class SchedulerBean implements Scheduler { private static Logger LOGGER = Logger.getLogger(SchedulerBean.class); @Resource private TimerService timerService; @Timeout public void scheduler(Timer timer) { LOGGER.info("HASingletonTimer: Info=" + timer.getInfo()); } @Override public void initialize(String info) { ScheduleExpression sexpr = new ScheduleExpression(); // set schedule to every 10 seconds for demonstration sexpr.hour("*").minute("*").second("0/10"); // persistent must be false because the timer is started by the HASingleton service timerService.createCalendarTimer(sexpr, new TimerConfig(info, false)); } @Override public void stop() { LOGGER.info("Stop all existing HASingleton timers"); for (Timer timer : timerService.getTimers()) { LOGGER.trace("Stop HASingleton timer: " + timer.getInfo()); timer.cancel(); } } }
Start each JBoss EAP 6 instance with clustering enabled.
To enable clustering for standalone servers, you must start each server with theHA
profile, using a unique node name and port offset for each instance.- For Linux, use the following command syntax to start the servers:
EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET
Example 9.5. Start multiple standalone servers on Linux
$ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node1
$ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100
- For Microsoft Windows, use the following command syntax to start the servers:
EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET
Example 9.6. Start multiple standalone servers on Microsoft Windows
C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node1
C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100
Note
If you prefer not to use command line arguments, you can configure thestandalone-ha.xml
file for each server instance to bind on a separate interface.Deploy the application to the servers
The following Maven command deploys the application to a standalone server running on the default ports.mvn clean install jboss-as:deploy
To deploy to additional servers, pass the server name. if it is on a different host, pass the host name and port number on the command line:mvn clean package jboss-as:deploy -Djboss-as.hostname=localhost -Djboss-as.port=10099
See thecluster-ha-singleton
quickstart that ships with JBoss EAP 6 for Maven configuration and deployment details.
9.5. Apache mod_cluster-manager Application
9.5.1. About mod_cluster-manager Application
9.5.2. Exploring mod_cluster-manager Application
Figure 9.1. mod_cluster Administration Web Page
- [1] mod_cluster/1.2.8.Final: This denotes the version of the mod_cluster native library
- [2] ajp://192.168.122.204:8099: This denotes the protocol used (either one of AJP, HTTP, HTTPS), hostname or IP address of the worker node and the port
- [3] jboss-eap-6.3-2: This denotes the worker node's JVMRoute.
- [4] Virtual Host 1: This denotes the virtual host(s) configured on the worker node
sticky-session-force
is set to "true"- [8] Load balancing group (LBGroup): The
load-balancing-group
property is set in the mod_cluster subsystem in EAP configuration to group all worker nodes into custom load balancing groups. Load balancing group (LBGroup) is an informational field which gives information about all set load balancing groups. If this field is not set, then all worker nodes are grouped into a single default load balancing groupNote
This is only an informational field and thus cannot be used to setload-balancing-group
property. The property has to be set in mod_cluster subsystem in EAP configuration. - [9] Load (value): This indicates the load factor on the worker node. The load factor(s) are evaluated as below:
-load > 0
: A load factor with value 1 indicates that the worker node is overloaded. A load factor of 100 denotes a free and not-loaded node.-load = 0
:A load factor of value 0 indicates that the worker node is in a standby mode. This means that no session requests will be routed to this node until and unless the other worker nodes are unavailable-load = -1
: A load factor of value -1 indicates that the worker node is in an error state.-load = -2
: A load factor of value -2 indicates that the worker node is undergoing CPing/CPong and is in a transition state
Chapter 10. CDI
10.1. Overview of CDI
10.1.1. Overview of CDI
10.1.2. About Contexts and Dependency Injection (CDI)
10.1.3. Benefits of CDI
- CDI simplifies and shrinks your code base by replacing big chunks of code with annotations.
- CDI is flexible, allowing you to disable and enable injections and events, use alternative beans, and inject non-CDI objects easily.
- It is easy to use your old code with CDI. You only need to include a
beans.xml
in yourMETA-INF/
orWEB-INF/
directory. The file can be empty. - CDI simplifies packaging and deployments and reduces the amount of XML you need to add to your deployments.
- CDI provides lifecycle management via contexts. You can tie injections to requests, sessions, conversations, or custom contexts.
- CDI provides type-safe dependency injection, which is safer and easier to debug than string-based injection.
- CDI decouples interceptors from beans.
- CDI provides complex event notification.
10.1.4. About Type-safe Dependency Injection
10.1.5. Relationship Between Weld, Seam 2, and JavaServer Faces
10.2. Use CDI
10.2.1. First Steps
10.2.1.1. Enable CDI
Contexts and Dependency Injection (CDI) is one of the core technologies in JBoss EAP 6, and is enabled by default. If for some reason it is disabled and you need to enable it, follow this procedure.
Procedure 10.1. Enable CDI in JBoss EAP 6
Check to see if the CDI subsystem details are commented out of the configuration file.
A subsystem can be disabled by commenting out the relevant section of thedomain.xml
orstandalone.xml
configuration files, or by removing the relevant section altogether.To find the CDI subsystem inEAP_HOME/domain/configuration/domain.xml
orEAP_HOME/standalone/configuration/standalone.xml
, search them for the following string. If it exists, it is located inside the <extensions> section.<extension module="org.jboss.as.weld"/>
The following line must also be present in the profile you are using. Profiles are in individual <profile> elements within the <profiles> section.<subsystem xmlns="urn:jboss:domain:weld:1.0"/>
Before editing any files, stop JBoss EAP 6.
JBoss EAP 6 modifies the configuration files during the time it is running, so you must stop the server before you edit the configuration files directly.Edit the configuration file to restore the CDI subsystem.
If the CDI subsystem was commented out, remove the comments.If it was removed entirely, restore it by adding this line to the file in a new line directly above the </extensions> tag:<extension module="org.jboss.as.weld"/>
- You also need to add the following line to the relevant profile in the <profiles> section.
<subsystem xmlns="urn:jboss:domain:weld:1.0"/>
Restart JBoss EAP 6.
Start JBoss EAP 6 with your updated configuration.
JBoss EAP 6 starts with the CDI subsystem enabled.
10.2.2. Use CDI to Develop an Application
10.2.2.1. Use CDI to Develop an Application
Contexts and Dependency Injection (CDI) gives you tremendous flexibility in developing applications, reusing code, adapting your code at deployment or run-time, and unit testing. JBoss EAP 6 includes Weld, the reference implementation of CDI. These tasks show you how to use CDI in your enterprise applications.
10.2.2.2. Use CDI with Existing Code
beans.xml
in the META-INF/
or WEB-INF/
directory of your archive. The file can be empty.
Procedure 10.2. Use legacy beans in CDI applications
Package your beans into an archive.
Package your beans into a JAR or WAR archive.Include a
beans.xml
file in your archive.Place abeans.xml
file into your JAR archive'sMETA-INF/
or your WAR archive'sWEB-INF/
directory. The file can be empty.
You can use these beans with CDI. The container can create and destroy instances of your beans and associate them with a designated context, inject them into other beans, use them in EL expressions, specialize them with qualifier annotations, and add interceptors and decorators to them, without any modifications to your existing code. In some circumstances, you may need to add some annotations.
10.2.2.3. Exclude Beans From the Scanning Process
One of the features of Weld, the JBoss EAP 6 implementation of CDI, is the ability to exclude classes in your archive from scanning, having container lifecycle events fired, and being deployed as beans. This is not part of the JSR-299 specification.
Example 10.1. Exclude packages from your bean
- The first one excludes all Swing classes.
- The second excludes Google Web Toolkit classes if Google Web Toolkit is not installed.
- The third excludes classes which end in the string
Blether
(using a regular expression), if the system property verbosity is set tolow
. - The fourth excludes Java Server Faces (JSF) classes if Wicket classes are present and the viewlayer system property is not set.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:weld="http://jboss.org/schema/weld/beans" xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee http://docs.jboss.org/cdi/beans_1_0.xsd http://jboss.org/schema/weld/beans http://jboss.org/schema/weld/beans_1_1.xsd"> <weld:scan> <!-- Don't deploy the classes for the swing app! --> <weld:exclude name="com.acme.swing.**" /> <!-- Don't include GWT support if GWT is not installed --> <weld:exclude name="com.acme.gwt.**"> <weld:if-class-available name="!com.google.GWT"/> </weld:exclude> <!-- Exclude classes which end in Blether if the system property verbosity is set to low i.e. java ... -Dverbosity=low --> <weld:exclude pattern="^(.*)Blether$"> <weld:if-system-property name="verbosity" value="low"/> </weld:exclude> <!-- Don't include JSF support if Wicket classes are present, and the viewlayer system property is not set --> <weld:exclude name="com.acme.jsf.**"> <weld:if-class-available name="org.apache.wicket.Wicket"/> <weld:if-system-property name="!viewlayer"/> </weld:exclude> </weld:scan> </beans>
10.2.2.4. Use an Injection to Extend an Implementation
You can use an injection to add or change a feature of your existing code. This example shows you how to add a translation ability to an existing class. The translation is a hypothetical feature and the way it is implemented in the example is pseudo-code, and only provided for illustration.
buildPhrase
. The buildPhrase
method takes as an argument the name of a city, and outputs a phrase like "Welcome to Boston." Your goal is to create a version of the Welcome
class which can translate the greeting into a different language.
Example 10.2. Inject a Translator
Bean Into the Welcome
Class
Translator
object into the Welcome
class. The Translator
object may be an EJB stateless bean or another type of bean, which can translate sentences from one language to another. In this instance, the Translator
is used to translate the entire greeting, without actually modifying the original Welcome
class at all. The Translator
is injected before the buildPhrase
method is implemented.
public class TranslatingWelcome extends Welcome { @Inject Translator translator; public String buildPhrase(String city) { return translator.translate("Welcome to " + city + "!"); } ... }
10.2.3. Ambiguous or Unsatisfied Dependencies
10.2.3.1. About Ambiguous or Unsatisfied Dependencies
- It resolves the qualifier annotations on all beans that implement the bean type of an injection point.
- It filters out disabled beans. Disabled beans are @Alternative beans which are not explicitly enabled.
10.2.3.2. About Qualifiers
Example 10.3. Define the @Synchronous
and @Asynchronous
Qualifiers
@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface Synchronous {}
@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface Asynchronous {}
Example 10.4. Use the @Synchronous
and @Asynchronous
Qualifiers
@Synchronous public class SynchronousPaymentProcessor implements PaymentProcessor { public void process(Payment payment) { ... } }
@Asynchronous public class AsynchronousPaymentProcessor implements PaymentProcessor { public void process(Payment payment) { ... } }
10.2.3.3. Use a Qualifier to Resolve an Ambiguous Injection
This task shows an ambiguous injection and removes the ambiguity with a qualifier. Read more about ambiguous injections at Section 10.2.3.1, “About Ambiguous or Unsatisfied Dependencies”.
Example 10.5. Ambiguous injection
Welcome
, one which translates and one which does not. In that situation, the injection below is ambiguous and needs to be specified to use the translating Welcome
.
public class Greeter { private Welcome welcome; @Inject void init(Welcome welcome) { this.welcome = welcome; } ... }
Procedure 10.3. Resolve an Ambiguous Injection with a Qualifier
Create a qualifier annotation called
@Translating
.@Qualifier @Retention(RUNTIME) @Target({TYPE,METHOD,FIELD,PARAMETERS}) public @interface Translating{}
Annotate your translating
Welcome
with the@Translating
annotation.@Translating public class TranslatingWelcome extends Welcome { @Inject Translator translator; public String buildPhrase(String city) { return translator.translate("Welcome to " + city + "!"); } ... }
Request the translating
Welcome
in your injection.You must request a qualified implementation explicitly, similar to the factory method pattern. The ambiguity is resolved at the injection point.public class Greeter { private Welcome welcome; @Inject void init(@Translating Welcome welcome) { this.welcome = welcome; } public void welcomeVisitors() { System.out.println(welcome.buildPhrase("San Francisco")); } }
The Translating
Welcome
is used, and there is no ambiguity.
10.2.4. Managed Beans
10.2.4.1. About Managed Beans
bean
.
@Inject
) is a bean. This includes every JavaBean and every EJB session bean. The only requirement to enable the mentioned services in beans is that they reside in an archive (a JAR, or a Java EE module such as a WAR or EJB JAR) that contains a special marker file: META-INF/beans.xml
.
10.2.4.2. Types of Classes That are Beans
@ManagedBean
, but in CDI you do not need to. According to the specification, the CDI container treats any class that satisfies the following conditions as a managed bean:
- It is not a non-static inner class.
- It is a concrete class, or is annotated
@Decorator
. - It is not annotated with an EJB component-defining annotation or declared as an EJB bean class in
ejb-jar.xml
. - It does not implement interface
javax.enterprise.inject.spi.Extension
. - It has either a constructor with no parameters, or a constructor annotated with
@Inject
.
10.2.4.3. Use CDI to Inject an Object Into a Bean
META-INF/beans.xml
or WEB-INF/beans.xml
file, each object in your deployment can be injected using CDI.
Inject an object into any part of a bean with the
@Inject
annotation.To obtain an instance of a class, within your bean, annotate the field with@Inject
.Example 10.6. Injecting a
TextTranslator
instance into aTranslateController
public class TranslateController { @Inject TextTranslator textTranslator; ...
Use your injected object's methods
You can use your injected object's methods directly. Assume thatTextTranslator
has a methodtranslate
.Example 10.7. Use your injected object's methods
// in TranslateController class public void translate() { translation = textTranslator.translate(inputText); }
Use injection in the constructor of a bean
You can inject objects into the constructor of a bean, as an alternative to using a factory or service locator to create them.Example 10.8. Using injection in the constructor of a bean
public class TextTranslator { private SentenceParser sentenceParser; private Translator sentenceTranslator; @Inject TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) { this.sentenceParser = sentenceParser; this.sentenceTranslator = sentenceTranslator; } // Methods of the TextTranslator class ... }
Use the
Instance(<T>)
interface to get instances programmatically.TheInstance
interface can return an instance of TextTranslator when parameterized with the bean type.Example 10.9. Obtaining an instance programmatically
@Inject Instance<TextTranslator> textTranslatorInstance; ... public void translate() { textTranslatorInstance.get().translate(inputText); }
When you inject an object into a bean all of the object's methods and properties are available to your bean. If you inject into your bean's constructor, instances of the injected objects are created when your bean's constructor is called, unless the injection refers to an instance which already exists. For instance, a new instance would not be created if you inject a session-scoped bean during the lifetime of the session.
10.2.5. Contexts, Scopes, and Dependencies
10.2.5.1. Contexts and Scopes
@RequestScoped
, @SessionScoped
, and @ConversationScope
.
10.2.5.2. Available Contexts
Context | Description |
---|---|
@Dependent | The bean is bound to the lifecycle of the bean holding the reference. |
@ApplicationScoped | Bound to the lifecycle of the application. |
@RequestScoped | Bound to the lifecycle of the request. |
@SessionScoped | Bound to the lifecycle of the session. |
@ConversationScoped | Bound to the lifecycle of the conversation. The conversation scope is between the lengths of the request and the session, and is controlled by the application. |
Custom scopes | If the above contexts do not meet your needs, you can define custom scopes. |
10.2.6. Bean Lifecycle
10.2.6.1. Manage the Lifecycle of a Bean
This task shows you how to save a bean for the life of a request. Several other scopes exist, and you can define your own scopes.
@Dependent
. This means that the bean's lifecycle is dependent upon the lifecycle of the bean which holds the reference. For more information, see Section 10.2.5.1, “Contexts and Scopes”.
Procedure 10.4. Manage Bean Lifecycles
Annotate the bean with the scope corresponding to your desired scope.
@RequestScoped @Named("greeter") public class GreeterBean { private Welcome welcome; private String city; // getter & setter not shown @Inject void init(Welcome welcome) { this.welcome = welcome; } public void welcomeVisitors() { System.out.println(welcome.buildPhrase(city)); } }
When your bean is used in the JSF view, it holds state.
<h:form> <h:inputText value="#{greeter.city}"/> <h:commandButton value="Welcome visitors" action="#{greeter.welcomeVisitors}"/> </h:form>
Your bean is saved in the context relating to the scope that you specify, and lasts as long as the scope applies.
10.2.6.2. Use a Producer Method
This task shows how to use producer methods to produce a variety of different objects which are not beans for injection.
Example 10.10. Use a producer method instead of an alternative, to allow polymorphism after deployment
@Preferred
annotation in the example is a qualifier annotation. For more information about qualifiers, refer to: Section 10.2.3.2, “About Qualifiers”.
@SessionScoped public class Preferences implements Serializable { private PaymentStrategyType paymentStrategy; ... @Produces @Preferred public PaymentStrategy getPaymentStrategy() { switch (paymentStrategy) { case CREDIT_CARD: return new CreditCardPaymentStrategy(); case CHECK: return new CheckPaymentStrategy(); default: return null; } } }
@Inject @Preferred PaymentStrategy paymentStrategy;
Example 10.11. Assign a scope to a producer method
@Dependent
. If you assign a scope to a bean, it is bound to the appropriate context. The producer method in this example is only called once per session.
@Produces @Preferred @SessionScoped public PaymentStrategy getPaymentStrategy() { ... }
Example 10.12. Use an injection inside a producer method
@Produces @Preferred @SessionScoped public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps, CheckPaymentStrategy cps ) { switch (paymentStrategy) { case CREDIT_CARD: return ccps; case CHEQUE: return cps; default: return null; } }
Note
Producer methods allow you to inject non-bean objects and change your code dynamically.
10.2.7. Named Beans and Alternative Beans
10.2.7.1. About Named Beans
@Named
annotation. Naming a bean allows you to use it directly in Java Server Faces (JSF).
@Named
annotation takes an optional parameter, which is the bean name. If this parameter is omitted, the lower-cased bean name is used as the name.
10.2.7.2. Use Named Beans
Use the
@Named
annotation to assign a name to a bean.@Named("greeter") public class GreeterBean { private Welcome welcome; @Inject void init (Welcome welcome) { this.welcome = welcome; } public void welcomeVisitors() { System.out.println(welcome.buildPhrase("San Francisco")); } }
The bean name itself is optional. If it is omitted, the bean is named after the class name, with the first letter decapitalized. In the example above, the default name would begreeterBean
.Use the named bean in a JSF view.
<h:form> <h:commandButton value="Welcome visitors" action="#{greeter.welcomeVisitors}"/> </h:form>
Your named bean is assigned as an action to the control in your JSF view, with a minimum of coding.
10.2.7.3. About Alternative Beans
Example 10.13. Defining Alternatives
@Alternative @Synchronous @Asynchronous public class MockPaymentProcessor implements PaymentProcessor { public void process(Payment payment) { ... } }
beans.xml
file.
10.2.7.4. Override an Injection with an Alternative
Alternative beans let you override existing beans. They can be thought of as a way to plug in a class which fills the same role, but functions differently. They are disabled by default. This task shows you how to specify and enable an alternative.
Procedure 10.5. Override an Injection
TranslatingWelcome
class in your project, but you want to override it with a "mock" TranslatingWelcome class. This would be the case for a test deployment, where the true Translator bean cannot be used.
Define the alternative.
@Alternative @Translating public class MockTranslatingWelcome extends Welcome { public String buildPhrase(string city) { return "Bienvenue à " + city + "!"); } }
Substitute the alternative.
To activate the substitute implementation, add the fully-qualified class name to yourMETA-INF/beans.xml
orWEB-INF/beans.xml
file.<beans> <alternatives> <class>com.acme.MockTranslatingWelcome</class> </alternatives> </beans>
The alternative implementation is now used instead of the original one.
10.2.8. Stereotypes
10.2.8.1. About Stereotypes
- default scope
- a set of interceptor bindings
- all beans with the stereotype have defaulted bean EL names
- all beans with the stereotype are alternatives
@Named
annotation, any bean it is placed on has a default bean name. The bean may override this name if the @Named annotation is specified directly on the bean. For more information about named beans, see Section 10.2.7.1, “About Named Beans”.
10.2.8.2. Use Stereotypes
Without stereotypes, annotations can become cluttered. This task shows you how to use stereotypes to reduce the clutter and streamline your code. For more information about what stereotypes are, see Section 10.2.8.1, “About Stereotypes”.
Example 10.14. Annotation clutter
@Secure @Transactional @RequestScoped @Named public class AccountManager { public boolean transfer(Account a, Account b) { ... } }
Procedure 10.6. Define and Use Stereotypes
Define the stereotype,
@Secure @Transactional @RequestScoped @Named @Stereotype @Retention(RUNTIME) @Target(TYPE) public @interface BusinessComponent { ... }
Use the stereotype.
@BusinessComponent public class AccountManager { public boolean transfer(Account a, Account b) { ... } }
Stereotypes streamline and simplify your code.
10.2.9. Observer Methods
10.2.9.1. About Observer Methods
10.2.9.2. Fire and Observe Events
Example 10.15. Fire an event
public class AccountManager { @Inject Event<Withdrawal> event; public boolean transfer(Account a, Account b) { ... event.fire(new Withdrawal(a)); } }
Example 10.16. Fire an event with a qualifier
public class AccountManager { @Inject @Suspicious Event <Withdrawal> event; public boolean transfer(Account a, Account b) { ... event.fire(new Withdrawal(a)); } }
Example 10.17. Observe an event
@Observes
annotation.
public class AccountObserver { void checkTran(@Observes Withdrawal w) { ... } }
Example 10.18. Observe a qualified event
public class AccountObserver { void checkTran(@Observes @Suspicious Withdrawal w) { ... } }
10.2.10. Interceptors
10.2.10.1. About Interceptors
Interception points
- business method interception
- A business method interceptor applies to invocations of methods of the bean by clients of the bean.
- lifecycle callback interception
- A lifecycle callback interceptor applies to invocations of lifecycle callbacks by the container.
- timeout method interception
- A timeout method interceptor applies to invocations of the EJB timeout methods by the container.
10.2.10.2. Use Interceptors with CDI
Example 10.19. Interceptors without CDI
- The bean must specify the interceptor implementation directly.
- Every bean in the application must specify the full set of interceptors in the correct order. This makes adding or removing interceptors on an application-wide basis time-consuming and error-prone.
@Interceptors({ SecurityInterceptor.class, TransactionInterceptor.class, LoggingInterceptor.class }) @Stateful public class BusinessComponent { ... }
Procedure 10.7. Use interceptors with CDI
Define the interceptor binding type.
@InterceptorBinding @Retention(RUNTIME) @Target({TYPE, METHOD}) public @interface Secure {}
Mark the interceptor implementation.
@Secure @Interceptor public class SecurityInterceptor { @AroundInvoke public Object aroundInvoke(InvocationContext ctx) throws Exception { // enforce security ... return ctx.proceed(); } }
Use the interceptor in your business code.
@Secure public class AccountManager { public boolean transfer(Account a, Account b) { ... } }
Enable the interceptor in your deployment, by adding it to
META-INF/beans.xml
orWEB-INF/beans.xml
.<beans> <interceptors> <class>com.acme.SecurityInterceptor</class> <class>com.acme.TransactionInterceptor</class> </interceptors> </beans>
The interceptors are applied in the order listed.
CDI simplifies your interceptor code and makes it easier to apply to your business code.
10.2.11. About Decorators
@Decorator
.
Example 10.20. Example Decorator
@Decorator public abstract class LargeTransactionDecorator implements Account { @Inject @Delegate @Any Account account; @PersistenceContext EntityManager em; public void withdraw(BigDecimal amount) { ... } public void deposit(BigDecimal amount); ... } }