Saturday, October 12, 2013

Java Synchronization and Concurrency Across Multiple JVMs, Multiple Computers

It is pretty standard fare for a Java application to coordinate multiple uses of a single resource by using the “synchronized” key word.  But there are times when a single resource must be used by multiple programs running in separate Java Virtual Machines (JVMs), perhaps even on different computers.  Synchronizing use of that resource involves a bit more planning.  Let’s look at single-JVM synchronization first so we can see where, how and why cross-JVM synchronization may be applied.

Typically, resources like counters and files that are used by multiple threads (multiple concurrent users) in a web application require synchronization.  In a Java Servlet, the doGet() and doPost() methods are multithreaded.  Each web browser addressing the web application will have a separate thread, executing the method independently and concurrently.  Whenever those methods update a resource that is declared outside of the method scope, it is a shared resource, and that update should be synchronized.
There are a couple easy ways to synchronize use of those resources.  We can make a method synchronized so that only one thread can use it at a time – all other threads will queue up until the synchronized method is available – they will take turns.  A second way to synchronize use of a resource is to update the resource within a synchronized block.  Let’s look at an example.

public class SynchServlet extends HttpServlet {
            private static PrintWriter fileOut; // Synchronize use of this!

            public void init( ServletConfig config ) throws ServletException {
                        super.init( config );
                        try {
                                    fileOut = new PrintWriter( "logfile.log" );
                        } catch( Exception x ) {
                                    throw new ServletException( x.toString() );
                        }
            }
                       
            public void destroy( ServletConfig config ) {
                        fileOut.close();
            }
           
            public void doGet( HttpServletRequest req, HttpServletResponse res )
                                    throws ServletException, IOException {
                        logWrite( "Using doGet()" );
                        //…
            }
           
            public void doPost( HttpServletRequest req, HttpServletResponse res )
                                    throws ServletException, IOException {
                        logWrite( "Using doPost()" );
                        //…
            }
           
            public static synchronized void logWrite( String logEntry ) {
                        fileOut.println( logEntry );
            }
}
 
In this first example, each time a browser calls on the doGet() and doPost() methods we will write to a log file. We need to have these threads take turns, so only one thread attempts to write to the file at a time – if more than one write occurs concurrently, a runtime exception will be generated.  The simplest way get the threads to take turns is to place the actual log file write in a synchronized method.  The example logWrite() method is modified with the “synchronized” key word so that each thread in this web application (Servlet) will wait until the logWrite() method is available, then the thread will lock the method for exclusive use until finished.  After that, the lock will be released, and the next thread will be able to use the method.

This synchronized method is static, so the lock is applied to the SynchServlet class, not to an instance of SynchServlet – that difference is irrelevant in this case, since there is usually only one instance of a Servlet.  However, in most cases, both “static” and “synchronized” modifiers should be used for a single resource that is shared within a JVM.  As a public static method, you can call it from other classes, and it will enforce the same synchronization controls, like this:
            SynchServlet.logWrite( "Some other message" );
 
Another way we can synchronize multiple threads using a shared resource is to put every use of the resource in synchronized blocks.  The following example shows how we might implement a doGet() success counter.

public class SynchServlet extends HttpServlet {
            private static int doGetSuccessCounter = 0;
            private static Object synchObject = new Object();
 
            public void doGet( HttpServletRequest req, HttpServletResponse res )
                                    throws ServletException, IOException {
                        try {     
                                    synchronized( synchObject ) {
                                                doGetSuccessCounter++;
                                    }
                                    //…                              

                                    synchronized( synchObject ) {
                                                System.out.println( "There are " +
                                                            doGetSuccessCounter + " doGet() successes!" );
                                    }
                        } catch( Exception x ) {
                                    synchronized( synchObject ) {
                                                doGetSuccessCounter--;
                                    }
                                    throw new ServletException( x.toString() );
                        }
            }
}
 
Notice in this example that we increment the doGetSuccessCounter integer in the doGet() method, and we decrement doGetSuccessCounter in the catch block – just for fun, we do not count a thread’s passage through doGet() as a “success” if an Exception is thrown.  Again we want to synchronize use of this shared resource (it is declared outside the method scope) so that we count and discount for every thread calling doGet(), without concurrent threads overwriting one another.  In addition, we synchronize around the report of the current value of doGetSuccessCounter so we are guaranteed to get the current value (after any other lock is released and we have exclusive access to the integer.)

Notice that in each case, when we set up the synchronized block, we specify that the synchronization lock be placed on an object named synchObject.  Any Java object can be used for synchronization locking, but not primitives – that is, we could lock on an Integer.class instance but not on an int primitive.  Our example instantiates an object of type Object named synchObject, and we use it for all the synchronized blocks.  Synchronized blocks must share the lock on a single object in order to coordinate their execution – so all our example synchronized blocks share the lock on synchObject.  Of all the synchronized blocks on synchObject, only one at a time may acquire the lock and execute.  The lock is automatically released at the end of the synchronized block.
You might observe that we have three separate synchronized blocks and that we could get rid of all of them if we just make the doGet() method synchronized.  I love code reduction and refactoring, but this is one case where it would be a very bad plan.  If we made the doGet() method synchronized, then only one browser could view our Servlet at a time; also, the entire doGet() would need to execute before the lock is released.  You always want to limit the amount of time and processing that is done in a synchronized block or synchronized method.  Look back at our examples, at the minimal synchronized block / method.

