Java Enterprise Edition (JEE)
Also known as Java Platform, Enterprise Edition (Java EE) and Java 2 Platform, Enterprise Edition (J2EE)
Startup Code
With an Eclipse MicroProfile container, create a class with @Initialized:
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.Initialized; import javax.enterprise.event.Observes; @ApplicationScoped public class ApplicationInitializer { public void onStartup(@Observes @Initialized(ApplicationScoped.class) Object o) { System.out.println(toString() + " started"); // code System.out.println(toString() + " finished"); } }
With a servlet container, create a class with @WebListener:
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class ApplicationInitializer implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println(toString() + " started"); // code System.out.println(toString() + " finished"); } @Override public void contextDestroyed(ServletContextEvent sce) { } }
With an EJB container, create an EJB with @Startup
import javax.annotation.PostConstruct; import javax.ejb.Singleton; import javax.ejb.Startup; @Singleton @Startup public class ApplicationInitializer { @PostConstruct private void onStartup() { System.out.println(toString() + " started"); // code System.out.println(toString() + " finished"); } }
Security
A client will be authenticated only if a resource is protected. You can use web.xml or annotations to protect a web resource. For JAX-RS see https://www.ibm.com/support/knowledgecenter/SSEQTP_liberty/com.ibm.websphere.wlp.doc/ae/rwlp_jaxrs_secure.html
The application-bnd info is for authorization after the authentication.
Web Applications
It is important to reduce the number of resources (images, CSS, Javascript, etc.) served for each request (caching and compression are also important, dealt elsewhere in the Cookbook). You can use browser or network sniffing tools to determine the largest number and sizes of resources. Here are some examples:
- Consider combining images into a single image - often called a "sprite" - and display those images using CSS sprite offset techniques.
- Consider combining multiple JavaScript files into a single file.
- Consider "minifying" JavaScript and CSS files.
- Consider compressing or resizing images more.
HTTP Sessions
Individual sessions retaining more than 1MB may be concerning. Use a system dump or heap dump and a tool such as the Memory Analyzer Tool with the IBM Extensions for Memory Analyzer to deep dive into session sizes and contents (http://www.ibm.com/developerworks/websphere/techjournal/0405_brown/0405_brown.html).
If there is a logout link, call javax.servlet.http.HttpSession.invalidate() to release the HTTP session as early as possible, reducing memory pressure: https://www.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/cprs_best_practice.html
If using session persistence, consider implementing manual update and sync of session updates: https://www.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/cprs_best_practice.html
Keep the amount of data in the HTTP session as small as possible.
Only touch session attributes that actually change. This allows for administrative changes to only persist updated attributes to the HTTP Session persistent storage.
Database Access
SQL statements should be written to use the parameterized ? (question mark) notation. In order for the prepared statement cache to be used effectively the parameterized statements will be reused from the cache. Consequently, building SQL statements with the parameters substituted in will all look like different statements and the cache will have little performance effect.
If you are using global transactions, use deferred enlistment: https://www.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/tdat_conpoolman.html
Make sure to close Connections, Statements, and ResultSets. In some databases (e.g. https://www.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/rprf_wastundb2.html), not closing all of these may cause additional overhead even if the objects will ultimately be closed by the pools.
JDBC Deadlocks
Applications that open more than one JDBC connection to the same datasource can result in a deadlock if there are not enough connections in the connection pool. See http://www-01.ibm.com/support/docview.wss?uid=swg1JR43775 If javacores show multiple threads waiting for a connection and WebSphere Application Server is reporting hung threads then you will want to increase the number of connections in the connection pool to at least 2n+1 where n = maximum number of threads in the thread pool. Applications that open more than 2 connections to the same datasource will need even larger pools (3n+1, 4n+1, etc).
To correct this problem the application developer has to fix the code to close a JDBC connection before opening another JDBC connection.
Web Services
Provide a jaxb.index file for every package that does not contain an ObjectFactory class. This action enables the system to completely avoid the search for JAXB classes. This approach does require application modification to account for the addition of the jaxb.index files. (https://www.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/cwbs_tuning_jaxbcontext.html)
Service Component Architecture (SCA)
Use @AllowsPassByReference if possible with SCA modules: https://www.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/tsca_passby_ref.html
Object Caching
The DistributedMap and DistributedObjectCache interfaces are simple interfaces for the dynamic cache. Using these interfaces, Java EE applications and system components can cache and share Java objects by storing a reference to the object in the cache. The default dynamic cache instance is created if the dynamic cache service is enabled in the administrative console. This default instance is bound to the global Java Naming and Directory Interface (JNDI) namespace using the name services/cache/distributedmap. (https://www.ibm.com/support/knowledgecenter/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/tdyn_distmap.html)
Tread with caution. Overly active distributed maps can become quite chatty amongst the JVMs and, in extreme cases, limit the total number of JVMs in the same distributed map domain because the JVMs spend most of their time chatting about the changes that occurred in the map.
MDB
An MDB exists within an EJB project inside an EAR. For example:
package com.example;
import java.time.Instant;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/Queue1"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") }, mappedName = "jms/Queue1")
public class AppMDB implements MessageListener {
public void onMessage(Message message) {
System.out.println(Instant.now() + ": Received " + message);
}
}
This is accompanied with a META-INF/ejb-jar.xml
; for
example:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/ejb-jar_3_2.xsd">
<display-name>AppMDBEJB</display-name>
</ejb-jar>
Example EAR file META-INF/application.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<application id="Application_ID" version="8" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/application_8.xsd">
<display-name>AppMDB</display-name>
<module id="Module_1594190856221">
<ejb>AppMDBEJB.jar</ejb>
</module>
</application>
JMS Client
Example JMS Client in a servlet:
package com.example;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.Instant;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AppJMSProducer extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String messageStr = "Hello World @ " + Instant.now();
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.println(Instant.now() + ": Started writing");
out.flush();
try {
InitialContext ctx = new InitialContext();
ConnectionFactory qcf = (ConnectionFactory)ctx.lookup("jms/ConnectionFactory1");
Queue queue = (Queue)ctx.lookup("jms/Queue1");
try (Connection connection = qcf.createConnection()) {
try (Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE)) {
MessageProducer producer = session.createProducer(queue);
TextMessage message = session.createTextMessage();
message.setText(messageStr);
producer.send(message);
}
}
} catch (Throwable t) {
out.println(Instant.now() + ": Error: " + t);
t.printStackTrace();
}
out.println(Instant.now() + ": Finished writing");
}
}
For a Maven project, the required dependencies:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>