Thursday, December 18, 2014

Writing your own logging service?

Application logging is one those things like favorite Editors war: everyone has their own opinions and there are endless of implemenations and flavors out there. Now a days, you likely would want to use something already available such as Log4j or Logback. Even JDK has a built in "java.util.logging" implementation. To avoid couple to a direct logger, many projects would opt to use a facade interface, and there is already couple good ones out there already, such as SLF4J or Apache Common Logging etc.

Despite all these, many project owners still want to try write their own logger service! I wondered if I were to ask and write one myself, what would it be like? So I played around and come up with this simple facade that wraps one of the logger provider (JDK logger in this case), and you can check it out here. With my logger, you can use it like this in your application:

 import zemian.service.logging.*;
 class MyService {
   Log log = LogFactory.createLog(MyService.class);
   public void run() {
     log.info(Message.msg("%s service is running now.", this));
   }
 }


Some principles I followed when trying this out:
  • Use simple names for different level of messages: error, warn, info, debug and trace (no crazy fine, finer and finest level names.)
  • Seperate Log service from implementation so you can swap provider.
  • Uses Message logging POJO as data encapsulation. It simplifies the log service interface.
  • Use log parameters and lazy format binding to construct log message to speed performance.
Do not go crazy with logging service implemenation make it complex. For example I recommend NOT to mix business logic or data in your logging if possible! If you need custom error codes to be logged for example, you can write your own Exception class and encapsulate there, and then let the logging service do its job: just logging.

Here are some general rules about using logger in your application that I recommend:
  •  Use ERROR log messages when there is reallyl a error! Try not to log an "acceptable" error message in your application. Treat an ERROR as critical problem in your application, like if it's in production, some one should be paged to take care of the problem immediately. Each message should have a full Java stacktrace! Some application might want to assign a unique Error Code to these level of messages for easier identification and troubleshoot purpose.
  • Use WARN log messages if it's a problem that's ignorable during production operation, but not good idea to supress it. Likely these might point to potentially problem in your application or env. Each message should have a full Java stacktrace, if available that is!
  • Use INFO log messages for admin operators or application monitors peoples to see how your application is doing. High level application status or some important and meaningful business information indicators etc. Do not litter your log with developer's messages and unessary verbose and unclear message. Each message should be written in clear sentence so operators knows it's meaningful.
  • Use DEBUG log messages for developers to see and troubleshoot the application. Use this for critical application junction and operation to show objects and services states etc. Try not to add repeated loop info messages here and litter your log content.
  • Use TRACE log message for developers to troubleshoot tight for loop and high traffic messages information.
  • You should select a logger provider that let you configure and turn these logging levels ON or OFF (preferrable at runtime if possible as well). Each level should able to automatically suppress all levels below it. And ofcourse you want a logger provider that can handle log message output to STDOUT and/or to FILE as destination as well.

Thursday, December 11, 2014

Getting started with Glassfish Server and Setting up SLF4J logging

Some notes I jot down while playing with GlassFish Server (3) for EE 6. You may get a working example here: https://github.com/saltnlight5/java-ee6-examples/tree/master/glassfish-logging-example

= Gettin started with Glassfish server

== Start server

1. cd $GF/bin
2. asadmin start-domain domain1

== Stop server

1. cd $GF/bin
2. asadmin stop-domain domain1

== Server Ports

Admin Console Application is at http://localhost:4848
 * Default setup has no user and password restriction!

Web applications is http://localhost:8080

== To create a new domain with diferent ports

1. cd $GF/bin
2. asadmin create-domain --portbase 9000 domain2

* If you accept default then again no password for admin user. After this, your
  admin console app is at http://localhost:9048 while your application is at
  http://localhost:9080

= Glassfish Server Setup

== How to setup SLF4J

1. Copy slf4j-api and slf4j-jdk jars into $GF/lib/endorsed
2. Edit $GF/domains/domain1/config/logging.properties and add your own logging package level
 mypackage.level=FINEST

== How to enable JSTL tag for all web applications

1. Copy jstl-1.2.jar into $GF/domains/domain1/lib

== How to add MySQL Driver for all applications

1. Copy mysql-connector-java-5.1.30-bin.jar into $GF/domains/domain1/lib