Also note that since synchObject is modified with the “static” keyword, all instances of the SynchServlet class will share the same lock – that is typically what you want for shared locks within a JVM.  If you also share the resource in another class (perhaps another servlet), you would lock on this same static object, like this:
            synchronized( SynchServlet.synchObject ) {
                        SynchServlet.doGetSuccessCounter++;
            }
 
Synchronized methods and synchronized blocks are foundational concurrency tools.  There are a number of other synchronization and concurrency techniques, many Thread-safe classes and the java.util.concurrent packages that may be also used.  All of them deal with synchronization within a single JVM.  Probably the clearest example of when all these techniques fall short is when you are running Java applications on separate computers and need to update a shared resource, like a file.

Typically, you might run an independent service to update the shared file.  You would provide a network port (or messaging) interface to the service so that all the applications that update the file can send messages to the service which would serve as a proxy to update the (shared) file on their behalf.  Alternatively, you could just update a database using JDBC.
However, it is possible to program synchronization into applications so that they can share and update a resource, even when running in separate JVMs.  In this case, we cannot share a lock on a Java object by using the synchronized keyword – no objects are shared between separate JVMs.  Instead we lock on a file.  We depend on the locking mechanisms inherent in the Operating System (OS) filesystem in order to synchronize our efforts.

Here is an example that shows how code running in separate JVMs can share updates to a file.  The shared file that we are updating is named “central.log”.  I’m using the escaped double backskash to indicate the file is found on a fileshare named \\server\dir.  I refer to another file named “lock.lock” on that same fileshare.  We will get an exclusive file lock on the “lock.lock” file before we do any updates to “central.log”.  Note that the method, centralLog() can be included in several applications, running in different JVMs, perhaps on different servers; and they will all be able to coordinate updates to the “central.log” file.
public class SynchServlet extends HttpServlet {
            public void init( ServletConfig config ) throws ServletException {
                        super.init( config );
                        centralLog( "Starting SynchServlet");
            }
 
            private static void centralLog( String message ) throws ServletException {
                        // Need something external to this JVM to test singularity
                        FileOutputStream fos = null;
                        FileLock fl = null;
                        PrintWriter centralOut = null;
                        try {
                            fos= new FileOutputStream( "\\\\server\\dir\\lock.lock" );
                            fl = fos.getChannel().tryLock();
                            // Null when can't get exclusive lock on file
                            while( fl == null ) {
                                      try {
                                                Thread.sleep(1000);
                                                fl = fos.getChannel().tryLock();
                                      } catch( Exception v ) {}
                            }
                            // At this point, I have exclusive lock on file!
                            centralOut = new PrintWriter( "\\\\server\\dir\\central.log" );
                            centralOut.println( message );
                        } catch( Exception x ) {
                                    throw new ServletException( x.toString() );
                        } finally {
                                    try {
                                                if( centralOut != null ) centralOut.close();
                                    } catch(Exception y) {}
                                    try {
                                                if( fl != null ) fl.release();
                                                if( fos != null ) fos.close();
                                    } catch(Exception y) {}
                        }
            }
}
 
There are three classes in the java.io package that can provide FileChannel objects: FileInputStream, FileOutputStream and RandomAccessFile.  In our example, we instantiate a FileOutputStream on the “lock.lock” file and call getChannel() to get the FileChannel object.  There are several methods to get an exclusive lock on a FileChannel.  We use the FileChannel.tryLock() method in our example.  If tryLock() is unsuccessful, it returns null, else it returns the associated FileLock object.  In our example, we call tryLock() then test the return value for null.  If it is null, we sleep for one second then try again, in the while( null ) loop.  When the FileLock is not null, we have an exclusive lock and can update shared resources.  It is very important that we release the FileLock.  For this reason, I have an independent try/catch block within the finally block that calls the release() method on the FileLock object.

I use a separate file, “lock.lock” for the FileLock, separate from the shared file that applications update, “central.log”.  We could alternatively establish the FileLock on the shared file.  I use a separate file for locking for a couple reasons: it makes the lock file job more obvious and explicit, and occasionally the object I’m sharing is not a file.  I have had to implement this style locking to accommodate a dedicated, single-user port: for example a serial port server, or in one bizarre situation, an FTP client that required a fixed port (per firewall rules), like this:
//package org.apache.commons.net.ftp;
package dac;
import org.apache.commons.net.ftp.*;
import org.apache.commons.net.ftp.parser.*;

public class FTPClient extends FTP {
           
