Java multitenancy: Configuration options, tenant lifecycle, and isolation in action

Dig deeper into the multitenancy implementation in the IBM SDK Java Technology Edition, Version 7 Release 1

The multitenant JVM is available with the IBM SDK Java™ Technology Edition, Version 7 Release 1 as a tech preview. By running multiple applications within a single multitenant JVM, a cloud system can speed applications' start times and reduce their memory footprint. Examine the tenant lifecycle, learn some of the available configuration options, and see the benefits of isolation of statics in tenant applications. This article builds on a previous developerWorks article, "Introduction to Java multitenancy."

Share:

Gavin Rolleston (gavin_rolleston@ca.ibm.com), Software Developer, IBM

Gavin Rolleston has been working on the J9 JVM team for 10 years, implementing core JVM components. He has contributed to IBM's Java deliverables over the years including the Java 5, Java 6, and Java 7 releases across platforms. In the past few years he has been contributing to IBM cloud and virtualization efforts for Java. Previously he was the lead developer for the layer of the JVM that provides and implements an abstraction of OS services across the various platforms supported by the IBM Java SDK.



Michael Dawson, Senior Software Developer, IBM Ottawa Lab

Michael Dawson graduated in 1989 from the University of Waterloo with a bachelor's degree in computer engineering and in 1991 from Queens University with a master's degree in electrical engineering, specializing in cryptography. He then did security consulting work and developed EDI security products. Next, he was the development lead for a start-up that delivered security products across various platforms. He has since held leadership roles in teams developing e-commerce applications and delivering them as services including EDI communication services, credit card processing, on-line auctions, and electronic invoicing. The technologies used ranged from C/C++ to Java and J2EE platforms and components across a range of operating systems. In 2006, Michael joined IBM and works on the J9 JVM and WebSphere Real Time.



31 July 2014

Also available in Chinese Japanese

Get the IBM SDK Java Technology Edition, Version 7 Release 1

To run this article's sample application, download and install the IBM SDK Java Technology Edition, Version 7 Release 1 for your operating system:

The multitenant JVM is available as a technology preview in the IBM SDK Java Technology Edition, Version 7 Release 1. By using this feature, application deployments can achieve better performance and isolation than they would have if they shared a traditional JVM. A previous developerWorks article, "Introduction to Java multitenancy," provides a high-level overview of:

  • The benefits and costs of the multitenant JVM
  • How to use the multitenant JVM
  • How isolation of static fields is achieved
  • Resource isolation through resource constraints, known as Resource Consumption Management (RCM)

That article highlights the simplicity of the multitenancy framework. To run your Java application as a tenant in the multitenant JVM you only need to add -Xmt to the command-line, as in:

./java -Xmt Hello

This article delves into two areas of the multitenant framework in more detail. First, we walk through the tenant lifecycle to give you a deeper understanding of how applications run within the multitenant JVM. During the walkthrough we also introduce configuration options in the framework for:

  • Passing options into a tenant application
  • Passing options into the JVM daemon process (javad) that runs the tenant applications
  • Targeting a tenant application at a specific JVM daemon process

Second, we demonstrate the benefits of isolation of statics in running applications.

Sample application and configuration

We'll use a sample application — one that you can compile and run — to demonstrate the configuration options and go through the lifecycle. We also provide the command line to run the application and the javad.options file for configuring the JVM daemon process. (See Downloads to get all of the example code, commands, and scripts for this article.)

Listing 1 shows the code for the sample application.

