17 June 2011

Building and Running OpenJDK 7 on Mac OS X - A Breeze

Preparing a little for the Adelaide JUG meeting yesterday, where Java SE 7 was going to be the topic of discussion, I finally thought it was time to grab the OpenJDK 7 source, get it built and working on my Mac.

http://wikis.sun.com/display/OpenJDK/Mac+OS+X+Port

After checking off the pre-reqs, the process was as simple as following the small set of very clear instructions on the Wiki page.

The only small side excursion I had was that my bash environment had a JAVA_HOME env var set, which aborted the build at one point.  Unsetting that and restarting the build, it then ran flawlessly for about 30mins before out popped a shiny new Open JDK 7 build.

Having never manually installed a JDK on my Mac (Apple updates have served me well) the instructions for how and where to install it was very helpful. And so simple! Particularly with how it results in the new JDK being visible to the /usr/libexec/java_home utility:

sbutton:~ $ /usr/libexec/java_home -V
Matching Java Virtual Machines (4):
1.6.0_24-b07-334, x86_64: "Java SE 6" /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
1.6.0_24-b07-334, i386: "Java SE 6" /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
1.7.0, x86_64: "OpenJDK 7" /Users/sbutton/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home
1.7.0, i386: "OpenJDK 7" /Users/sbutton/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home

Voila!

sbutton:~ $ export JAVA_HOME=`/usr/libexec/java_home  -v 1.7`
sbutton:~ $ java -version
openjdk version "1.7.0-internal"
OpenJDK Runtime Environment (build 1.7.0-internal-sbutton_2011_06_16_11_39-b00)
OpenJDK 64-Bit Server VM (build 21.0-b15, mixed mode)  

 
Now looking forward to trying out some of the new language features with NetBeans 7.0.


All in all, a total breeze! Kudos to the OpenJDK Mac porters group.

14 June 2011

WebLogic Server: Listing Groups of an Authenticated User

Another question from a colleague today: "for a given authenticated user, how can I see what groups the user belongs to?" 

Yep, good question Pas. Not sure, but worth some time to find out.

Hunting through the WebLogic Security Documentation, I found a good description and diagram of how a Subject is related to a Principal.



A principal is an identity assigned to a user or group as a result of authentication. Both users and groups can be used as principals by application servers such as WebLogic Server. The Java Authentication and Authorization Service (JAAS) requires that subjects be used as containers for authentication information, including principals.

As part of a successful authentication, principals are signed and stored in a subject for future use.  

Any principal that is going to represent a WebLogic Server user or group needs to implement the WLSUser and WLSGroup interfaces, which are available in the weblogic.security.spi package.
Thus presumably, if you can get hold of the Subject, then you should be able to see the Principals associated with the User, of which some should reflect the Group(s) the User belongs to.

But how do you get the Subject?

I kept reading through documentation, but I couldn't see any really obvious description of where/how the Subject could be obtained. 

Then, after almost giving up and resorting to looking through the Javadoc, I found this small reference:
http://download.oracle.com/docs/cd/E17904_01/web.1111/e13711/thin_client.htm#i1030501

In Example 3-3, notice that the JSP is calling an API (request.getRemoteUser()) to get the name of the user that logged in.
A different API, weblogic.security.Security.getCurrentSubject(), could be used instead. To use this API to get the name of the user, use it with the SubjectUtils API as follows:

String username = weblogic.security.SubjectUtils.getUsername(
weblogic.security.Security.getCurrentSubject());
    
Note: In the actual doc, there's a missing opening parenthesis in the supplied code snippet.  I've added it in orange in the above.

Looking at the Javadoc, the weblogic.security.Security class has a static method that returns a javax.security.auth.Security object.

I also found a post from Edward Biemond here: http://blog.whitehorses.nl/2010/01/29/weblogic-web-application-container-security-part-1/ that discusses this area, from a slightly different more practical perspective perhaps.