            private int getActivePort() {
                    if( true ) return 11111; // requires ip / port filter permission
 
Let me mention the limitation of this file locking strategy.  It does not work between JVMs running on different OS architectures.  If all your JVMs are running on Windows or all your JVMs are running on UNIX, there is no problem; however, you cannot obtain an exclusive lock on a file from a Windows computer and have that lock observed on a UNIX computer, nor vice versa.  On the bright side, you can have a JVM on a Windows computer get an exclusive lock on a file that resides on a UNIX computer, and other Windows JVMs will observe the lock.  The same is true for JVMs on UNIX obtaining exclusive locks on files that reside on Windows.

There is also a risk that while a file is locked, the JVM may unexpectedly quit without completing the finally block and releasing the lock.  And there is a risk that if the locked file is on a remote computer, there may be a network failure, or the locking computer may unexpectedly reboot; which could leave the file in a locked state.  A key to avoiding this scenario is to only keep a file locked for the minimum time required.  Lock it, do your work, unlock it ASAP.
Thus ends part one of this story, Java Synchronization Across JVMs.  In part two, I will discuss flag-passing synchronization.

The complete code follows:

package dac;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.FileLock;
 
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class SynchServlet extends HttpServlet {
            private static final long serialVersionUID = 1L;
            private static PrintWriter fileOut; // Synchronize use of this!
            private static int doGetSuccessCounter = 0;
            private static Object synchObject = new Object();
 
            @Override
            public void init(ServletConfig config) throws ServletException {
                        super.init(config);
                        try {
                                    centralLog("Starting SynchServlet");
                                    fileOut = new PrintWriter("logfile.log");
                        } catch (Exception x) {
                                    throw new ServletException(x.toString());
                        }
            }
 
            public void destroy(ServletConfig config) {
                        fileOut.close();
            }
 
            @Override
            public void doGet(HttpServletRequest req, HttpServletResponse res)
                                    throws ServletException, IOException {
                        try {
                                    synchronized (synchObject) {
                                                doGetSuccessCounter++;
                                    }
                                    logWrite("Using doPost()");
                                    // …

                                    synchronized (synchObject) {
                                                logWrite("There are " + doGetSuccessCounter
                                                                        + " doGet() successes!");
                                                System.out.println("There are " + doGetSuccessCounter
                                                                        + " doGet() successes!");
                                    }
                        } catch (Exception x) {
                                    synchronized (synchObject) {
                                                doGetSuccessCounter--;
                                    }
                                    throw new ServletException(x.toString());
                        }
            }
 
            public void altGet(HttpServletRequest req, HttpServletResponse res)
                                    throws ServletException, IOException {
                        try {
                                    synchronized (SynchServlet.synchObject) {
                                                SynchServlet.doGetSuccessCounter++;
                                    }
                                    SynchServlet.logWrite("Some other message");
                                    // …

                                    synchronized (synchObject) {
                                                logWrite("There are " + doGetSuccessCounter
                                                                        + " doGet() successes!");
                                                System.out.println("There are " + doGetSuccessCounter
                                                                        + " doGet() successes!");
                                    }
                        } catch (Exception x) {
                                    synchronized (synchObject) {
                                                doGetSuccessCounter--;
                                    }
                                    throw new ServletException(x.toString());
                        }
            }
 
            @Override
            public void doPost(HttpServletRequest req, HttpServletResponse res)
                                    throws ServletException, IOException {
                        logWrite("Using doPost()");
                        // …

            }
 
            // synchronized instance method, not class method (static)
            private static synchronized void logWrite(String logEntry) {
                        fileOut.println(logEntry);
            }
 
            private static void centralLog(String message) throws ServletException {
                        // Need something external to this JVM to test singularity
                        FileOutputStream fos = null;
                        FileLock fl = null;
                        PrintWriter centralOut = null;
                        try {
                                    fos = new FileOutputStream("\\\\server\\dir\\lock.lock");
                                    fl = fos.getChannel().tryLock();
                                    // Null when can't get exclusive lock on file
                                    while (fl == null) {
                                                try {
                                                            Thread.sleep(1000);
                                                            fl = fos.getChannel().tryLock();
                                                } catch (Exception v) {}
                                    }
                                    // At this point, I have exclusive lock on file!
                                    centralOut = new PrintWriter("\\\\server\\dir\\central.log");
                                    centralOut.println(message);
                        } catch (Exception x) {
                                    throw new ServletException(x.toString());
                        } finally {
                                    try {
                                                if (centralOut != null) centralOut.close();
                                    } catch (Exception y) {}
                                    try {
                                                if (fl != null) fl.release();
                                                if (fos != null) fos.close();
                                    } catch (Exception y) {}
                        }
            }
}

 

Cross-Platform Java Details on Compiling and JNA - Getting Extended User Name, Windows and gecos


One of the great things about Java is cross-platform runtime execution of code .  For example, we can compile web applications to be run in Tomcat and can run them on both Windows and UNIX servers.  In addition to cross-platform web applications, I write utilities in Java, and I benefit from cross-platform runtime compatibility.
However, there are some pitfalls to watch out for!  For one thing, directory structures are different across platforms.  On UNIX systems, directory structures start at the root “/” and get assembled at mount points.  For example “/” might be a mount point for one drive, and “/usr” might be a mount point for a separate drive.  On Windows, the directory structure takes a different form with drive letters mapped to each drive, for example “C:” is usually the boot drive.  These generalizations are standard and typical; although, there’s always a new mousetrap, for better or worse.

Other areas of difference between Windows and UNIX systems are worth mentioning.  (1) The classpath separator character on UNIX is “:” while the classpath separator on Windows is “;”.  This is usually not a factor in Java code, but is a concern for getting your java runtime started.  (2) The directory (path) separator is forward slash (“/”) on UNIX and backslash (“\”) on Windows.  In Java code, it is always good to use the forward slash (UNIX style) path separator.  This will be handled correctly on both Windows and UNIX.  Because the backslash character has dual meaning (it’s a character and an “escape” character), to use it as a path separator, you need to double it, like “\\Windows\\System32”.  (3) The line separator character is different between UNIX and Windows.  Windows uses two characters: carriage return (CR, 0x0D, “\r”) and line feed (LF, 0x0A, “\n”); while UNIX only uses line feed.  The IO and NIO classes in Java which read lines (like java.io.BufferedReader.readLine()) will accommodate either line separator syntax, but if you intend to read lines manually (byte by byte) then you will need to be aware of this difference and handle it accordingly.
Now to the heart of this discussion...  There are certain aspects of Java that are not cross-platform capable.  These are few, and are not included in the standard Java library packages.  One thing that is done differently on different platforms is acquiring the user id and name.  You can imagine that Windows and UNIX keep user information in different formats.

A simple effort can be made to acquire the current userID from the Java system properties with a simple call like this:
String userID = System.getProperties().getProperty( "user.name" );

This approach is much better than reading the operating system environment, and I’m almost embarrassed to say that in my almost 2 decades of writing Java, I have done that more than once.  Here’s an example from code I wrote in 2008:
Process proc;
Runtime rt = Runtime.getRuntime();
if( props.getProperty( "os.arch" ).equals( "x86" ) &&
    props.getProperty( "os.name" ).startsWith( "Windows" ) )
{
    proc = rt.exec( "cmd /c set" );
} else {
    proc = rt.exec( "ksh -c set" );
}
InputStream is = proc.getInputStream();
int in;
while( ( in = is.read() ) != -1 ) {
// ... parse for User ID
 
But now I know that reading the Java system properties is cross-platform compatible; whereas, executing a Runtime and getting a Process to do work or get information is simply a way to turn Java into a scripting language – it is a role reversal, to be done only when the same job cannot be done in native Java.  With that philosophy, we will promote pure Java solutions for cross-platform tasks.

So, we are able to get the userID from the Java system properties, but how good is that?  It is not good at all!  If I take a second before running the Java code, and set the USERNAME environment variable to something else (“set USERNAME=fake” on Windows, “export USER=fake” in most UNIX shells), then the Java system properties will present this miss-set userID as if that were the real userID.  Note that this same frailty (security vulnerability) exists when reading User ID from the operating system environment settings.
A better way to do this would be to talk directly to the operating system (Windows or UNIX).  However, with the Java Virtual Machine (JVM) between your Java code and the native operating system, this becomes a bit more difficult.  One way to work around this difficulty in an enterprise environment is to call on a separate service, like an LDAP directory service, to provide the information.  And Java can readily do that, with sufficient credentials and access, and with the service available.  For cross-platform applications, there will probably be separate directory services available for each client platform.

But shouldn’t the local computer be able to tell me what I want?  The local machine knows my userID and already knows or can find out more details about me.  So our next job will be to have Java talk to the native operating system.
As I have said, the platform-specific sections of Java are kept outside of the standard Java library packages, but some are nevertheless included in the standard distribution library jar files.  For example, in the rt.jar file that comes with every Java Runtime Environment (JRE) there is a package named “com.sun.security.auth.module”.  You can tell this package is not a standard Java library, because the name does not start with “java”, like “java.lang” or “java.util”.  And it is for this very reason that certain Integrated Development Environments (IDEs) like Eclipse do not give ready access to classes in the “com.sun.*” packages, even though they are part of the standard distribution.  We will deal with that.

Let me assume that you are developing code using Eclipse on a Windows computer.  The standard JRE rt.jar library will include a class named “com.sun.security.auth.module.NTSystem”.  We will use it to find the current user’s identity like this:
String userName = (new com.sun.security.auth.module.NTSystem()).getName();

This will generate an error indicator in Eclipse, and will not be able to be compiled.  The error text is “Access restriction: The method getName() from the type NTSystem is not accessible due to restriction on required library”.  Basically, the class and method exist, but you’re not encouraged (or readily permitted) to use them.  To immediately resolve this problem, you can go to your Eclipse project Properties and add the local rt.jar file as an External Jar to your Java Build Path.  Note that rt.jar is in the default classpath, so once we get Eclipse to accept and compile the code, there is no problem running it.  However, as we shall see, that is true only on a Windows platform.
On a UNIX platform, the standard JRE distribution of rt.jar does not have the NTSystem.class file at all!  That makes sense, come to think of it, since NTSystem is not functional on UNIX (it is not cross-platform compatible).  As an alternative, in the UNIX JRE, we find this class that did not exist in the standard Windows JRE, “com.sun.security.auth.module.UnixSystem”.  We will use it like this:

String userName = (new com.sun.security.auth.module.UnixSystem()).getUsername();

However, now we are faced with another difficulty – that class doesn’t even exist on the Windows workstation where we are running Eclipse!  To remedy this, we need to copy the rt.jar file from a UNIX computer to our Windows workstation.  I recommend you rename it on the Windows box to something like UNIXrt.jar, so it’s clear what it is.  Then you can add that file as an External Jar to your Java Build Path in your project Properties.  Now that’s all well and good, and your code should be free from reported errors, but there are precautions we need to take.
Unless you are willing to always and only run your code from your Eclipse environment (not a very cross-platform approach), then you need to avoid mentioning classes that don’t exist.  If you run your code on a different Windows machine, or at the command prompt (without manipulating the classpath to point at the UNIXrt.jar file) then you will not have access to UnixSystem.class, and it is unmentionable (else runtime exceptions will ensue.)  And on a UNIX platform, NTSystem.class is unmentionable.  So how can you run this code cross-platform?  These are the steps we must take:

1)      Do not import these classes, rather refer to them with their complete package names, as shown in the code above.

2)      Determine what platform you are running on, and only attempt to execute code specific to that platform.
Here is a model I like to use for separating platform-specific code.  I use the Java System Properties to determine if I’m executing on a Windows box or not, and call the platform-specific code in an If/else block:

Properties props = System.getProperties();
String userName;
if( ( props.getProperty( "os.arch" ).equals( "x86" ) ||
    props.getProperty( "os.arch" ).equals( "amd64" ) ) &&
    props.getProperty( "os.name" ).startsWith( "Windows" ) )
{
    userName = (new com.sun.security.auth.module.NTSystem()).getName();
} else {
    userName = (new com.sun.security.auth.module.UnixSystem()).getUsername();
}

That code model examines two properties, operating system architecture and name.  The architecture can be either “x86” for 32-bit or “amd64” for 64-bit.  In this case, that is an indication of 32-bit or 64-bit implementation of Java, not an indication of the CPU type.  Also, the OS name, for Windows machines, can have several appearances, but they all start with “Windows”.
I spend a bit of text describing how NTSystem, UnixSystem and the Java Authentication and Authorization System (JAAS) package operates in the chapter on Single Sign-On in my book, Expert Oracle and Java Security, published by Apress.  Of course, I recommend that book.  Also see my current blogs at http://oraclejavasecure.blogspot.com/

So what have we done so far?  We have achieved cross-platform determination of current user identity from the operating system.  However, what if we are really after the user’s name, like “John Doe”?  Is that a value we can acquire from the local machine?  I should hope so, but for this effort, we will need to talk a bit more intimately with the operating system.   We will use Java Native Interface (JNI) programming to accomplish this, and we will use some existing utility code that was developed to make this effort easy, the Java Native Access (JNA) packages.  JNI is used, not to talk to operating systems, but rather to talk to other programming languages.  In this case, we will be talking to C language libraries that have the intimate kind of access to the operating system that we want to tap into.
To use JNA, you will need to browse to jna.java.net and download jna-3.3.0.jar and jna-3.3.0-platforms.jar.  Add these two files as External JAR files to your Java Build Path in your project Properties.  These will also need to be included in the classpath you configure for your java runtime from the command line or a script.

As is frequently the case when trying to work well with other computers or languages, a lowest-common-denominator (LCD) for data exchange must be established.  In JNI, the LCD data exchange is byte arrays.  Passing arrays of bytes can be easily done, even when both parties do not have a common understanding of a String.  So before getting the Windows extended user name via JNA, we define a byte array (char array) to hold it.  The following code passes our char array as the second parameter to the GetUserNameEx() method of the JNA class, Secur32.  That method returns the Windows extended user name in our char array.  For local use, we create a String from the char array and trim it to its significant characters.
char[] name = new char[100];
// Gets current user extended name
com.sun.jna.platform.win32.Secur32.INSTANCE.GetUserNameEx(
    com.sun.jna.platform.win32.Secur32.EXTENDED_NAME_FORMAT.NameDisplay,
    name, new com.sun.jna.ptr.IntByReference(name.length) );
userName = new String(name).trim();

I wish it were as easy as this to get the extended user name (“John Doe”) from UNIX and Linux computers, but the classes in the com.sun.jna.platform package that are specific for UNIX variants do not include that ability – probably because of the many implementations that would be required.
However, do not fret.  Similar C libraries that provide intimate access to UNIX platforms do exist, and with a little Java tailoring, we can acquire the same level of data as on Windows.  Here we will be using closer-to-the-bone JNI programming, using existing native libraries.   The JNA package gives us a friendly interface into those libraries.

Let’s start with the interface between Java and the specific C Library we want to address.  This example configures the interface that is specific to the Oracle Solaris platform.  Likely the only change you will have to make to accommodate other UNIX / Linux variants is in the list of fields included in the data structure returned from the library.
interface CLibrary extends Library {
    // Method to shadow C library getpwnam function
    passwd getpwnam(String username);
 
