HTTP/2 server push

HTTP/2 general

In general HTTP/2 is about optimizing server resource usage. This is mainly achieved by using 1 connection between server and client and re-using that connection for all requests/responses for the duration of the session.

This is in sharp contrast with HTTP/1. Where a connection is created, a request is sent, a response is received and the connection is terminated. This is overhead is more or less hidden to the user because multiple connections are used in parallel.

HTTP/2 server push

HTML pages reference many other resources. Using HTTP/1 the client needs to parse the HTML page, identify the referenced resources and fetch them. Every round-trip incurring the connection setup/breakdown overhead.

Using HTTP/2 the server can send these referenced resources to the client before they are needed and because the same connection is used there is no connection setup/breakdown overhead.

Use cases

Pushing page resources before they are needed will make a site/application more responsive to it’s users. But doing this manually for all pages of an application is only going to be feasible for the smallest of applications.

Web/ UI component frameworks may push framework resources that are needed. Multiple approaches can be taken in this space. All framework resources can be pushed or only the resources that are needed based on the components of the framework that are used.

What is available

Since HTTP/2 is a draft spec, it is still early days for HTTP/2. Currently there is no standard Servlet API but that can’t stop us, Jetty already has an experimental API.

Google Chrome Canary has support for HTTP/2 when started with –enable-spdy4 as start parameter.

Firefox has support for HTTP/2 when the network.http.spdy.enabled.http2draft is switched on.

Test case

In order to test server push I’ve taken one of my panoramic vacation photos and sliced it up into 400 parts. This may be a little over the top but as with all tests we want to test the limits.

The test has been executed using 2 web-modules:

  • blog-http1-no-push – containing a servlet on URL /nopush that does not perform any pushes.
  • blog-http2-push – containing a servlet on URL /push that executes server pushes for the image slices.

The blog-http1-no-push web module was deployed to a Jetty server containing only the http, annotations and deploy modules running on port 8080.

The blog-http2-push web module was deployed to a Jetty server containing only the http2, annotations and deploy modules running on port 8443.

Both these setups are available as Docker images:

Both web modules contain a single servlet. The servlets take a rows & columns attribute as parameters. This allows us to control the amount of resources that are contained in the generated page. They also control the amount of resources that are pushed by the blog-http2-push web module.

During testing I did notice that the server sometimes becomes unstable when trying to push all 400 image slices. I’ve contacted the jetty users mailing list, perhaps some additional configuration needs to be set when pushing a lot of resources. I’m waiting for their reply.

How do you use HTTP/2 in code

Initially there was a push method on the Dispatcher class, but while writing this blog the Jetty project deprecated that method and made a PushBuilder available via the Request class.

final Request jettyRequest = (Request) getRequest();

jettyRequest
    .getPushBuilder()
    .push(resourcePath);

Checkout the sources on Github https://github.com/teyckmans/http2-push

Performance difference

In order to have a correct test case I’ve deployed the Docker images in the Google cloud in the us-central1-a zone in order to have real network overhead. Measurements have been taken with cache disabled in Google Chrome Canary using the load number.

HTTP/1 – no push

http://%5Bhost-external-ip%5D:8080/blog-http1-no-push/nopush?rows=5

3.01s (average of 6)

HTTP/2 – push

https://%5Bhost-external-ip%5D:8443/blog-http2-push/push?rows=5

1.51s (average of 6)

Pretty spectacular difference if you ask me.

Do It Yourself

I’ve uploaded the Docker images to the docker hub so you can try it out and experience the difference yourself.
Use the following command lines to run the test web-modules.

HTTP/1 – no push
docker pull teyckmans/blog-http1-no-push
docker run –name blog-http1-no-push-1a -i -t -p 8080:8080 teyckmans/blog-http1-no-push

HTTP/2 – push
docker pull teyckmans/blog-http2-push
docker run –name blog-http2-push-1a -i -t -p 8443:8443 teyckmans/blog-http2-push