Tuesday, December 2, 2014

How to secure a web application by the app server container

There are many benefits to allow a container in managing users, groups and authentication policies. You may configure your WAR application to take advantage of this by adding the following in the WEB-INF/web.xml file

   <security-constraint>
        <web-resource-collection>
            <web-resource-name>webuser</web-resource-name>
            <url-pattern>/*</url-pattern>

        </web-resource-collection>
        <auth-constraint>
            <role-name>webuser</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>default</realm-name>
    </login-config>
    <security-role>
        <role-name>webuser</role-name>
    </security-role>


Above will secure the entire application and allow only users with "webuser" role to access it. The name "webuser" can be any name you want.

Each app server will manage users differently. In the case of WLS, it lets you create "user" and "user group", and then you can map the defined role above to the group. To do this, add the following to the  WEB-INF/weblogic.xml file.

<weblogic-web-app
    xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app
        http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd">

    <security-role-assignment>
        <role-name>webuser</role-name>
        <principal-name>webusergroup</principal-name>
    </security-role-assignment>
</weblogic-web-app>


Here we tell WLS that we map the "webuser" role defined in web.xml to use the "webusergroup", a WLS user group.

To create  user or user group in WLS, you may use the WLS Admin console web application. Go to the Security Realm and select the default "myrealm", and then select User or Group tab. Go ahead and add a user with password under a new group named "webusergroup". After this you can deploy your app, and it would prompt you for user and password whenever you try to access its URL.

You can find out more security info at https://docs.oracle.com/cd/E23943_01/web.1111/e13711/thin_client.htm#SCPRG171.

Sunday, November 2, 2014

How to use SSH tunneling to get to your restricted servers

Have you ever been told that in your network serverX can only be reached by a serverY via SSH? Now you have access to serverY from your own PC with normal SSH access as well, but just not directly to serverX.

What can you do in situation like this if you need to access the restricted serverY? Well you can always ssh into serverY, then ssh again into serverX to check your work or log or whatever. But what happen if you have a database server or WebLogic Server instance running in serverX; and you want your local PC's fancy tools to access the serverX? (Eg: Accessing the WLS admin console, or using SqlDeveloper to connect to your DB etc). In this case, that's where ssh tunneling can help you, and here is how.

 1. Establish a connection to your serverY that you have access to from your PC. On top of that and at the same time, you will create a tunnel to serverX (your restricted server) by letting serverY redirect all the network traffic data back to your local PC on a specific port. Sounds scary, but it can be done with single command. For example this is how I can access the WLS Admin Console app that was running on the restricted server X. On your own PC, open a terminal and run the following:

bash> ssh -L 12345:serverX:7001 serverY

Above will prompt you to access serverY with your normal ssh credential that you have access to. Once logged in, you need to keep the terminal open. Now the tunnel is established and redirecting traffic from port 7001 (where the WLS admin console is running) on serverX to your own PC on port 12345 via the tunnel of server Y.

2. Open a browser on your own PC and type in address http://localhost:12345/console

Now you should able to access your restricted serverX's WLS admin console!

Same can be done with a database server such as MySQL. For example, you will run  ssh -L 12346:serverX:3306 serverY and then change your SqlDeveloper JDBC connection url string to the tunnel port jdbc:mysql://localhost:12346/mydb

This is a cool technique to get around a secured environment.

Wednesday, October 29, 2014

How to setup custom SSLSocketFactory's TrustManager per each URL connection

We can see from javadoc that javax.net.ssl.HttpsURLConnection provided a static method to override with setDefaultSSLSocketFory() method. This allow you to supply a custom javax.net.ssl.TrustManager that may verify your own  CA certs handshake and validation etc. But this will override the default for all "https" URLs per your JVM!

So how can we override just a single https URL? Looking at javax.net.ssl.HttpsURLConnection again we see instance method for setSSLSocketFactory(), but we can't instantiate HttpsURLConnection object directly! It took me some digging to realized that the java.net.URL is actually an factory class for its implementation! One can get an instance like this using new URL("https://localhost").openConnection()

To complete this article, I will provide a simple working example that demonstrate this.

package zemian;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class WGetText {
    public static void main(String[] args) throws Exception {
        String urlString = System.getProperty("url", "https://google.com");
        URL url = new URL(urlString);
        URLConnection urlConnection = url.openConnection();
        HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
        SSLSocketFactory sslSocketFactory = createSslSocketFactory();
        httpsUrlConnection.setSSLSocketFactory(sslSocketFactory);
        try (InputStream inputStream = httpsUrlConnection.getInputStream()) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line = null;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        }
    }

    private static SSLSocketFactory createSslSocketFactory() throws Exception {
        TrustManager[] byPassTrustManagers = new TrustManager[] { new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }

            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }
        } };
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, byPassTrustManagers, new SecureRandom());
        return sslContext.getSocketFactory();
    }
}

Monday, October 27, 2014

How Servlet and JSP create sessions

In Servlet, you may get the Session object by "httpServletRequest.getSession(true)". The "true" flag will create the session if it doesn't already exist, else it gets the existing session.

Now if you want to check whether you have the session exists or not (without have to create one if doesn't exist), you need to pass in "false" and then check for "null".

Session session = httpServletRequest.getSession(false);
if (session == null) {
  // do something without creating session object.
}

Now comes the trick party. If you run above code and then dispatch the request to render a JSP page, you might quickly come to find out that the container will create a new Session object still! It turns out that by default JSP will create new Session object if you do not have one! To disable this, you need to set this explicitly on top of the JSP page:

 <% page session="false" %>

Only with this you will able to actually prevent creation of unnecessary Session object if you were to use JSP for output! Something to watch out for when debugging session based application.

Saturday, October 18, 2014

Poking around your REST application with a scriptable endpoint

I love the fact that JDK comes with a ScriptEngine. It's so flexible when you want to evaluate and troubleshoot your application that's already deployed in an server environment. Add this REST endpoint into a Java EE app, and it will give you instant access to internal states of the app.

package myrestapp;

import java.io.StringReader;
import java.util.logging.Logger;
import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;

/**
 * Give instant access to your internal application with dynamic scripting.
 *
 * <p>Example script:
 * <pre>
 * "sc" + servletContext + ", req=" + request;
 * </pre>
 *
 * <p>Example2
 * <pre>
 * names = servletContext.getAttributeNames();
 * while(names.hasMoreElements()) {
 *   name = names.nextElement();
 *   println(name);
 * }
 * </pre>
 *
 * <p>Example on how to import Java packages and classes.
 * <pre>
 * importPackage(Packages.java.text);
 * df = new SimpleDateFormat("MM/dd/yyyy");
 * dt = df.parse("01/01/2014");
 * </pre>
 */