    // This matches the struct for passwd on Solaris
    // Modify to match passwd struct on other platforms
    public class passwd extends Structure {
        public String pw_name;
        public String pw_passwd;
        public int pw_uid;
        public int pw_gid;
        public String pw_age;
        public String pw_comment;
        public String pw_gecos;
        public String pw_dir;
        public String pw_shell;
    }
}

Notice that this interface extends the Library interface, included in the JNA package.  In our extension, we define one method, getpwnam() which shadows a function in the native C library which returns a data structure representing an entry in the system passwd datastore.  On Solaris, there are nine fields returned in this structure, and the “gecos” field contains data that is often the user’s name, but can be any text data specific to the user.  In my limited research, I’ve seen that “gecos” stands for “General Electric” so-and-so.  That’s hard for me to believe, but with other examples of UNIX utilities named for the initials of their authors, I guess I shouldn’t be surprised.  The gecos field is returned in the seventh field of our passwd.class, which extends the JNA Structure class.
To use our CLibrary interface, we instantiate a class based on it by calling the JNA Native.loadLibrary() method.  We tell that method to load the “c” native library, which includes the getpwnam function, and to wrap it in our CLibrary interface.  At that point we can call the getpwnam() method we defined in CLibrary.  Then we can access the pw_gecos member of the passwd instance returned by that method.  Note that this function reads the passwd datastore for a specified userID, so we are passing in the userID we acquired earlier.

CLibrary libc = (CLibrary)Native.loadLibrary("c", CLibrary.class);
// Gets named user gecos field from passwd struct
userName = libc.getpwnam(userName).pw_gecos;

I am including the complete code, below, for a class that executes cross-platform and acquires the extended username from the native platform.  Of course, all the significant code is shown previously in this article, along with instructions on how to set up your environment to edit and compile it.

package dac;

import java.util.Properties;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;

public class CrossPlatformUserName {