With the Subject accessible and the description of the WLUser and WLGroup interfaces above, the group(s) an authenticated user belongs to can be readily discovered.
    
    import weblogic.security.Security;
    import weblogic.security.spi.WLSUser;
    import weblogic.security.spi.WLSGroup;
    ...
    private void doSubjectStuff(PrintWriter out, 
      HttpServletRequest request, 
      HttpServletResponse response) {

        Subject subject = Security.getCurrentSubject();
       
        StringBuffer groups = new StringBuffer();
        String user = null;
        boolean first = true;


        for(Principal p: subject.getPrincipals()) {
            if(p instanceof WLSGroup) {
                if(first) {
                    first=false;
                } else {
                    groups.append(", ");
                }
                groups.append(p.getName());
            } else if (p instanceof WLSUser) {
                user = p.getName();
            }
        }
       
        out.printf("<p>RemoteUser: %s, User: %s, belongs to: %s</p>",
                request.getUserPrincipal().getName(),
                user,
                groups);
    }



Calling this from a servlet, deployed within a simple web application that uses BASIC authentication mapped to a couple of WebLogic Server groups, the following output is displayed when the user "steve" logs in:
RemoteUser: steve, User: steve, belongs to: AppTesters, Deployers

Note, as Edmond also points out, the groups that are provided in the Subject are the WebLogic Server groups the user belongs to, and not the logical role-name defined in the web.xml file.
web.xml:

    <security-role>
        <role-name>ADMINS</role-name>
    </security-role>
weblogic.xml:

  <security-role-assignment>
    <role-name>ADMINS</role-name>
    <principal-name>AppTesters</principal-name>
    <principal-name>Administrators</principal-name>
  </security-role-assignment>
The primary new takeaway point I got from this was the use of the weblogic.security.Security class to obtain an instance of the current Subject.

07 June 2011

Using SLF4J with WebLogic Server Logging

Just recently, I needed to take a look at the SLF4J (Simple Logging Facade 4 Java) API, and more specifically, to how it could be employed on WebLogic Server.

Simple Logging Facade for Java

http://www.slf4j.org/

SLF4J follows the Commons-Logging model in that it provides a "Simple Logging Facade" with an API that can be used to insert log messages into application code, without specifying the specific Logging framework that is to be used. The actual work of capturing and emitting the log message is handled by "pluggable" SLF4J binding, which are added at deployment/runtime time (or server start) and handle the actual logging implementation work. There are pre-built bindings for the common logging frameworks such as JDK logging, Log4J, LogBack, as well as a simple Logging implementation that appears to simply print log messages to stdout.

Basic Problem

Having a look at a project that was using SLF4J with WebLogic Server, the question that came up was how could we direct the SLF4J logs into the standard WebLogic Server log facilities.

In it's basic form, with the slf4j-simple-1.6.0.jar binding being used, the SLF4J logs were not consistent with the rest of the WebLogic Server logs.

