© Peter Späth 2021
P. SpäthBeginning Java MVC 1.0https://doi.org/10.1007/978-1-4842-6280-1_9

9. Java MVC and EJBs

Peter Späth1 
(1)
Leipzig, Sachsen, Germany
 

Enterprise Java Beans (EJBs) are classes that encapsulate business functionalities, each of a certain kind. Unlike normal Java classes, however, EJBs run in a container environment , which means the server adds system-level services to them. These services include lifecycle management (instantiating and destroying, when and how), transactionality (building logical, atomic, rollback-enabled units of work), and security (which users can invoke which methods). Because Java MVC runs inside such a container, namely Jakarta EE, EJBs are a good way for Java MVC applications to encapsulate their business functionalities.

The EJB technology includes session beans and message driven beans. However, the latter go beyond the scope of this book, so here we will talk about session EJBs only.

About Session EJBs

Session EJBs can be accessed locally (in the same application), remotely (over the network, via method invocation), or via some web service interface (distributed applications across heterogeneous networks, HTML, XML, or JSON data formats).

Concerning the creation and destruction of session EJBs, there are three types of session EJBs:
  • Singleton: With a singleton session EJB, the container instantiates only one instance and all clients share this single instance. You can do this if the EJB does not have a state that discriminates clients, and concurrent access does not impose problems.

  • Stateless: EJBs of the “stateless” kind do not maintain a state, so a particular client can have different instances assigned to subsequent EJB invocations (the container handles this; the client doesn’t know about this assignment).

  • Stateful: Stateful EJBs maintain a state and a client can be sure it will receive the same session EJB instance from the container for subsequent uses of the same EJB. You will often hear that stateful EJB clients maintain a conversational state concerning using stateful EJBs. Stateful session EJBs cannot implement web services, because web services are not allowed to have state and no session information is communicated .

Defining EJBs

To define a singleton EJB, a stateless EJB, or a stateful EJB, you add one of these annotations—@Singleton, @Stateless, or @Stateful, respectively—to the EJB implementation.

Consider three examples. An EJB called Configuration for the encapsulated access to application-wide configuration settings. Another EJB called Invoice, which handles invoice registration and inquiry given some invoice ID. A third EJB called TicTacToe for a simple tic-tac-toe game implementation. Obviously, for the configuration EJB we can use a singleton EJB, since neither local state nor concurrency matter. Similarly, for the invoice EJB, we can use a stateless EJB, since the state is mediated by the ID, which does not access an EJB state but rather a database state. The last one, the tic-tac-toe EJB, needs to maintain the game board for each client and we thus must use a stateful EJB for it.
import javax.ejb.Singleton;
import javax.ejb.Stateless;
import javax.ejb.Stateful;
...
@Singleton
public class Configuration {
    ... configuration access methods
}
@Stateless
public class Invoice {
   ... invoice access methods
}
@Stateful
public class TicTacToe {
    ... tic-tac-toe methods
}

Of course, all those classes must go to different files. We put them together for illustration purposes only.

Concerning their accessibility from client code, session EJBs can use one or a combination of three methods (all annotations shown are from the javax.ejb package):
  • No-interface: You use this method if you don’t want to describe the EJB access via an interface. This is only possible with local clients running inside the same application. While the separation into interfaces (describing what gets done in interfaces) and the implementation (the how, implemented in non-abstract classes) is generally a good idea for clean code, a no-interface view can make sense for simple EJBs. For no-interface EJBs, you just declare the implementation, as follows:

         @Stateless public class Invoice {
             ... implementation
         }
The EJB clients can then of course only access the implementation class directly, without mediating interfaces.
  • Local: If you want to define local access to session EJBs (EJBs and EJB clients running in the same application) and want to use an interface view for that, you can mark the interface with @Local and let the EJB implementation class implement the interface:

         @Local public interface InvoiceInterface {
             ... abstract interface methods
         }
         @Stateless public class Invoice
               implements InvoiceInterface {
             ... implementation
         }
Or you use the @Local annotation in the implementation class:
         public interface InvoiceInterface {
             ... abstract interface methods
         }
         @Stateless
         @Local(InvoiceInterface.class)
         public class Invoice implements InvoiceInterface {
             ... implementation
         }
You can even omit the implementation, as follows:
         public interface InvoiceInterface {
             ... abstract interface methods
         }
         @Stateless
         @Local(InvoiceInterface.class)
         public class Invoice {
             ... implementation
         }