5 hidden gems in Java 8

There’s been a lot of attention for the major new features of Java 8: Lambdas, the streaming API and the new Date and Time API. Of course these are the ones that make this a game changing release, but there’s more to Java 8. Inspired by José Paumard´s Devoxx talk 50 new things we can do with Java 8 I’d like to shed some light on 5 smaller features that will make your life as a Java developer easier. I won’t go 50 like José (and actually… neither did he), but these are the ones you definitely need to see.

Join me!

So we probably all had our fair share of recreating the same boilerplate code over and over again: iterating over a list of values in order to concatenate them with a delimiter to a single String. In Java 7 this would probably look something like this:

List<String> values = ...
StringBuilder result = new StringBuilder("[");
boolean first = true;
for(String item : values) {
  if(first) {
    first = false;
  } else {
    result.append(", ");
  }
  result.append(item);
}
result.append("]");
System.out.println(result.toString());

While this is certainly more concise and more readable than how the code would have looked in the 1.4 era, before generics and enhanced for-loops, but it still is a hideous pile of boilerplate for something very simple. So now for the Java 8 solution:

StringJoiner joiner = new StringJoiner(", ", "[", "]");
values.forEach(joiner::add);
System.out.println(joiner.toString());

This actually showcases not only the new StringJoiner, but two other Java 8 features as well: method references and the forEach() method on the Iterable interface.