@Path("script")
public class ScriptResource {
    private static final Logger logger = Logger.getLogger(ScriptResource.class.getName());
   
    @Context
    private ServletContext servletContext;
       
    @POST
    public String script(@Context HttpServletRequest request, String scriptText) throws Exception {
        String engineName = "JavaScript";
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName(engineName);
        logger.info("Running script text length=" + scriptText.length() + ", engine=" + engine);
        Object result = null;
        try (StringReader reader = new StringReader(scriptText)) {
            Bindings bindings = engine.createBindings();
            bindings.put("servletContext", servletContext);
            bindings.put("request", request);
            result = engine.eval(reader, bindings);
        }
        logger.info("Result " + result);
        return "RESULT=" + result;
    }
}


Notice that I gave couple JavaScript examples in the comment area already. You will have access to two binding variables that should give you full access to many internal components of your application. And here is a quick reference on scripting JDK 7.

Need an UI to test out this endpoint? How about give the "Advance Rest Client" Chrome Extension a try? (Thanks to my co-worker's Chris Griffith for showing me this cool extension. It's really handy tool to have!).

UPDATES (12/8/2014)
- If you are using FireFox, try the "RESTClient" add-ons.
- If you are using JDK7 Rhino JavaScript engine, here is a good ref for help: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino/Scripting_Java
- If you're in a EE6 web application and doesn't have REST enabled yet, then simply add a class like this to your application. It should automatically configure an endpoint that you can access as "http://localhost/rest/script".

package myrestapp;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

/**
 * This is the main entry into REST Application that bootstrap the provider.
 */
@ApplicationPath("rest")
public class RestApplication extends Application {
}