This last method will further reduce the coupling of the interface, although this is in general not recommended.
  • @Remote: Use the @Remote annotation for this session EJB to be accessible from outside the application. You can simply replace @Local with @Remote and everything that was said for the local access and concerning the interfaces is true unaltered for remote access. So you write the following, for example:

         public interface InvoiceInterface {
             ... abstract interface methods
         }
         @Stateless
         @Remote(InvoiceInterface.class)
         public Invoice
               implements InvoiceInterface {
             ... implementation
         }
EJBs can have a local and a remote interface; just use both annotations together:
         public interface InvoiceLocal {
             ... abstract interface methods
         }
         public interface InvoiceRemote {
             ... abstract interface methods
         }
         @Stateless
         @Local(InvoiceLocal.class)
         @Remote(InvoiceRemote.class)
         public Invoice
               implements InvoiceLocal,
                          InvoiceRemote {
             ... implementation
         }
Also, nobody hinders us from using the same interface for both local and remote access:
         public interface InvoiceInterface {
             ... abstract interface methods
         }
         @Stateless
         @Local(InvoiceInterface.class)
         @Remote(InvoiceInterface.class)
         public Invoice implements InvoiceInterface {
             ... implementation
         }
Caution

Remote access means parameters in method calls are passed by value, not by reference! So, although local and remote interfaces are declared co-natural to each other, you must be careful with method parameters under certain circumstances.

Accessing EJBs

Accessing local EJBs from a Java MVC controller is easy: you just use the @EJB injection to let CDI assign an instance access to an EJB:
public class SomeController {
    ...
    @EJB
    private SomeEjbInterface theEjb;
    // or, for no-interface EJBs
    @EJB
    private SomeEjbClass theEjb;
    ...
}
Addressing remote EJBs is considerably more complicated compared to local-access EJBs. You have to set up a JNDI context and then use it to do a lookup of a remote instance:
...
String remoteServerHost = "localhost";
// or "192.168.1.111" or something
String remoteServerPort = "3700";
// Port 3700 is part of the GlassFish conf
Properties props = new Properties();
props.setProperty("java.naming.factory.initial",
  "com.sun.enterprise.naming."+
  "SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs",
  "com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state",
  "com.sun.corba.ee.impl.presentation.rmi."+
  "JNDIStateFactoryImpl");
props.setProperty("org.omg.CORBA.ORBInitialHost",
  remoteServerHost);
props.setProperty("org.omg.CORBA.ORBInitialPort",
  remoteServerPort);
try {
  InitialContext ic = new InitialContext(props);
  // Use this to see what EJBs are available
  // and how to name them
  //NamingEnumeration<NameClassPair> list =
  //       ic.list("");
  //while (list.hasMore()) {
  //  System.out.println(list.next().getName());
//}
// Looking up a remote EJB
  SomeEjbRemote testEJB = (SomeEjbRemote)
      ic.lookup(
       "book.jakarta8.testEjbServer.SomeEjbRemote");
  // Invoking some EJB method
  System.out.println(testEJB.tellMe());
}catch(Exception e) {
  e.printStackTrace(System.err);
}
This example assumes that, on the remote server side, you created a session EJB with a remote interface:
package book.jakarta8.testEjbServer;
public interface SomeEjbRemote {
    String tellMe();
}
And an implementation like this one:
package book.jakarta8.testEjbServer;
import javax.ejb.Remote;
import javax.ejb.Stateless;
@Stateless
@Remote(SomeEjbRemote.class)
public class SomeEjb implements SomeEjbRemote {
  @Override
    public String tellMe() {
      return "Hello World";
    }
}

Obviously, for this to work, the Java MVC application must have access to the compiled remote interfaces. That means in the EJB server build, you must have somehow included a step to extract the interfaces from the generated classes. We’ll talk about that in detail later.

If the remote EJB server is a GlassFish server, you can also use its asadmin command to see which EJBs are eligible for remote access and how they are named:
cd [GLASSFISH_INST]
cd bin
./asadmin list-jndi-entries

Other Java Enterprise Edition (JEE or Jakarta EE) application servers probably apply other naming schemes for remotely accessible EJBs. So you must consult their documentation and/or get the remotely visible JNDI entry listing. For the latter, you can try programmatic access (commented out in the previous listing), or use some administration features implemented for the remote EJB server.

EJB Projects