    public static void main(String[] args) {
        String userName = "";
        Properties props = System.getProperties();
        userName = props.getProperty( "user.name" );
        System.out.println( "A: " + userName );

        System.out.println( "Arch: " + props.getProperty( "os.arch" ) +
            ", OS: " + props.getProperty( "os.name" ) );
        // Some example reports of Arch and OS:
        //  Arch: amd64, OS: Windows 7
        //  Arch: x86, OS: Windows Vista
        //  Arch: sparc, OS: SunOS

        if( ( props.getProperty( "os.arch" ).equals( "x86" ) ||
            (props.getProperty( "os.arch" ).equals( "amd64" ) ) &&
            props.getProperty( "os.name" ).startsWith( "Windows" ) ) )
        {
            try {
                // Eclipse complains:
                // Access restriction: The method getName() from the type NTSystem
                //  is not accessible due to restriction on required library
                //  C:\dac\java\jdk1.6\jre\lib\rt.jar
                // Add that standard rt.jar to project properties compile build path
                //  to remove the restriction
                userName = (new com.sun.security.auth.module.NTSystem()).getName();
                System.out.println( "B: " + userName );

                // Certain Java Native Access (JNA) applications are available for
                //  various platforms.
                // One will allow us to read the Users full name from Windows
                // Include 2 jar files, download from jna.java.net, jna-3.3.0.jar
                //  and jna-3.3.0-platform.jar
                //  as external jars in your project properties, compile build path
                char[] name = new char[100];
                // Gets current user extended name
                com.sun.jna.platform.win32.Secur32.INSTANCE.GetUserNameEx(
                com.sun.jna.platform.win32.Secur32.EXTENDED_NAME_FORMAT.NameDisplay,
                name,new com.sun.jna.ptr.IntByReference(name.length) );
                userName = new String(name).trim();
                System.out.println( "C: " + userName );
            } catch( Throwable t ) {
                System.out.println( t.toString() );
            }
        }
        else {
            // ldaplist -l -v passwd $USER | grep gecos:
            // Eclipse on Windows machine, accessing Windows JDK/JRE does not find
            //  UnixSystem.class
            // Copy rt.jar from a UNIX machine, which will include UnixSystem
            //  then add that rt.jar as an External Jar to your project properties
            // Now you will be able to compile on Windows, even with this reference
            //  to a UNIX-specific class. Alternatively, you could create a
            //  local project, with empty UnixSystem class in the same package
            //  with an empty getUsername() method
            try {
                userName =
                    (new com.sun.security.auth.module.UnixSystem()).getUsername();
                System.out.println( "B: " + userName );
 
                CLibrary libc = (CLibrary)Native.loadLibrary("c", CLibrary.class);
                // Gets named user gecos field from passwd struct
                userName = libc.getpwnam(userName).pw_gecos;

                System.out.println( "C: " + userName );
            } catch( Throwable t ) {
                t.printStackTrace();
            }
        }
        System.exit(0);
    }
}

// Create our own platform-specific library
interface CLibrary extends Library {
    // Method to shadow C library getpwname function
    passwd getpwnam(String username);