<Jun 7, 2011 3:22:04 PM CST> <Notice> <WebLogicServer> <BEA-000395> <Following extensions directory contents added to the end of the classpath:
/Users/sbutton/Projects/Domains/wls1035/lib/slf4j-api-1.6.0.jar:/Users/sbutton/Projects/Domains/wls1035/lib/slf4j-simple-1.6.0.jar>
<Jun 7, 2011 3:22:05 PM CST> <Info> <WebLogicServer> <BEA-000377> <Starting WebLogic Server with Java HotSpot(TM) 64-Bit Server VM Version 19.1-b02-334 from Apple Inc.>
<Jun 7, 2011 3:22:05 PM CST> <Info> <Management> <BEA-141107> <Version: WebLogic Server 10.3.5.0 Fri Apr 1 20:20:06 PDT 2011 1398638 >
. . .
. . .
<Jun 7, 2011 3:22:13 PM CST> <Notice> <WebLogicServer> <BEA-000331> <Started WebLogic Admin Server "myserver" for domain "mydomain" running in Development Mode>
<Jun 7, 2011 3:22:13 PM CST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to RUNNING>
<Jun 7, 2011 3:22:13 PM CST> <Notice> <WebLogicServer> <BEA-000360> <Server started in RUNNING mode>
14 [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] INFO com.oracle.demo.wlsslf4j.TestServlet - *** This is an info: class com.oracle.demo.wlsslf4j.TestServlet
14 [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] WARN com.oracle.demo.wlsslf4j.TestServlet - *** This is a warning: class com.oracle.demo.wlsslf4j.TestServlet
14 [[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'] ERROR com.oracle.demo.wlsslf4j.TestServlet - *** This is an error: class com.oracle.demo.wlsslf4j.TestServlet

The question was: without writing a specific WLS binding, is there a way to enable the SLF4J logs to be directed into the WebLogic Server logs so they are consistently formatted and able to be viewed in the WebLogic Server console with the rest of the logs?

One Solution

The solution was quite straightforward in the end, simply by combining two pieces of existing functionality:

1. The SLF4J distribution ships with a binding for JDK logging called slf4j-jdk14-1.6.0.jar. This results in the SLf4J logs being directed into the JDK logging framework.

2. WebLogic Server ships with a JDK logging handler which will pick up log messages emitted from JDK logging framework and direct them into the WebLogic Server logging system.

The weblogic.logging.ServerLoggingHandler is documented here: http://download.oracle.com/docs/cd/E14571_01/web.1111/e13739/logging_services.htm#CHDBBEIJ

To get it all working, all it required was making the relevant libraries available to WebLogic Server and wiring them together using a logging.properties file.

Copy Libraries into WebLogic Server

SLF4J basically comes as two components. There's the API which is used by applications, and then there's the bridgings that handle the actual production of log messages into the desired form.

To make SLF4J available to WebLogic Server, I employed the simplest way I know - copy them into the domain/lib directory. This has a certain set of limitations in terms of updating versions without a server restart, etc. but it's a really simple way to get things working in the first instance.

sbutton:~/Projects/Domains/wls1035 $ ls -l lib/
total 88
-rw-r--r-- 1 sbutton staff 702 6 Jun 13:15 readme.txt
-rw-r--r-- 1 sbutton staff 25496 7 Jun 15:19 slf4j-api-1.6.0.jar
-rw-r--r-- 1 sbutton staff 8887 7 Jun 15:34 slf4j-jdk14-1.6.0.jar

Create a logging.properties file

The logging.properties file is a standard way to control JDK logging. Since we have directed SLf4J logs into JDK logging framework by making the slf4j-jdk14-1.6.0 binding available, we can configure JDK logging to use the ServerLoggingHandler provided by WebLogic Server, so as to direct its logs into the WebLogic Server logging system.

The JDK logging framework and its configuration options are all well documented, starting from Nov 2001 here: http://download.oracle.com/javase/1.4.2/docs/guide/util/logging/overview.html

Here is the logging.properties file that was used:
####
# Specify the handlers to create in the root logger
handlers = weblogic.logging.ServerLoggingHandler

# Register handlers for the com.oracle.demo package and its child loggers
com.oracle.demo.handlers = weblogic.logging.ServerLoggingHandler

# Do not send the org.hibernate.validator log messages to the root handler
com.oracle.demo.useParentHandlers = false

# Set the default logging level for the root logger
.level = ALL
com.oracle.demo.level = ALL

# Set the default logging level for new ServerLoggingHandler instances
weblogic.logging.ServerLoggingHandler.level = ALL

To direct the JDK logging framework to use the logging.properties file, the standard System property java.util.logging.config.file is used. With WebLogic Server, this can be easily accomplished by setting the JAVA_OPTIONS System property with the corresponding value.



$ export JAVA_OPTIONS="-Djava.util.logging.config.file=/Users/sbutton/Projects/Domains/wls1035/logging.properties"


Start WebLogic Server and Observe

With the libraries copied into the domain/lib directory, the logging.properties file created and specified as $JAVA_OPTIONS, starting WebLogic Server should now allow the logs created via SLF4J to be directed into the WebLogic Server logging framework. This should let them be seen on stdout in a consistent form with the standard WebLogic Server log messages, as well as via the WebLogic Server console through it's log viewer.
SL4J logs in stdout
<Jun 7, 2011 3:52:01 PM CST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to RUNNING>
<Jun 7, 2011 3:52:01 PM CST> <Notice> <WebLogicServer> <BEA-000360> <Server started in RUNNING mode>
<Jun 7, 2011 3:52:07 PM CST> <Warning>
<com.oracle.demo.wlsslf4j.TestServlet> <BEA-000000> <*** This is a warning: class com.oracle.demo.wlsslf4j.TestServlet>
<Jun 7, 2011 3:52:07 PM CST> <Error> <com.oracle.demo.wlsslf4j.TestServlet> <BEA-000000> <*** This is an error: class com.oracle.demo.wlsslf4j.TestServlet>


SL4J logs in WebLogic Server console