Jakarta EE projects don’t have to be web projects; they can also just expose services to clients accessing their remote EJB interfaces. Web interfaces, like REST or web service interfaces, are your first choice for interoperability with web browsers and non-Jakarta EE servers. But for faster communication among Jakarta EE participants in a larger system with different network nodes, using Component-to-EJB communication might be a better choice.

Web projects can also expose remote EJBs to appropriate clients. If you want to have a streamlined project without web capabilities, the procedure to do that inside Eclipse is described in the following paragraphs.

Start a new Gradle project similar to the web projects we created so far, but change the plugin declaration to the following:
plugins {
    id 'java-library'
}
From there on, create the EJBs and their remote interfaces as described, with the following additional constraint: move the EJB interfaces to their own package. For example:
book.javamvc.ejbproj.ejb              <- Implementation
book.javamvc.ejbproj.ejb.interfaces   <- Interfaces
Inside the build file, we add a task that automates the EJB stub generation:
task extractStubs (type: Jar, dependsOn:classes) {
  archiveClassifier = 'ejb-stubs'
  from "$buildDir/classes/java/main"
  include "**/interfaces/*.class"
}
jar.finalizedBy(extractStubs)

This ensures that, after each jar task execution, the stubs are created. You can then run the jar task to create the full EJB jar and the stubs. You’ll find both in the build/libs folder. You may have to press F5 on that folder to update the view. Any client wishing to communicate with the EJBs must include the interface JAR as a dependency. Of course, the EJB project itself must be deployed on the server for the EJBs to work.

EJBs with Dependencies

Until now, we developed only very simple EJBs without the need to use libraries included as JARs. Once you need to add libraries to an EJB, you’ll run into trouble. The reason for this is that there is no standard way to add dependencies to isolated EJB modules. If you need to add library JARs, the best way is to pack the EJB module into an Enterprise Archive (EAR).

EARs are archives that bundle EJBs, web applications (WARs), and library JARs. Dealing with EARs instead of isolated EJBs somewhat increases the complexity of the administration activities. But adding library JARs to EARs is the best way of including dependencies with non-web applications.

In order to add EAR functionality to an application inside Eclipse, you basically have to do the following:
  1. 1.

    Build a new Gradle project. Go to New ➤ Other... ➤ Gradle ➤ Gradle Project.

     
  2. 2.

    Choose any name you like. It’s a good idea to add “ear” to the end of the name.

     
  3. 3.

    Inside build.gradle, change the plugins { } section to plugins { id 'ear' }.

     
  4. 4.

    Inside build.gradle, use as the dependencies { } section:

     
         dependencies {
           deploy project(path: ':war',
              configuration: 'archives')
           deploy project(path: ':ejb1',
              configuration: 'archives')
           earlib "org.apache.commons:"+
               "commons-math3:3.6.1"
         }
  1. 5.

    Create the war and ejb1 folders in the project root.

     
  2. 6.

    Open the settings.gradle file and add the following:

     
         include 'war', 'ejb1'
  1. 7.

    Invoke Gradle ➤ Refresh Gradle Project. Eclipse might throw an error message; you can ignore it for now.

     
  2. 8.

    The two subprojects war and ejb1 show up in the Project Explorer. You may have to update the working set if you are using one.

     
  3. 9.

    Convert both subprojects to a Faceted form (choose Configure ➤ Convert to Faceted Form...), and in the settings, add Java 1.8 capabilities.

     
We now have an EAR project with two subprojects. What is left to do is to add Gradle capabilities to each of the subprojects. The WAR project needs a build file like one of the many build.gradle files we used for Java MVC projects. What is different, though, is that we add a dependency to the sibling EJB project:
dependencies {
    implementation project(":ejb1")
    // Other dependencies...
}
Note

This is for the Gradle dependencies. In order for Eclipse to recognize the dependency, you have to add the EJB project as a dependency in the Java Build Path (choose Project Settings ➤ Java Build Path ➤ Projects tab).

For the EJB project, you probably use a build.gradle file like the following:
plugins {
    id 'java-library'
}
java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}
repositories {
    jcenter()
}
dependencies {
    implementation 'javax:javaee-api:8.0'
    // Add dependencies here...
}

If you run the ear task, the subprojects and the EAR file will be built. The latter can be found in the build/libs folder.

Asynchronous EJB Invocation

EJB clients call EJB methods asynchronously. This means the client invokes an EJB method that was marked eligible for asynchronous invocation, immediately regains control of the program execution, and handles the result from the EJB invocation later, when it is available.