    // This matches the struct for passwd on Solaris
    // Modify to match passwd struct on other platforms
    public class passwd extends Structure {
        public String pw_name;
        public String pw_passwd;
        public int pw_uid;
        public int pw_gid;
        public String pw_age;
        public String pw_comment;
        public String pw_gecos;
        public String pw_dir;
        public String pw_shell;
    }
}

Friday, September 27, 2013

Java Version in Oracle 12c

Oracle has had an embedded Java Virtual Machine (JVM) for several years, but it has always been a bit behind the current release of Java.  Prior to Oracle 11g, Java 1.4 JVM was the latest available and was called the Aurora JVM, in Oracle.  In Oracle 11g, finally Java 1.5 JVM was available, and it was based on the Java Security features of that release that I wrote my book, “Expert Oracle and Java Security”.  Now the production release of Oracle 12c is available, and the first thing I wanted to know is what version of the JVM was included.

To test what version of Java is available in Oracle 12c, I wrote this small Java class, and a Java Stored Procedure (Function) to encapsulate it.

CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED JSProc AS
public class JavaStoredProcedures {
    public static String getVersion() {
        return System.getProperty("java.version");
    }
}
/


CREATE OR REPLACE FUNCTION f_get_java_version
    RETURN VARCHAR2
    AS LANGUAGE JAVA
    NAME 'JavaStoredProcedures.getVersion() return java.String';


Running the Java Stored Procedure, I found that Java 1.6 was available in Oracle 12c. Yay!  Even though 1.7 is out and 1.8 is coming soon, new encryption algorithms that I pre-programmed for in the code of my book are now supported with 1.6, along with even more secure algorithms.  This is great!

SELECT f_get_java_version FROM DUAL;
-- 1.6.0 --

Cleaning up after running my Java Stored Procedure requires removing the function and the Java class.  Cleaning up after ourselves is part of good, secure programming.

DROP FUNCTION f_get_java_version;

DROP JAVA SOURCE JSProc;

Oracle 12c Multitenant Architecture Option

This is a very brief introduction and starter concepts for the Multitenant Architecture option available with Oracle 12c.  The idea is that you can host several Oracle instances on a single computer without separate virtual machines and while sharing core Oracle code – no multiple Oracle homes or installation directories.

This is accomplished by having ONE Container Database (CDB) which should not be used to support your database applications.  The CDB supports MANY Pluggable Database (PDB) instances.  The PDBs are equivalent to your separate Oracle home / database instances that you’re used to.

For example, if you elect to use the Multitenant Architecture option when you install Oracle 12c, you will end up with a CDB named “CDB$ROOT” and a PDB named, for example “PDBORCL”.  The CDB will be available via the base instance / service name, for example “ORCL”.  Normal installation will configure a Service or startup script to start the “ORCL” instance and the Listener.  However, the PDB(s) which are configured in the CDB are not started automatically (after a reboot).  This we can fix with a startup trigger.  Let’s examine these concepts a bit:

Connect to your Oracle 12c instance (as SYS) and view the containers that are available:

SELECT CON_ID, NAME, OPEN_MODE FROM V$CONTAINERS;

You will see something like the following table.  The PDB$SEED container is like a template that can be used when creating additional PDBs and when converting stand-alone Oracle instances into PDB containers.

1    CDB$ROOT    READ WRITE
2    PDB$SEED    READ ONLY
3    PDBORCL     MOUNTED

There are great resources in the Oracle Documents and on-line that you can use when you go on to create PDBs from scratch, from PDB$SEED or from a clone PDB; when you plug or unplug PDB into or from a CDB; and other CDB / PDB tasks.  I will not be discussing those items here.

You are, by default, in the CDB container, and you can return there from a PDB by setting your container:

ALTER SESSION SET CONTAINER = CDB$ROOT;

As I mentioned, after shutdown, the PDBs are not automatically started back up.  Use this command to manually change the status of a PDB from “MOUNTED” to “READ/WRITE”:

ALTER PLUGGABLE DATABASE pdborcl OPEN;

Typically, we would want to have all our PDB containers opened whenever the CDB is restarted, without manual intervention.  We can accomplish this with a startup trigger:

CREATE OR REPLACE TRIGGER SYS.g_after_startup AFTER STARTUP ON DATABASE
BEGIN
    -- Opens all pluggable databases
    EXECUTE IMMEDIATE 'ALTER PLUGGABLE DATABASE ALL OPEN';
END;
/

Before we move on to the PDB, let’s create a user within the CDB in order to demonstrate the naming requirements:

ALTER SESSION SET CONTAINER = CDB$ROOT;
CREATE USER tuser IDENTIFIED BY tpassword;

This generates an Oracle error, “ORA-65096: invalid common user or role name”.  All non-default users and roles in the CDB require the ugly naming convention prefix “C##”.  That helps distinguish items that you will see, merged into every PDB, but that may not exist when the PDB is plugged into a different CDB.  Typically, you would not create any items in the CDB – it is a “Container Database”, not a “Central Database”.  However, there may be exceptions to that plan, and there will be technical considerations for those exceptions that would need to be addressed and that we will explore here.  Let’s create a user and role, using “Common User” naming conventions, in the CDB to use as an example:

CREATE USER C##tuser IDENTIFIED BY tpassword;
CREATE ROLE C##create_session_role;
GRANT CREATE SESSION TO C##create_session_role;
GRANT C##create_session_role TO C##tuser;
SELECT * FROM ALL_USERS;

You will see our new user, C##tuser listed, with the designation “YES” in the COMMON column.  This user will appear in all PDBs.  Observe the data from ALL_USERS in the following commands – again the C##tuser user is listed as a COMMON user, within the PDB.  First, we switch containers to the PDB (e.g., pdborcl), still as the SYS user.  SYS is a default common user, and many of the “common” SYS views are merged with the “local” SYS views on the PDB.

ALTER SESSION SET CONTAINER = pdborcl;
SELECT * FROM ALL_USERS;

The common users exist in the PDB; however, the grants we made above only exist in the CDB!  To allow the common user to connect within the PDB, you need similar grants in that container:

GRANT CREATE SESSION TO C##create_session_role;
GRANT C##create_session_role TO C##tuser;

There is where it gets a bit convoluted – a system privilege (CREATE SESSION), specific to this PDB, is granted to a common role; and a common role is specifically granted in “this PDB only” to a common user.  Alternatively, and I recommend this alternate approach, unless there is some exceptional reason for using a common role, create a local (PDB) role to receive local system privileges.  If needed, grant the local role to a common user.

REVOKE CREATE SESSION FROM C##create_session_role;
REVOKE C##create_session_role FROM C##tuser;

CREATE ROLE create_session_role;
GRANT CREATE SESSION TO create_session_role;
GRANT create_session_role TO C##tuser;

You can now connect as this user in both the CDB and the PDB.  Note that there will be naming conventions in your connection strings, based on your Listener configuration that may not be consistent.  Again, I will direct you to Oracle and on-line sources for configuring your Listener.ora and related files.  Here are a couple example connections, using SQLPlus:

SQLPlus C##tuser@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)
(PORT=1521))(CONNECT_DATA=(SID=orcl)))