While StringJoiner is actually meant for some behind-the-scenes processing for a Collector in the streaming API (http://blog.joda.org/2014/08/stringjoiner-in-java-se-8.html), it does eliminate a lot of boilerplate in more traditional code.

Longing for hashCode

When implementing your own hashCode() method for a class that has long fields, in the past you had to calculate the hash yourself or wrap the long value in a Long object and then call its hashCode() method. In order to avoid unnecessary creation of objects, Java 8 allows you to use the static method Long.hashCode(long value) for this.

Line it up!

Java 7 gave us the very convenient Files class with a lot of useful static helper methods for working files, amongst which Files.readAllLines(Path path). This class is also updated to make the best use of the Streaming API. So now we get the even more useful Files.lines(Path path), which does not return a List of all the lines, but a Stream. This is probably a better programming model in almost all cases. When you read all the lines in a file, you will probably want to process them somehow instead of keeping them in memory. Below an example of reading all the lines in a file and printing out only those lines that start with an “A”.

Path file = Paths.get("path", "to", "file.txt");
try (Stream<String> lines = Files.lines(file)) {
  lines
    .filter(s -> s.startsWith("A"))
    .forEach(System.out::println);
} catch (IOException ioe) {
  // ...
}

Repeat after me

A new feature that will probably find most of its use in the Java EE world, is @Repeatable. By annotating your annotation type with the meta-annotation @Repeatable, it can be placed at the same element more than once. Under the hood this still wraps the separate annotations in a container annotation, but it reads a lot better.

Since the annotation is not used within Java SE 8 itself, there are only a lot of imaginary examples circulating on the internet. But then again this feature was introduced with Java EE in mind. So below snippet (derived from the Java EE 7 spec) will likely be a valid JAX-B example in Java EE 8:

public class Foo {
  @XmlElement(name="A", type=Integer.class)
  @XmlElement(name="B", type=Float.class)
  public List items;
}

Which will below the surface be translated to the current notation:

public class Foo {
  @XmlElements(
    @XmlElement(name="A", type=Integer.class),
    @XmlElement(name="B", type=Float.class)
  )
  public List items;
}

By default

Default methods on interfaces are often presented as a by-product of Lambda’s, but they can make it a lot easier to create a sustainable future-proof API. A default method is a method on an interface of which the (default) implementation is already provided.

Say for instance your public API exposes the following interface:

public interface Foo {
  void addListener(FooListener listener);
}

Say for instance that you want to add the possibility to add multiple listeners in one go without breaking the implementations of your customers. This can be achieved by adding a default method:

public interface Foo {
  void addListener(FooListener listener);

  default void addListeners(Collection<FooListener> listeners) {
    listeners.forEach(this::addListener);
  }
}

And many more

While I stick to five here, there are many, many more additions to Java 8. You can find the full list here: http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html.

New in Java 8 : Consumers and Predicates : a simple introduction

The java.util.function package is new in Java 8 and contains interfaces that are used for lambda expressions and functional references. In this blog, I give a brief introduction of  2 interfaces of this package :

  • Consumer
  • Predicate

For the examples in this blog, we have a list of invoices with name, amount and category :

public class Invoice {
   private String name,amount,category;
   public Invoice(String name,String amount,String category) {
     super;
     this.name=name;
     this.amount=amount;
     this.category=category;
   }
   public String getName() {
     return this.name;
   }
   public String getAmount() {
     return this.amount;
   }
   public String getCategory() {
     return this.category;
   }
}

To generate a list of invoices, we’ll use the following method:

public static List<Invoice> generateInvoices()  {
   List<Invoice> list = new ArrayList<Invoice>();
   list.add(new Invoice("Oracle","1000","SOFTWARE"));
   list.add(new Invoice("Microsof","30000","HARDWARE"));
   list.add(new Invoice("Apple","5000","SOFTWARE"));
}

Consumer

A Consumer is an interface that ‘consumes’ an object. It takes an argument and does something with it. It does not return a result.

The Consumer interface has 2 methods :

  • void accept(T t) : contains the code that is executed on t
  • default Consumer<T> andThen(Consumer<? super T> after) : This method returns a consumer that is executed after the previous one and enables you to execute a chain of consumers.

For this demo, we are using the (new in Java8) method of the Collection API :

Collection.forEach(Consumer<? super T> action)

This method executes the consumer ‘action’ on every item of the collection.

First we create 2 methods that each return a Consumer object. The first will print the name of the invoice, the second prints the amount.

Finally we use these 2 methods in a Collection.foreach method.


public static Consumer<Invoice> printName() {
    return new Consumer<Invoice>() {
         public void accept(Invoice invoice) {
           System.out.println(invoice.getName());
         }
    };
}

public static Consumer<Invoice> printAmount() {
    return new Consumer<Invoice>() {
         public void accept(Invoice invoice) {
           System.out.println(invoice.getAmount());
         }
    };
}

generateInvoices().forEach(printName().andThen(printAmount());

As you can see in the last line, first the printName() is executed, and then the printAmount(). This line will print the following :
Oracle
1000
Microsoft
3000
Apple
5000

When an error occurs in the foreach method, an exception is thrown, and further processing of the List stops.

Predicate

A Predicate is an interface that is used to assign lambda expressions. It has a functional method :

boolean Test(T t )

Predicates are used as stream operations. Stream operations can be executed on Collections in order to execute complex data processing queries. But in this blog we’ll keep it simple, we just want to select all the invoices with category=’HARDWARE’, and put them in a new List.

Using a predicate in combination with the new Streams API, will simplify and shorten our code and make it more readable.

First we define our predicate, and then we’ll use it on our List of invoices. The stream method will filter the List using our predicate, and then collect the items that fulfill the predicate in a new List.

public static Predicate<Invoice> isHardware() {
     return i -> i.getCategory().equals("HARDWARE");
}

List<Invoice> listHardware = generateInvoices().stream.filter(isHardware()).collect(Collectors.<Invoice>toList());

Our new list will now contain 1 invoice, the one from Microsoft which has ‘HARDWARE’ as category.
As you can see, Predicate is a class that contains a function that we can pass to other classes. Actually it is just a reference to a function, AKA ‘a function reference’.
With Streams, you also sort and map data, before collecting, but that’s for another blog.

So that’s it for now. I hope this blog has shown that, by using Consumers and Predicates, our code will become shorter, cleaner and more readable.

 

Introduction to Websockets and JSON-P API in JEE7

Websockets (JSR 356) and the JSON-Processing API (JSR 353) are both introduced in the JEE7 specification. Together with JavaScript an HTML5, they enable web applications to deliver a richer user experience.

Websockets allow you to communicate bidirectional and full duplex over TCP, between your server and different kind of clients (browser’s, JavaFX… ). It’s basically a push technology, where, for example events or data originating from the server or a client, can be pushed to all the other connected clients.

In our demo , JSON strings are send between client and server, so that’s where the JSON Processing API comes in. It’s a  portable API that allows you to parse, generate, transform and query JSON by using the streaming or model API. But you could also send XML or any other proprietary format.

Serverside components

  1. A java class annotated with
    @ServerEndpoint(value=”/endpoint”, decoders=EncodeDecode.class, encoders=EncodeDecode.class)
    with following method annotations :
    @OnOpen : when connections is open
    @OnMessage : when a message comes in
    @OnClose : when a message is closed
  2. A java class that encode/decodes the message from/to JSON and Java object. (That’s where the JSON-P API comes in).

Clientside component

An html file that contains JavaScript to communicate with our server endpoint. Communication is done through a WebSocket object, declared as follows :

connection = new WebSocket(‘ws://localhost:8080/mywebsocket/endpoint’);

will trigger the @OnOpen method of our server side endpoint.

connection.onmessage : fired when a message comes in

connection.send : will trigger the OnMessage annotated method of our endpoint

connection.close : will trigger the OnClose annotated method of out endpoint

Demo

It’s a screen that sends messages to all the connected clients, including itself. When the client opens a connection on the server, his session is added to a list of active sessions. When a client sends a message to the server, it is distributed to all the sessions in the list. When the client closes his browser tab or window, his session is removed from the list. The data that we send, can be any complex JSON or XML model. To keep it simple, we just send a simple string.

This application needs to be deployed on a JEE7 compliant servet. So at this moment (May 2014) it will only run on Glassfish 4.0 or WildFly 8.

The war file can be found here. After deployment, open url (for Glassfish) http://localhost:8080/mywebsocket/socket.html.

 The Code

Java endpoint

package be.iadvise.mywebsocket;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value="/endpoint", decoders=EncodeDecode.class, encoders=EncodeDecode.class)
public class MyEndPoint {
 // contains list of active sessions
 private static List<Session> sessions = Collections.synchronizedList(new ArrayList<Session>());

 @OnOpen
 public void onOpen (Session s) {
 sessions.add(s);
 System.out.println("Open session : no of sessions = "+sessions.size());
 }

 @OnMessage
 public void onMessage (MyMessage msg, Session s) throws IOException, EncodeException {
 for (Session session : sessions) { // loop over active sessions and send the message.
 session.getBasicRemote().sendObject(msg);
 }
 }
 @OnClose
 public void onClose (Session s) {
 sessions.remove(s); // remove session from the active session list.
 }
}

Java Decode/Encode message

package be.iadvise.mywebsocket;

import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.stream.JsonGenerator;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

/**
 * This class will encode/decode the messages from/to the client.
 * Decoder : from client to server -> converts the JSON to MyMessage object
 * Encoder : from server to client -> converts MyMessage object to JSON
 *
 * We are using JSON, but you can use XML or any other format.
 */
public class EncodeDecode implements Decoder.Text<MyMessage>, Encoder.Text<MyMessage> { 

 @Override
 public MyMessage decode(String txt) throws DecodeException {
 Reader reader = new StringReader(txt);
 JsonReader jsonReader = Json.createReader(reader);
 JsonObject object = jsonReader.readObject();
 String text = object.getJsonString("text").getString();
 return new MyMessage (text);
 }

 //Check if decode is possible. If not, return false
 @Override
 public boolean willDecode(String s) {
 System.out.println("Will decode asked for " + s);
 return true;
 }

 @Override
 public void init(EndpointConfig config) {
 System.out.println("init called on chatdecoder");
 }

 @Override
 public void destroy() {
 System.out.println("destroy called on chatdecoder");
 }

 @Override
 public String encode(MyMessage object) {
 System.out.println("I have to encode " + object);
 StringWriter sw = new StringWriter();
 JsonGenerator generator = Json.createGenerator(sw);
 generator.writeStartObject();
 generator.write("text", ((MyMessage)object).getText());
 generator.writeEnd();
 generator.flush();
 String answer = sw.toString();
 System.out.println("I encoded an object: " + answer);
 return answer;
 }
}

Java message


package be.iadvise.mywebsocket;

public class MyMessage {
private String text;
public MyMessage(String text) {
super();
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@Override
public String toString() {
return "MyMessage [text=" + text + "]";
}
}

The html file


<html>
<head>
<script language="javascript">
var connection;
var me;
function openSocket() {
connection = new WebSocket('ws://localhost:8080/mywebsocket/endpoint');
connection.onmessage = function(evt) {
var x = JSON.parse(evt.data);
mytext = x.text;
var chld = document.createElement("p");
chld.innerHTML = mytext;
var messages = document.getElementById("messages");
messages.appendChild(chld);
}
}

function talk() {
var txt = document.getElementById("msg").value;
var message = {
'text':txt
};
connection.send(JSON.stringify(message));
}
function closeSocket() {
alert('closing socket')
connection.onclose = function () {}; // disable onclose handler first
connection.close();
}
</script>

<script type="text/javascript">
if (window.addEventListener) { // all browsers except IE before version 9
window.addEventListener ("beforeunload", closeSocket, false);
}
else {
if (window.attachEvent) { // IE before version 9
window.attachEvent ("onbeforeunload", closeSocket);
}
}
</script>
</head>
<body onLoad="openSocket();">
<p>
SimpleWebSocket
</p>
<!-- <table id="chatbox" style="display:none"> -->
<table id="chatbox">
<tr><th width="400">messages</th></tr>
<tr>
<td width="400" id="messages">
</td>
</tr>
<tr>
<td>
<input type="text" id="msg"/>
<input type="submit" value="send" onclick="talk(); return false;"></input>
</td>
</tr>
</table>
</body>
</html>

 Conclusion

Websockets are a huge improvement for building rich applications. This is the first time that push technology is actually build in the JEE framework. Before that, we had to use polling or other techniques in order to get the same results. In this blog, I showed that you don’t need much code to start off. Once you get this working, you can gradually go further building more complex sockets.

 

Caching in a JEE : don’t write it yourself, use LoadingCache from Google Guava libraries.

Caching data is something you use in almost every JEE project. Most of the time it’s pretty simple : put your data in a .properties file and use a PropertyManager to fetch the data.

But that’s not very flexible and manageable. Updating the values means, updating your property file, repackaging the ear file, and redeploying, and only developers can update the data.

Putting the data in JNDI entries, and using JNDI lookups may solve the problem of redeploying, but if you got a few 100 properties, it’s still not very manageable.

Most of the times, JNDI entries are entered via some application server console which, in a production environment, is not accessible for your users who need to manage this data.

So lets put the data that needs to be cached in a database, or make it accessible via a web service. That would be ideal. You can write your own application on it, and have the data managed by your users.

But that means that you have to write your own, thread safe, caching algorithms.

No big deal if the data only changes once every 10 years, but refreshing it on a time or size basis, makes the whole thing a bit more complicated. And that’s where the great LoadingCache class from the Google Guava library comes in.

What are the Guave libraries ? Well, here’s how they describe it : ‘The Guava project contains several of Google’s core libraries that we rely on in our Java-based projects: collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, and so forth.’

Now for caching, the Guava LoadingCache class caches data in a key-object map, and lets you define a cache refreshing mechanism, all done in a thread safe manner.

So lets show a small  example and explain how it works. Suppose your cache contains a list of products that are on sale for 1 day. Depending on the no. of sold products, the price will increase during that day. This means that the cache should be updated every few seconds, to update the price, and after 1 day, the whole cache should be refreshed with new products. Suppose that price setting and product selections are in the database, updated by some back-end application, and we need the new data in our frontend application and we want to cache it.

All this can be done with this simple class :

import java.util.concurrent.TimeUnit;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import be.iadvise.dao.DatabaseDAO;
import be.iadvise.entities.Product;
import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.MoreExecutors;

@Singleton
public class ProductCache {

@EJB
 DatabaseDAO databaseDAO;
 private static final Integer REFRESH_PRODUCT_AFTER_5_SECONDS = 5;
 private static final Integer EXPIRE_PRODUCT_AFTER_1_DAY = 1;
 private final LoadingCache<String, Optional<Product>> cache;

 public ProductCache() {
      cache = CacheBuilder.newBuilder()
           .expireAfterWrite(EXPIRE_PRODUCT_AFTER_1_DAY, TimeUnit.DAYS)
           .refreshAfterWrite(REFRESH_PRODUCT_AFTER_5_SECONDS, TimeUnit.SECONDS)
           .build( new CacheLoader<String, Optional<Product>>() {
                 @Override
                 public Optional<Product> load( String productId ) throws Exception {
                     return loadCache(productId);
                 }
           }
     );
 }

 public Optional<Product> getEntry( String productId ) {
      return cache.getUnchecked( productId );
 }

 private Optional<Product> loadCache(String productId) {
      Product product = databaseDAO.getProduct(productId);
      return Optional.fromNullable(product);
 }
}

Explanation

  1. In the constructor, we build the cache using the CacheLoader, defining the refresh mechanism. In our example we define 2 rules :
    – expireAfterWrite : after this period, the object will be evicted from the cache, and replaced the next time it is requested.
    – refreshAfterWrite : after this period, the object will be refreshed using the loadCache method. (with our new price)
  2. getEntry(String productId) method : will return the object with given key. So in this example, the cache is not loaded all at once, but only when the object is needed.
  3. loadCache(String productId) : will load the product and add it to the cache, or replace it if it’s already there and needs to be refreshed.

That’s all there is to it !

A few other remarks on the code

  1. There are other mechanism like expire/refresh AfterRead, which will time only from the last read, or let the cache hold only a certain no. of objects,…
  2. This code is implemented as a session bean. To make a singleton, I’m using EJB 3 annotation @Singleton. Because I only want 1 cache in my application
  3. My DAO is also injected using the @EJB annotation
  4. The LoadingCache does not want any null objects in the map (returns an error), so I’m using the Guava ‘Optional’ class here. This is basically a wrapper for my object and used to check if there is a value for my product id or not. So if someone uses a wrong productId, my cache will indicate that there is no product for this id, and I don’t have to go to the database every time it is requested.

To conclude:

Programming a caching mechanism in a JEE environment is not as trivial as it may seem. Testing it in a multithreaded environment is even more difficult. The caching classes of Guave gives you ready-to-use solution. It’s programmed, tested and used by Google, so I think we can say in all honesty : this is proven technology.

A remark on deploying on Weblogic 12c:

Weblogic also uses the Guava libraries, but an older version. This causes following error on deployment :

java.lang.NoSuchMethodError: com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor()

Lcom/google/common/util/concurrent/ListeningExecutorService;

Adding the following to your weblogic-application.xml will solve the problem (force weblogic to use your deployed Guava libraries :

<wls:prefer-application-packages>
<wls:package-name>
com.google.common.*
</wls:package-name>
<wls:prefer-application-packages>

Guava libraries run under Apache license, more info/download can be found on :

https://code.google.com/p/guava-libraries/

Have fun !

Mobile Business Development seminar in Belgium and The Netherlands(4 and 6 June)

iAdvise organises a free seminar about Mobile Business Development on 4(Antwerp, Belgium) and 6(Breda, The Netherlands) June.

In this seminar we will discuss Mobile Development and Usability(UX – User Experience) in Mobile Development.
But we also compare different solutions, give pros and cons about: APEX Mobile, ADF Mobile and Tabris.

Of course we will also show you best practices, problems and solutions,…

More information(Dutch): http://www.iadvise.eu/seminars

Using ADF Logging in a non-ADF project

In a previous post (Starting with ADF 11G Logging), I explained how ADF logging is simple to set up, and how it will enable you to set the logging levels at runtime, without having to restart any server. When I showed this to a colleague of mine, he immedialtely popped the question : “Can’t we use this for all of our java applications, even the ones that don’t use ADF?”. Well, the answer is yes, and it turns out to be very easy. Just add the correct jar to your project and your done.

This blog will demonstrate how to get this working. I use Eclipse Juno to create a small webproject, only containing a servlet that does the logging. In fact I will use the same servlet I used in the previous post.

So I open my Eclipse , and started with a File -> New -> Dynamic Web project. Give it a name, set ‘Dynamic web module version’ to 2.5, click the  ‘Add project to an ear’ checkbox and click finish.

dyn_wb_prj

Now Eclipse has created a web and ear module for me.

Image

Now right click the web project (ADFLogging), and select New -> Servlet, give it a name, eg. TestServlet, and click finish.

Remove the generated code in the servlet, and copy the code from the servlet ‘ExecuteLogger’ from my previous post (here) and paste it in our new serlvet.

PS. : When you copied the code from my previous blog, don’t forget to set ADFLogger.createADFLogger to our current servlet class name : TestServlet.class.

We will get compile errors on HttpServletRequest,etc… and on the ADFLogger class because they are not defined in the classpath of the project. So we’ll add them in order to get our servlet compiled.  I get the 2 jar’s from a JDeveloper installation I did on my machine. We’ll only add these jar’s in order to get the servlet compiled in Eclipse. We will NOT deploy them, as they are already available on our Weblogic server.

To add the jar’s, right click on the web project, and go to Properties. In the Properties, click on ‘Java Build Path’.

buildpath

Click on ‘Add External JARs…’ , and go to the directory where you installed your JDeveloper, which in my case is : C:\Oracle\Middleware.

In that directory , get following jar’s from the sub-directory :

\oracle_common\modules\javax.servlet_1.0.0.0_2-5.jar : contains the servlet classes like HttpServletRequest/Response,etc…

\oracle_common\modules\oracle.adf.share.ca_11.1.1\adf-share-base.jar : contains the ADFLogger classes.

Now we see the the following jar’s added :

jars_added

Click OK and return to the servlet. In the servlet use CTRL-SHIFT-O to import the neccessary classes from the jar’s we just added.

Now all compile errors should be gone.

Generate the ear file as follow : File -> Export -> Ear file

Select the ear project and enter destination of the ear file

When you examine the ear, you will notice that the folder \WEB-INF\lib is empty.

As the servlet and ADFLogger jar is already available on Weblogic, there is no need to deploy it with our application.

Now deploy the ear to the Weblogic and test the servlet with following url. :

http://localhost:7101/ADFLogging/TestServlet

It will generate following output :

output

To check the logging done by this servlet :

As I used the integrated Weblogic of JDeveloper, I will look for my logs using JDeveloper, but in a production environment,

these logs can be viewed using the enterprise manager of Weblogic. For details, see my previous blog.

In the Oracle Diagnostics Logging configuration, I see my servlet after the deployment. No message level is defined, so it will take “Warning”, as this one is defined as default by the Root Logger

logger

After te execution, I see following log lines in the log analyzer.

result

So that’s it. So the bottom line is to add the ADFLogger jar to your non-ADF project, and you are ready to go !