To mark an EJB method for asynchronous invocation , you add the @Asynchronous annotation from the javax.ejb package to the method:
import java.util.concurrent.Future;
import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Singleton;
@Singleton // Example only, all EJB types work!
public class SomeEjb {
  @Asynchronous
  public Future<String> tellMeLater() {
    // Simulate some long running calculation
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
    }
    return new AsyncResult<String>(
        "Hi from tellMeLater()");
    }
}

This example EJB uses the no-interface method, but asynchronous invocation works for local and remote interfaces as well. AsyncResult is a convenience class that allows for the easy creation of a Future. This Future object will not be exposed to the client; its main purpose is to obey the method signature. The Future returned to the client will instead be transparently created by the EJB container .

On the EJB client side, you invoke the EJB as usual, and handle the Future you received from the EJB invocation as used from the JRE concurrency API:
...
@EJB
private SomeEjb someEjb;
...
Future<String> f = someEjb.tellMeLater();
try {
    // Example only: block until the result
    // is available:
    String s = f.get();
    System.err.println(s);
} catch (Exception e) {
    e.printStackTrace(System.err);
}

Timer EJBs

EJBs can be equipped with timer facilities, such as for delayed execution of some task or recurring automatic method invocations. You have two options: automatic timers and programmatic timers.

For automatic timers, you add a @Schedule or @Schedules annotation (from the javax.ejb package) to any void method (the visibility doesn’t matter) either without a parameter, or with a javax.ejb.Timer parameter. The parameters of the @Schedule annotation describe the frequency, as follows:
@Stateless
public class SomeEjb {
  @Schedule(minute="*", hour="0", persistent=false)
  // every minute during the hour between 00:00 and 01:00
  public void timeout1() {
    System.err.println("Timeout-1 from " + getClass());
  }
}

A delayed execution like “Do something once ten seconds after the server has started” is not possible with automatic timers.

The following is a listing of some example schedules you can use inside automatic timers:
@Schedule(second="10", minute="0", hour="0")
  // <- at 00:00:10 every day
@Schedule(minute="30", hour="0",
      dayOfWeek="Tue")
  // <- at 00:30:00 on Tuesdays (second defaults to 00)
@Schedule(minute="11", hour="15",
      dayOfWeek="Mon,Tue,Fri")
  // <- at 15:11:00 on mondays, Tuesdays and Fridays
@Schedule(minute="*/10", hour="*")
  // <- every 10 minutes, every hour
@Schedule(minute="25/10", hour="1")
  // <- 01:25, 01:35, 01:45 and 01:55
@Schedule(hour="*", dayOfMonth="1,2,3")
  // <- every hour at 1st, 2nd and 3rd each month
  // (minute defaults to 00)
@Schedule(hour="*/10")
  // <- every 10 hours
@Schedule(month="Feb,Aug")
  // <- 00:00:00 each February and August
  // (hour defaults to 00)
@Schedule(dayOfMonth="1", year="2020")
  // <- 00:00:00 each 1st each month during 2020
@Schedule(dayOfMonth="1-10")
  // <- 00:00:00 each 1st to 10th each month
The @Schedules annotation can be used to apply several @Schedule specifications to a timer callback:
@Schedules({
  @Schedule(hour="*"),
  @Schedule(hour="0", minute="30")
})
private void someMethod(Timer tm) {
    ...
}

This means every x:00:00 (x = 00 through 23), but also at 00:30:00. Unless you also give a persistent=false to the @Schedule annotation, a timer survives an application and a server restart.

Timers can also be defined programmatically. Here it is also possible to define a one-time shot, such as this:
@Singleton
@Startup
public class Timer1 {
  @Resource
  private SessionContext context;
  @PostConstruct
  public void go() {
    context.getTimerService().
         createSingleActionTimer(5000, new TimerConfig());
  }
  @Timeout
  public void timeout(Timer timer) {
    System.err.println("Hello from " + getClass());
  }
}

The method annotated with @Timeout is called every time the timer fires. For this example, this will be 5000 milliseconds after EJB creation, because of the createSingleActionTimer() invocation . The timer service you get with context.getTimerService() enables various scheduling options; see the API documentation for details.

Exercises

Exercise 1