SQLPlus C##tuser@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)
(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=pdborcl.org.com)))

OK, now let’s see the limitations of data stored in the CDB, and how it will not be specifically beneficial to our database applications, running from PDBs.  We will create a local user while we are in the PDB container, then we will return to the CDB and create a table of data:

ALTER SESSION SET CONTAINER = pdborcl;
CREATE USER tuser IDENTIFIED BY tpassword;
-- or
-- CREATE USER tuser IDENTIFIED BY tpassword CONTAINER=CURRENT;
GRANT create_session_role TO tuser;

You could grant system privileges or a role with privileges to tuser in order to create tables, but we will just do this as SYS, for now.  Please refer to my book, “Expert Oracle and Java Security” from Apress for a thorough discussion of user and role security.

ALTER SESSION SET CONTAINER = CDB$ROOT;
ALTER USER c##tuser QUOTA 10M ON USERS;
CREATE TABLE C##tuser.test_table
    (text_col VARCHAR2 (256));
/
INSERT INTO C##tuser.test_table
    (text_col) VALUES ('test one');
SELECT * FROM C##tuser.test_table;
GRANT SELECT ON C##tuser.test_table TO tuser;

This last command generates the Oracle error, “ORA-01917: user or role 'TUSER' does not exist”.  Here is the first problem with data in the CDB; non-common, PDB users do not exist in the CDB, so they cannot be granted access to data in the CDB.  But, hey, the common user, C##tuser exists in the PDB, so let’s just read his data from there:

ALTER SESSION SET CONTAINER = PDBORCL;
SELECT * FROM C##tuser.test_table;

This generates the Oracle error, “ORA-00942: table or view does not exist”.  The common user account exists in the PDB, and with sufficient grants, the user can connect to the PDB, but his schema and all his data are in the CDB – not available within the PDB.  So, let’s try another tack.  Let’s create a link in the PDB to access data in the CDB, and try reading data through the link:

CREATE DATABASE LINK l_cdb_tuser
    CONNECT TO C##tuser IDENTIFIED BY tpassword
    USING '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)
    (PORT=1521))(CONNECT_DATA=(SID=orcl)))';
SELECT * FROM C##tuser.test_table@l_cdb_tuser;

That worked, so it is possible to read data in the CDB from a user session on the PDB over a database link.  Well, that’s no different from reading data over database links to other PDBs or non-Multitenant-Architecture Oracle instances.  There is no benefit to placing data in a CDB versus a PDB, and in fact there are strong reasons to argue that it should not be done.  You could, but should NOT do that, because the idea behind PDBs is that you can export them from one server and plug them into another server to run there without change.  That would hardly work if you were placing central data or code in the CDB.

Perhaps you can imagine a scenario where all PDBs need to share some centralized data, and that data is different on each server (CDB).  In that case, I suggest you do not put data in the CDB; but rather, create a “core” PDB on each CDB to hold that shared data.