Listing 1. The sample application
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Hello {

   public static void main(String[] args) {

      InputStreamReader inputStream = new InputStreamReader(System.in);
      BufferedReader reader = new BufferedReader(inputStream);

      System.out.println("What is your name?");

      try {
         String name = reader.readLine();
         System.out.println("Hello " + name);
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

The command line (to be entered on a single line) for launching the application as a tenant is:

java -Xmt -Djavad.home=/tmp/multitenant_daemons -Xmx100M 
-Xlimit:netIO=5M-100M -DexampleProperty=1 -jar hello.jar

Options on the java -Xmt command-line affect only the tenant application. Here's what each option does:

-Xmt
Tells the Java launcher to launch the application as a tenant application.
-Djavad.home=/tmp/multitenant_daemons
Targets a specific JVM daemon process. If -Djavad.home is not used, all tenants that this user runs will run under the same JVM daemon.
-Xmx100M
Tells the JVM daemon process to limit this tenant's portion of the JVM's object heap to 100MB. The total size of the JVM's object heap is specified in the javad.options file. If the tenant tries to use more than 100MB, the tenant receives an OutOfMemoryError (OOME), but this OOME doesn't affect any of the other tenants running under the daemon.
-Xlimit:netIO=5M-10M
Tells the JVM daemon to reserve 5 megabytes per second (MBps) of network I/O for this tenant but to limit the tenant to 10MBps. If the JVM daemon is unable to reserve this minimum bandwidth, an error message is delivered to the tenant launcher and the tenant application isn't started.
-DexampleProperty=1
Makes this Java property visible only to this tenant. None of the other tenants running under this instance of the JVM daemon process can see it.

The only way to pass options for the JVM daemon itself is through the javad.options file. Listing 2 shows the javad.options file to use for the JVM daemon that will run the sample application.

Listing 2. javad.options
# Option file for javad
-Xtenant
-Xrcm:consumer=autotenant
-Djavad.persistAfterEmptyTime=1
-Xmx2G
-Xdump:java:label=/tmp/multitenant_daemons/javacore.%pid.%seq.txt

Here's what the options in the javad.options file do:

-Xtenant -Xrcm:consumer=autotenant
Instruct the instance of the JVM that serves as the daemon process to run in multitenant mode.
-Djavad.persistAfterEmptyTime=1
Instructs the JVM daemon process to terminate one minute after the last tenant shuts down.
-Xmx2G
Specifies the maximum object heap size of the JVM daemon to be 2GB. All tenants running under the JVM daemon will use a portion of this 2GB object heap.
-Xdump:java:label=/tmp/multitenant_daemons/javacore.%pid.%seq.txt
Assigns the specified filename to the javacore files generated by the JVM daemon. (See the user guide for details of this option.)

Now that you understand the options that the example uses, you're ready to set up your environment for running the application.


Environment setup

Because the command line for running the application specifies -Djavad.home (so that it can target a specific JVM daemon process), you must create the -Djavad.home directory:

mkdir /tmp/multitenant_daemons

Next, change to /tmp/multitenant_daemons, copy the javad.options file that's included in the SDK into this directory, and add our additional options:

cd /tmp/multitenant_daemons
cp /tmp/Java7R1_SR1/jre/bin/javad.options .
echo -Djavad.persistAfterEmptyTime=1 >> javad.options
echo -Xdump:java:label=/tmp/multitenant_daemons/javacore.%pid.%seq.txt >> javad.options
echo -Xmx2G >> javad.options

Run cat javad.options and examine the contents of the javad.options file to verify that it's correct:

# Option file for javad
-Xtenant
-Xrcm:consumer=autotenant
-Djavad.persistAfterEmptyTime=1
-Xdump:java:label=/tmp/multitenant_daemons/javacore.%pid.%seq.txt
-Xmx2G

At this point the newly created -Djavad.home directory contains only the javad.options file. After starting, the JVM daemon process will write connection information into this directory.


Exploring the tenant lifecycle

Now that you have configured your environment and have a sample application and configuration, we can step through the lifecycle. The main stages are:

  1. Tenant launcher and JVM daemon startup
  2. Application execution
  3. Tenant shutdown
  4. JVM daemon shutdown

Stage 1: Tenant launcher and JVM daemon startup

When a Java application is launched as a tenant (via the-Xmt command-line option), the tenant launcher looks to see if a JVM daemon process is already running. If the process isn't running, the tenant launcher starts one. After the JVM daemon process starts, some "handshaking" occurs between it and the tenant launcher. As part of this handshake, the environment of the launcher process and the tenant command-line options are sent to the JVM daemon. Finally, the tenant application runs under the JVM daemon process.

Take a look at this sequence in more detail in the context of the example application. Start by running the tenant launcher command line (entered as a single line):

java -Xmt -Djavad.home=/tmp/multitenant_daemons -Xmx100M -Xlimit:netIO=5M-100M 
-DexampleProperty=1 -jar /tmp/hello.jar

The Java launcher sees-Xmt on the command line and becomes a tenant launcher.

The tenant launcher reads -Djavad.home=/tmp/multitenant_daemons and, as a result, looks in /tmp/multitenant_daemons to determine if a JVM daemon process is already associated with this user and directory. The launcher determines that no such JVM daemon process exists, so it starts one. Recall that the options contained in the javad.options file are passed into the JVM daemon process.

After the JVM daemon process starts, it "advertises" itself by writing connection information to the -Djavad.home directory. It creates a new directory of the form .java.uid to store this connection information. For example, the name of the new directory here is .javad.4068, where 4068 is the current user's UID:

dave /tmp/multitenant_daemons  $ ls -altr
total 32
-rwxr-xr-x 1 dave bgroup 164 Jun 26 17:04 javad.options
drwxrwxrwt. 10 root root 20480 Jun 26 17:05 ..
drwxr-xr-x 3 dave bgroup 4096 Jun 26 17:05 .
drwxr-xr-x 2 dave bgroup 4096 Jun 26 17:05 .javad.4068
dave /tmp/multitenant_daemons $

The UID is used to associate the JVM daemon process with the current user. Other users cannot connect to the JVM daemon process because access to the contents of .java.4068 is restricted to the user who initiated the creation of the JVM daemon:

dave /tmp/multitenant_daemons $ ls -altr .javad.4068
total 12
drwxr-xr-x 3 dave bgroup 4096 Jun 26 17:05 ..
drwxr-xr-x 2 dave bgroup 4096 Jun 26 17:05 .
-rw------- 1 dave bgroup 103 Jun 26 17:05 4068
dave /tmp/multitenant_daemons $

The tenant launcher reads the connection information in .javad.4068 and creates a socket to connect to the JVM daemon process. All subsequent communication between the tenant launcher and the JVM daemon process is done through this socket via an internally specified wire protocol.

What "creating a new tenant" means

Keep in mind that one of the ways that the multitenant JVM reduces memory footprint is by sharing the classes on the bootstrap classpath amongst tenants. For this technique to work, each tenant needs a unique view of these classes' static members. Therefore, when a new tenant is created, all the statics in the classes on the appropriate classpaths are initialized to the default state, and the initialization status of these classes is reset in the context of that tenant. This process ensures that static initialization occurs in the same manner as it would if the application were run in its own JVM. The class library bootstrap is then initiated to bring the state for the tenant to the same state that would be expected before the main method is run by a JVM.

The tenant launcher sends its command line and a full copy of its environment variables to the javad daemon process, then waits for messages from the javad process.

The JVM daemon reads -Xlimit:netIO=5M-100M and checks that it can satisfy the minimum bandwidth requirement of 5MBps. The daemon performs this check by verifying that the sum of all the tenants' minimum bandwidth requirements does not exceed the values specified in rcm.xml.

The JVM daemon then creates a tenant to run the Java application specified by the tenant launcher.

The final part of startup entails redirecting the tenant application's System.out, System.in, and System.err to the socket channel that connects the tenant launcher and the JVM daemon process.

Stage 2: Application execution

At this point the JVM is ready to run the application, and the JVM daemon invokes the main method in the newly created tenant. Look at what happens in each section of the code in Listing 1.

The first part just writes out a message:

public static void main(String[] args) {

   InputStreamReader inputStream = new InputStreamReader(System.in);
   BufferedReader reader = new BufferedReader(inputStream);

   System.out.println("What is your name?");

The JVM daemon intercepts this write to System.out and redirects it over the socket to the tenant launcher. The tenant launcher writes "What is your name?" to its console:

dave /tmp/multitenant_daemons $ /tmp/Java7R1_SR1/jre/bin/java 
-Xmt -Djavad.home=/tmp/multitenant_daemons -Xmx100M -Xlimit:netIO=5M-100M 
-DexampleProperty=1 -jar /tmp/hello.jar
What is your name?

The next part reads from standard in:

String name = reader.readLine();

The JVM daemon process intercepts this request on System.in and sends a message to the tenant launcher indicating that it is waiting for user input.

Gathering information for debugging

While the application waits for user input, this is a convenient time to take a slight detour and look at how you can get information about a running application, which can aid debugging. Start by mapping the tenant launcher to the JVM daemon process (javad) that is running the application. This mapping is useful if you want to generate a javacore file to see what the JVM daemon process is doing.

Use ps -ef to find the process ID of the JVM daemon that corresponds to the tenant launcher. The daemon and the launcher can be mapped to each other because they have the same -Djavad.home directory. Then inject a SIGQUIT:

dave /tmp/multitenant_daemons $ ps -ef | grep javad
dave 5092 3632 0 17:05 pts/1 00:00:00 /tmp/Java7R1_SR1/jre/bin/java -Xmt 
-Djavad.home=/tmp/multitenant_daemons -Xmx100M 
-Xlimit:netIO=5M-100M -DexampleProperty=1 -jar /tmp/hello.jar
dave 5094 5092 2 17:05 ? 00:00:01 /tmp/Java7R1_SR1/jre/bin/javad -Djavad.home=/tmp/multitenant_daemons
dave 5164 3543 0 17:07 pts/0 00:00:00 grep javad
dave /tmp/multitenant_daemons $ kill -QUIT 5094
dave /tmp/multitenant_daemons $

The JVM daemon process receives the SIGQUIT and generates a javacore file in the directory specified by -Xdump in the javad.options file. The JVMDUMP messages are broadcast back to the tenant launcher:

dave /tmp/multitenant_daemons $ /tmp/Java7R1_SR1/jre/bin/java -Xmt 
-Djavad.home=/tmp/multitenant_daemons -Xmx100M -Xlimit:netIO=5M-100M 
-DexampleProperty=1 -jar /tmp/hello.jar
What is your name?
JVMDUMP039I Processing dump event "user", detail "" at 2014/06/26 17:07:20 - please wait.
JVMDUMP032I JVM requested Java dump using 
'/tmp/multitenant_daemons/javacore.5094.0001.txt' in response to an event
JVMDUMP010I Java dump written to /tmp/multitenant_daemons/javacore.5094.0001.txt
JVMDUMP013I Processed dump event "user", detail "".

All JVMDUMP messages are broadcast to all tenant launchers connected to the JVM daemon. So, if the JVM daemon process fails, all tenant launchers are notified.

Having identified the JVM daemon (javad) process and set the dump options in the javad.options file, you can use this technique to get any of the standard JVM diagnostics when running the multitenant JVM.

Returning to execution

Now let's go back to looking at the execution of the main method. Enter Dave, which the tenant launcher sends to the JVM daemon process.

The JVM daemon process receives Dave and directs it to System.in for the tenant application.

The tenant application then writes Hello Dave to System.out:

System.out.println("Hello " + name);

The JVM daemon intercepts this write to System.out and redirects it over the socket to the tenant launcher.

The tenant launcher receives Hello Dave and writes it out to its console. At this point, the main method is complete and the tenant enters the shutdown phase.

Stage 3: Tenant shutdown

The primary difference between shutting down an application running in the multitenancy framework and shutting down an application running under a nonmultitenant JVM is that in the multitenancy case the JVM daemon process running the application does not terminate along with the Java application.

However, from the perspective of the Java application that runs as a tenant, the behaviour is the same:

  1. The application waits for its nondaemon threads to terminate.
  2. The JVM daemon runs the application's shutdown hooks.
  3. The JVM daemon terminates any of the application's remaining daemon threads.
  4. The tenant launcher terminates with the exit code specified by the Java application.

The fourth step is accomplished by the JVM daemon sending a message containing the application's exit code to the tenant launcher. In this example, the tenant launcher terminates with exit code 0:

dave /tmp/multitenant_daemons $ /tmp/Java7R1_SR1/jre/bin/java -Xmt -Djavad.home=
/tmp/multitenant_daemons -Xmx100M -Xlimit:netIO=5M-100M -DexampleProperty=1 -jar /tmp/hello.jar
What is your name?
JVMDUMP039I Processing dump event "user", detail "" at 2014/06/26 17:07:20 - please wait.
JVMDUMP032I JVM requested Java dump using '/tmp/multitenant_daemons/javacore.5094.0001.txt' 
in response to an event
JVMDUMP010I Java dump written to /tmp/multitenant_daemons/javacore.5094.0001.txt
JVMDUMP013I Processed dump event "user", detail "".
Dave
Hello Dave
dave /tmp/multitenant_daemons $ echo $?
0
dave /tmp/multitenant_daemons $

Stage 4: JVM daemon shutdown

By default, the JVM daemon stays up and running indefinitely. However, in this example the javad.options file specifies -Djavavd.persistAfterTime=1. Therefore, the JVM daemon process terminates one minute after the tenant application shuts down. If another tenant launcher connects before the timeout occurs, the one-minute timer will restart when no more tenants are connected to it.

This completes our dive into the tenant lifecycle. The next section delves into the isolation that is enforced between tenants that are running in the same JVM daemon.


Isolation in action

The ability to run applications unchanged, or with very limited changes, is one of the key benefits achieved through isolation.

The multitenant JVM imposes a level of isolation among tenants to limit the extent to which one application can directly affect the operation of others. This section explores the benefits of this key feature through code examples that you can compile and run yourself. To run the examples, download the supporting Java code and Linux scripts.

As "Introduction to Java multitenancy" explains, a key element of achieving isolation is static field isolation. To help you understand this concept in terms of real-world applications, we'll start with the two simple applications in Listing 3.

Listing 3. Two simple applications
public class StillHere extends ExampleBase {
   public static void main(String[] args) {
      while(notDone()) {
         println(args, "Still Here");
         sleep(SECONDS_2);
      }
      println(args, "Done");
   }
}

public class Goodbye extends ExampleBase {
   public static void main(String[] args) {
      println(args, "Hello");
      sleep(SECONDS_4);
      println(args, "About to exit, Goodbye");
      System.exit(-1);
   }
}

The first application prints Still Here every two seconds until it is done. The second prints Hello, waits four seconds, and then calls System.exit(). These (obviously toy) applications represent:

  • An application that runs on an ongoing basis, carrying out a periodic task
  • An application that carries out some processing and then terminates

The two applications run concurrently in the same javad process when started with the following command lines:

./java -Xmt -cp isolationExamples.jar StillHere app1 & 
./java -Xmt -cp isolationExamples.jar Goodbye app2

Alternatively, you could run the two same applications in a regular JVM. First, you'd put them in a wrapper class, as shown in Listing 4.

Listing 4. Wrapper class for running the two applications in a conventional JVM
public class HelloGoodBye extends StandardJVMRunner{
   public static void main(final String[] args) {
      Thread stillHereThread = new Thread() {
         public void run() { StillHere.main(APP1); }
      };
      
      Thread goodByeThread = new Thread() {
         public void run() { Goodbye.main(APP2); }
      };
      stillHereThread.start();
      goodByeThread.start();
   }
}

Then you'd run them with the following command line:

./java -cp isolationExamples.jar HelloGoodBye

One of the key differences between the two ways of running the applications is the degree of isolation. When you run the applications together without the multitenancy feature, the output is:

Run in normal JVM
app1 [Still Here]
app2 [Hello]
app1 [Still Here]
app2 [About to exit, Goodbye]
app1 [Still Here]
app1 [Still Here]

Notice that soon after app2 calls System.exit(), you no longer see output from app1. This happens because the call in app2 to System.exit() causes the shared JVM process to terminate, which includes the termination of app1. This is an extreme example of one application being able to affect another application directly. The lack of isolation causes the Goodbye application to terminate the Still Here application.

Now run the same two applications under the multitenant JVM:

Run in MT JVM
app1 [Still Here]
app2 [Hello]
app1 [Still Here]
app1 [Still Here]
app2 [About to exit, Goodbye]
dave /tmp/dave/apr16/jre/bin $ app1 [Still Here]
app1 [Still Here]
app1 [Still Here]
app1 [Still Here]
app1 [Still Here]
app1 [Done]

As you can see, app1 continues to run after app2 calls System.exit(), and app1 continues to run until it terminates normally and prints Done. The isolation provided by the multitenant JVM enables the Goodbye application to run existing code and terminate without affecting the other application that runs in the same process.

Because the multitenant JVM shares a single process, an application can make calls that normally affect the JVM process, while limiting the effects on the application itself. System.exit() is a simple example, but the same principle applies in other cases — shutdown hooks, system in/out, and many others. As you can see from the example, this capability limits the ability of one application to affect other applications, without requiring changes to the applications themselves. The ability to run applications unchanged, or with very limited changes, is one of the key benefits achieved through isolation.

Consider a slightly more complex example: support for a different (human) language in each of two applications. Listing 5 shows both applications.

Listing 5. Applications that use different languages
 public class OutputInDefaultLocale extends ExampleBase {
   public static void main(String[] args) {
      while(notDone()) {
         Locale localeToUse = Locale.getDefault(); 
         ResourceBundle messages = ResourceBundle.getBundle("Messages",localeToUse);
         println(args, messages.getString( "HELLO_KEY"));
         sleep(SECONDS_2);
      }
      println(args, "Done");
   }
}
public class ChangeLocale extends ExampleBase {
   public static void main(String[] args) {
      try { Thread.sleep(SECONDS_4); } catch (Exception e) {};
      println(args, "Changing default locale from:" + Locale.getDefault());
      Locale.setDefault(new Locale("fr","CA"));
      println(args, "Locale is now:" + Locale.getDefault());
      OutputInDefaultLocale.main(args);
   }
}

The bundle files are:

Messages.properties  -  content ? HELLO_KEY=Hello
Messages_fr.properties? content ? HELLO_KEY=Bonjour

The first application (OutputInDefaultLocale) gets the default locale and outputs Hello in the default language every two seconds until done. The second application (ChangeLocale) waits four seconds and changes the default locale to French, such that its subsequent writes of Hello come out as Bonjour.

As in the preceding example, you can run these two applications as tenants concurrently by using the multitenancy feature with the following command lines:

./java -Xmt -cp isolationExamples.jar OutputInDefaultLocale app1 & 
./java -Xmt -cp isolationExamples.jar ChangeLocale app2

And, as before, you can also run the applications in a standard JVM by using a wrapper. Listing 6 shows the wrapper.

Listing 6. Wrapper for running the OutputInDefaultLocale and ChangeLocale applications
public class LocaleIssues extends StandardJVMRunner {
   public static void main(final String[] args) {
      Thread outputInDefaultLocalThread = new Thread() {
         public void run() { OutputInDefaultLocale.main(APP1); }
      };
      
      Thread changeLocaleThread = new Thread() {
         public void run() { ChangeLocale.main(APP2); }
      };
      
      outputInDefaultLocalThread.start();
      changeLocaleThread.start();
   }
}

The command line for running the applications without the multitenancy feature is:

./java -cp isolationExamples.jar LocaleIssues

Without the multitenancy feature, the output is:

Run in normal JVM
app1 [Hello]
app1 [Hello]
app2 [Changing default locale from:en_US]
app2 [Locale is now:fr_CA]
app2 [Bonjour]
app1 [Bonjour]
app2 [Bonjour]
app1 [Bonjour]
app2 [Bonjour]
app1 [Bonjour]
app2 [Done]
app1 [Done]

The first application starts running, and the output is in English because the default locale is en_US. After the second application finishes its four-second wait, it changes the local to French, fr_CA. After that point the output for both applications is in French. Wait — that's not what we wanted! But it makes sense that this happens because both applications share a JVM, and there's only one default locale.

Now run the same applications with the multitenancy feature:

Run in MT JVM
app1 [Hello]
app1 [Hello]
app2 [Changing default locale from:en_US]
app2 [Locale is now:fr_CA]
app2 [Bonjour]
app1 [Hello]
app2 [Bonjour]
app1 [Hello]
app2 [Bonjour]
app1 [Hello]
app1 [Done]
app2 [Bonjour]
app2 [Done]

After the second application changes the default locale, the first application continues to output in English while the second application outputs in French. This is what we want. The isolation provided by the multitenant JVM enables the two applications to share the same process, with correct behaviour, without needing to change the application. In this case, the default locale is stored in a static, and the isolation of statics provided by the multitenancy feature enables each application (tenant) to use its own locale.


Conclusion

This article looks at the multitenant JVM and its operation in more detail, as a follow-on to "Introduction to Java multitenancy." You now have a deeper knowledge of the lifecycle of a tenant application and a better understanding of the benefits provided by the isolation of statics.

We encourage you to download the multitenant JVM, try out the demos, get the documentation, and write your own applications. You can also join the IBM multitenant JVM community to keep up to date with the latest available information and to provide us with feedback to help shape the direction of the technology.


Downloads

DescriptionNameSize
Code and commands for hello applicationhello.jar2KB
Code and scripts for isolation examplesisolationExamples.jar12KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, Cloud computing
ArticleID=978537
ArticleTitle=Java multitenancy: Configuration options, tenant lifecycle, and isolation in action
publish-date=07312014