Which of the following is/are true?
  • EJBs must have a local and a remote interface.

  • Not providing interfaces means EJBs are automatically assigned to local and remote interfaces by the EJB container (the part of the Jakarta EE server that handles EJBs).

  • A remote EJB means the EJB can be accessed from other applications on the same server. Access from other Jakarta EE servers is not possible.

  • EJBs cannot have a state.

  • If a client accesses an EJB, a new instance of the EJB is created on the server side.

  • To access any EJB from a client, you must use do a lookup in a JNDI context.

  • In order to use an EJB from a client, the EJB’s interfaces and its implementation must be imported into the client project.

Exercise 2

Create four projects:
  • A JSE project (no Jakarta EE capabilities) with a single MyDateTime class and a method called date( String format ), which returns the LocalDateTime as a string, according to the format string specified as a parameter. Make it a Gradle project.

  • An EJB project with a single EJB MyDateTimeEjb and local and remote interfaces. Have it use the JAR file generated from the JRE project above. Hint: You can use something like implementation files( '../../- SimpleNoJEE/build/libs/SimpleNoJEE.jar' ) to specify a local dependency.

  • An EAR project that contains the EJB project and adds the necessary JAR dependency.

  • A simple no-Jakarta-EE EJB client project that tests the remote interface from the MyDateTimeEjb EJB. Hint: Include gf-client.jar from GlassFish’s lib folder as a library dependency.

Summary

Enterprise Java Beans (EJBs) are classes that encapsulate business functionalities, each of a certain kind. Unlike normal Java classes, however, EJBs run in a container environment, which means the server adds system-level services to them. These include lifecycle management (instantiating and destroying, when and how), transactionality (building logical, atomic, rollback-enabled units of work), and security (which users can invoke which methods). Because Java MVC runs inside such a container, namely Jakarta EE, EJBs are a good way for Java MVC applications to encapsulate their business functionalities.

The EJB technology includes session beans and message driven beans. Session EJBs can be accessed locally (in the same application), remotely (over the network, via method invocation), or via some web service interface (distributed applications across heterogeneous networks, HTML, XML or JSON data formats).

Concerning the creation and destruction of session EJBs, there are three types of session EJBs. Singleton EJBs, stateless EJBs, and stateful EJBS. To define any of them, you add the appropriate annotation—@Singleton, @Stateless, or @Stateful—to the EJB implementation.

Concerning their accessibility from client code, session EJBs can use one or a combination of three methods: no-interface access, local access, or remote access.

Accessing local EJBs from a Java MVC controller is easy: you just use the @EJB injection to let CDI assign instance access to an EJB: @EJB private SomeEjbInterface theEjb.

Addressing remote EJBs is considerably more complicated compared to local-access EJBs. You have to set up a JNDI context and then use it to do a lookup of a remote instance.

For this to work, the Java MVC application must have access to the compiled remote interfaces. That means, in the EJB server build, you must have somehow included a step to extract the interfaces from the generated classes.

Jakarta EE projects don’t have to be web projects; they can also just expose services to clients accessing their remote EJB interfaces. Web interfaces, like REST or web service interfaces, are your first choice for interoperability with web browsers and non-Jakarta EE servers. For faster communication among Jakarta EE participants in a larger system with different network nodes, using Component-to-EJB communication might be a better choice. Web projects also can expose remote EJBs to appropriate clients.

Once you need to add libraries to an EJB, the best way is to pack the EJB module into an Enterprise Archive (EAR). EARs are archives that bundle EJBs, web applications (WARs), and library JARs. Dealing with EARs instead of isolated EJBs somewhat increases the complexity of the administration activities. But once you’re finished, if you run the ear task, the subprojects and the EAR file will be built. The latter can be found in the build/libs folder.

EJB clients call EJB methods asynchronously. This means the client invokes an EJB method that was marked eligible for asynchronous invocation, immediately regains control of the program execution, and handles the result from the EJB invocation later, when it is available.

To mark an EJB method for asynchronous invocation, you add the @Asynchronous annotation from the javax.ejb package to the method.

EJBs can be equipped with timer facilities, such as for delayed execution of some task or reoccurring automatic method invocations. You have two options: automatic timers and programmatic timers.

With automatic timers, you add a @Schedule or @Schedules annotation (from the javax.ejb package) to any void method (the visibility doesn’t matter), either without a parameter or with a javax.ejb.Timer parameter. The parameters of the @Schedule annotation describe the frequency.

Timers can also be defined programmatically. It is also possible to define a one-time invocation.

In the next chapter, we learn how to connect Java MVC to databases.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.8.82