StreamExplorer pushing findings as JSON messages to a WebSocket channel for live HTML Dashboard updates

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

A common desire when doing real time event processing with Stream Explorer and/or Oracle EVent Processor is the ability to present the findings from Stream Explorer in a live dashboard. This dashboard should hold a visualization of whatever information we have set up Stream Explorer to find for us – and it should always show the latest information.

User interfaces are commonly presented in web browsers and created using HTML(5) and JavaScript. As part of the HTML5 evolution that brought today’s browsers, we now have the ability to use Web Sockets through which we can push information from server to browser to have the user interface updated based on messages pushed from the server. This allows us to create a dashboard that listens from the browser to a Web Socket and use whatever messages appear on the web socket to actualize the user interface. Such a dashboard and its implementation using standard Java (EE) was discussed in a recent article: Java Web Application sending JSON messages through WebSocket to HTML5 browser application for real time push. The results from that article provide the foundation for this article you are reading right now.

We will create a Stream Explorer application that exposes a REST interface to which we will publish JSON messages (in this example using SoapUI as the client from which to generate the test events). These messages report on groups of people entering or leaving a specific room in a movie theater. The exploration we create will aggregate the information from the messages – providing us with a constant insight in the total number of people in each room. This information is subsequently pushed to the REST service exposed by a Java EE application that routes that information across the web socket to the HTML5 client. The next figure illustrates the application architecture:

image

In this article, we will assume that Java EE application including the dashboard are already available, as described in the referenced article. All we need to do is

  • Create a Stream exposed as (inbound) REST interface – discussed in this article.
  • Create an Exploration on top of this Stream – to aggregate the events from the Stream.
  • Configure a target for this Exploration using the outbound REST adapter (an example of which is discussed here) and publish the exploration.
  • Run the Java EE application, open the dashboard and publish messages to the Stream Explorer REST service; watch the dashboard as it constantly updates to reflect the actual status

 

After configuring the Stream (as discussed in this article), create an exploration, for example called CinemaExploration. Create a Summary of type SUM based on the property partySize and group by room. Edit the Properties and change the name of property SUM_of_partySize to occupation. The exploration will look like this:

 

image

We can start pushing some messages to it from SoapUI:

image

based in part on twice sending this SoapUI request:

image

 

Next, click on Configure a Target.

image

Select type REST and set the URL

image

Click on Finish.

Publish the Exploration.

image

 

The dashboard is opened:

image

Now we can run a test case in SoapUI to send test messages to the Stream Explorer application:

image

 

Here is what the live output stream in the Stream Explorer UI shows next to a screenshot taken of the Cinema Monitor dashboard:

image

The dashboard is constantly updated with the most recent finding published by Stream Explorer.Note: the notion of having a negative occupancy is one that will require some explaining! I(more careful test data management seems to be called for)

After running some more of the SoapUI Test Cases that publish cinema events to the REST ful entry point to the Stream Explorer application, the situation is as follows:

image

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Java Web Application sending JSON messages through WebSocket to HTML5 browser application for real time push

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

imageThis article describes a Java EE 7 web application that exposes a REST service that handles HTTP POST requests with JSON payload. Any message received is sent through a Web Socket to the web socket (server) endpoint that is published by a Java Class deployed as part of the web application. A static HTML page with two associated JavaScript libraries is opened in a web browser and has opened a web socket connection to this same end point. The message sent from the REST service endpoint to the web socket endpoint is pushed through the web socket to the browser and used to instantly update the web page.

The specific use case that is implemented is a simple web dashboard to monitor a movie theater: the current number of people in each of the four rooms of this movie theater is observed. The REST service receives the actual spectator count and through the two web socket interactions, this count ends up in the browser and in the visual presentation.

Below you will find a step by step instruction for implementing this use case including all required source code. The implementation uses only standard technologies: Java EE 7 (including JAX-RS and Web Socket ) and plain HTML5 and JavaScript – no special libraries are involved. The code is developed in Oracle JDeveloper (12c) and deployed to Oracle WebLogic  (12c). However, given that only standard components are used, the same code should work equally well on other containers and from other IDEs.

Note: for the use case presented in this article, a somewhat simpler solution would be possible using Server Sent Events (SSE) – a simpler and lighter weight approach than the use of web sockets. SSE is uni-directional (server to client push only) and that of course is exactly what I am doing in this particular example.

The steps will be:

  • Implement the REST service to handle json payloads in HTTP Post requests
  • Implement the WebSocket endpoint
  • Interact from REST service endpoint with WebSocket (endpoint)
  • Implement the HTML and JavaScript web application to present the live status for the movie theater based on the web socket messages

The starting point is a basic Java EE web application – with no special setup in web.xml or other files.

The final application looks like this:

image

For JDeveloper 12c users: the required libraries are JAX-RS Jersey Jettison (Bundled), JAX-RS Jersey 2.x, WebSocket, Servlet Runtime.

Implement the REST service to handle json payloads in HTTP Post requests

Publishing a REST service from Java (EE) is done using JAX-RS. In an earlier post, I described how to expose a REST service from Java SE (out of Java EE containers, leveraging the HTTP Server in the JVM). Publishing from within a Java EE container is very similar – and even easier. All we need is a single class with the right annotations.

image

The class is shown below. It is called MovieEvent (although the Class name does not matter at all). The class is annotated with the @Path annotation that is part of the JAX-RS specification. Because of this annotation, the class is published as a REST resource. The string parameter in this annotation (“cinemaevent”) defines the resource name as used in the URL for this REST service. The entire URL where this service can be invoked will be http://host:port/<web application root>/resources/cinemaevent. Note the segment resources that comes between the web application root and the name of the resource. That one had me confused for some time. The web application root for this application is set to CinemaMonitor by the way.

package nl.amis.cinema.view;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;

//invoke at : http://localhost:7101/CinemaMonitor/resources/cinemaevent
@Path("cinemaevent")
public class MovieEvent {

public MovieEvent() {
}

@POST
@Consumes("application/json")
@Produces("text/plain")
public String postMovieEvent(@Context Request request, String json) {
System.out.println("received event:" + json);
return "event received " + json;
}

@GET
@Produces("text/plain")
public String getMovieEvent(@Context Request request) {
return "nothing to report from getMovieEvent";
}
}

Method postMovieEvent is annotated with @POST – making it the handler of POST requests sent to the REST resource (at the URL discussed above). However, the annotation @Consumes(“application/json”) ensures that only HTTP requests with their content-type appropriately set to application/json will indeed be delivered to this method. The JSON payload of these requests is passed in the json input parameter. Note that not its name is relevant but the fact that it is the first String input parameter to this method without special annotations such as @Context.

At the present, the method does nothing useful – it write the JSON payload to the system output and returns a fairly bland confirmation message. Before too long, we will extend both this method and the entire class to interact with the web socket.

The REST service can be tested, for example from SoapUI or a Java client program by sending requests such as this one:

image

The corresponding output in the console (from the running Java EE container):

image

Implement the WebSocket endpoint

Implementing a WebSocket endpoint in Java EE is defined through the JSR-356 specification. A very good overview of how to use web sockets in Java applications is provided in this article.

Turning a Java Class into a WebSocket endpoint is actually quite simple. Use a few annotations, and we are in business. It is important to realize that even though the class that acts as the Web Socket server (endpoint) is deployed in this case as part of a Java EE web application, it stands quite apart from the rest of that application. The web socket endpoint can be accessed, not just from browsers but from Java clients as well. But there is no instance of the Class that is accessible for direct Java calls, nor does the WebSocket endpoint hook into EJBs, JMS destinations or JSF managed beans. It is a rather isolated component within the Java EE application. It does however have the potential to consume CDI Events (as described by Bruno Borges in this excellent article that inspired me to write this piece).

Create a Class called CinemaEventSocketMediator. Add the following annotation: @ServerEndpoint(“/cinemaSocket/{client-id}”). This turns the class into a web socket endpoint that exposes a Web Socket [channel]at ws://host:port/<web application context root>/cinemaSocket (in this case that will be ws://localhost:7101/CinemaMonitor/cinemaSocket). The final segment of the URL (/{client-id}) introduces a path parameter. The address of the Web Socket ends with the segment cinemaSocket. Anything that is added behind it is interpreted as an additional parameter that can be leveraged in the onOpen, onClose and onMessage methods through the @PathParam annotation – as we will see next.

A collection of peers is defined in which each client that starts a web socket connection will be retained.

The onOpen method – or rather the method annotated with @OnOpen – is invoked when a new client starts communications over the web socket channel. This method saves the session to the peers collection and returns a welcoming message to the new contact. Note how through the @PathParam annotated input parameter the method knows a little bit more about the client, provided the client did indeed add some content after the ‘regular’ Web Socket URL.

The method decorataed with @OnMessage is triggered when a message arrives on the Web Socket [channel]. In this case, the message is received and instantiated as a JSONObject. This would allow us to perform JSON style operations on the message (extract nested data elements, manipulate and add data). However, at the present, all we do is pass the message to each of the peers, regardless where the message came from (client-id) or what contents it contains. Note that the method would have to handle an exception if the message were not correct JSON data.

Finally the method with @OnClose handles clients closing their web socket channel connection. These clients are removed from the peers collection.

This particular WebSocket endpoint does not do anything that is special for the use case at hand. There are no references no movies, cinema events or whatever in this class. There could be logic that interprets messages, routes based on their content for example, but there does not need to be such business specific logic.

 

package nl.amis.cinema.view.push;

import java.io.IOException;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

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

import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;

@ServerEndpoint("/cinemaSocket/{client-id}")
public class CinemaEventSocketMediator {

private static Set peers = Collections.synchronizedSet(new HashSet());

@OnMessage
public String onMessage(String message, Session session, @PathParam("client-id") String clientId) {
try {
JSONObject jObj = new JSONObject(message);
System.out.println("received message from client " + clientId);
for (Session s : peers) {
try {
s.getBasicRemote().sendText(message);
System.out.println("send message to peer ");
} catch (IOException e) {
e.printStackTrace();
}

}
} catch (JSONException e) {
e.printStackTrace();
}
return "message was received by socket mediator and processed: " + message;
}

@OnOpen
public void onOpen(Session session, @PathParam("client-id") String clientId) {
System.out.println("mediator: opened websocket channel for client " + clientId);
peers.add(session);

try {
session.getBasicRemote().sendText("good to be in touch");
} catch (IOException e) {
}
}

@OnClose
public void onClose(Session session, @PathParam("client-id") String clientId) {
System.out.println("mediator: closed websocket channel for client " + clientId);
peers.remove(session);
}
}

Interact from REST service endpoint with WebSocket (endpoint)

The JSON messages received by the REST service exposed by class MovieEvent should be pushed through the Web Socket to the (external) clients, i.e. the web browser. There are two main options to hand these messages from class MovieEvent to the CinemaEventSocketMediator. One is through the use of CDI Events (as was mentioned above) and the other is by making class MovieEvent another client of the Web Socket [channel]exposed by CinemaEventSocketMediator. In this case, we opt for the latter strategy. Note that this means that there is no need for the MovieEvent class and the CinemaEventSocketMediator class to be in the same web application; their only interaction takes place across the web socket and they have no dependencies. I have them included in the same application for easy deployment. The same applies by the way to the client side of this article: the HTML and JavaScript that are loaded by the browser to present the dashboard to the end user. This too is currently included in the same web application and it too only has interaction over the web socket. There is no real reason for it to be part of the same application.

Using an excellent description on StackOverflow, I have created class MovieEventSocketClient with the @ClientEndpoint annotation. This class acts as a client to the Web Socket. It is the counterpart of the CinemaEventSocketMediator that is more or less the host or server for the web socket. The constructor for this class has two important steps: through the ContainerProvider (Provider class that allows the developer to get a reference to the implementation of the WebSocketContainer) a reference to the WebSocketContainer is retrieved (this is an implementation provided object that provides applications a view on the container running it. The WebSocketContainer container various configuration parameters that control default session and buffer properties of the endpoints it contains. It also allows the developer to deploy websocket client endpoints by initiating a web socket handshake from the provided endpoint to a supplied URI where the peer endpoint is presumed to reside. ) Using this container reference, through the connectToServer method, the client endpoint MovieEventSocketClient is connectedto its server. (This method blocks until the connection is established, or throws an error if either the connection could not be made or there was a problem with the supplied endpoint class.)

Class MovieEventSocketClient has methods annotated with @OnOpen, @OnClose and @OnMessage with more or less the same role as the counterparts in CinemaEventSocketMediator (and in the JavaScript client as we will see later). Note how in the @OnOpen annotated method the input parameter of type Session is retained and how in the method sendMessage this user session is used to send a message across the web socket.

package nl.amis.cinema.view.push;

import java.net.URI;

import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

// based on http://stackoverflow.com/questions/26452903/javax-websocket-client-simple-example

@ClientEndpoint
public class MovieEventSocketClient {
public MovieEventSocketClient(URI endpointURI) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, endpointURI);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

Session userSession = null;

@OnOpen
public void onOpen(Session userSession) {
System.out.println("client: opening websocket ");
this.userSession = userSession;
}

/**
* Callback hook for Connection close events.
*
* @param userSession the userSession which is getting closed.
* @param reason the reason for connection close
*/
@OnClose
public void onClose(Session userSession, CloseReason reason) {
System.out.println("client: closing websocket");
this.userSession = null;
}

/**
* Callback hook for Message Events. This method will be invoked when a client send a message.
*
* @param message The text message
*/
@OnMessage
public void onMessage(String message) {
System.out.println("client: received message "+message);
}

public void sendMessage(String message) {
this.userSession.getAsyncRemote().sendText(message);
}

}

Next we extend Class MovieEvent – the REST service that receives the JSON messages as HTTP POST requests – to interact with MovieEventSocketClient to pass the JSON messages to the Web Socket.

Method initializeWebSocket is added to instantiate MovieEventSocketClient with the address of the web socket. In postMovieEvent – the method annotated with @POST that handles the HTTP POST requests – a call is added to sendMessageOverSocket that hands the JSON message to MovieEventSocketClient  (after initializing it) to send it across the web socket (where it will be received in class CinemaEventSocketMediator).

 

package nl.amis.cinema.view;

import java.net.URI;
import java.net.URISyntaxException;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;

import nl.amis.cinema.view.push.MovieEventSocketClient;

//invoke at : http://localhost:7101/CinemaMonitor/resources/cinemaevent

@Path("cinemaevent")
public class MovieEvent {

private MovieEventSocketClient client;

private final String webSocketAddress = "ws://localhost:7101/CinemaMonitor/cinemaSocket";

public MovieEvent() {
}

private void initializeWebSocket() throws URISyntaxException {
//ws://localhost:7101/CinemaMonitor/cinemaSocket/
System.out.println("REST service: open websocket client at " + webSocketAddress);
client = new MovieEventSocketClient(new URI(webSocketAddress + "/0"));
}

private void sendMessageOverSocket(String message) {
if (client == null) {
try {
initializeWebSocket();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
client.sendMessage(message);

}

@POST
@Consumes("application/json")
@Produces("text/plain")
public String postMovieEvent(@Context Request request, String json) {
System.out.println("received event:" + json);
sendMessageOverSocket(json);
return "event received " + json;
}

@GET
@Produces("text/plain")
public String getMovieEvent(@Context Request request) {
return "nothing to report from getMovieEvent";
}
}

At this point, the application can be deployed. JSON messages sent to the REST service exposed by MovieEventSocketClient should be sent onward to the Web Socket (end point), leading to a message being written to the system output from onMessage in class CinemaEventSocketMediator.

Implement the HTML and JavaScript web application for the Cinema Monitor

The final piece in the puzzle discussed in this article is the client application to present the live status for the movie theater based on the web socket messages. It runs in a relatively modern browser – all standard browsers have HTML5 support which includes Web Socket interactions – and consists of an HTML page and two JavaScript libraries.

image

 

The HTML itself is relatively straightforward and boring.

image

Important are the <script> statements that import the JavaScript libraries that interact with the web socket and handle messages received over the web socket. The four rooms in the movie theater that are being monitored are represented by four TD elements with their id values set to room1..room4. These id values will be used in the JavaScript to locate the HTML element to update when a JSON message is received on the web socket for a particular room.

The imported JavaScript library websocket.js initializes the web socket connection to the end point ws://localhost:7101/CinemaMonitor/cinemaSocket. It configures JavaScript handlers for onOpen, onClose and onMessage. The latter is the most important one: any messages received on the web socket are checked for the string room. If the string is found, the message is handed off to the function updateRoomDetails(). This function is defined in the second JavaScript library moviemonitor.js.

var wsUri = "ws://" + document.location.host + "/CinemaMonitor/cinemaSocket/5";
var websocket = new WebSocket(wsUri);

websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
websocket.onopen = function(evt) { onOpen(evt) };

function onMessage(evt) {
console.log("received over websockets: " + evt.data);
console.log("looked for room index of: "+ evt.data.indexOf("room"));
var index = evt.data.indexOf("room");
writeToScreen(evt.data);
if (index&gt;1) {
console.log("found room index of: "+ evt.data.indexOf("room"));
updateRoomDetails( evt.data);
}
}

function onError(evt) {
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}

function onOpen() {
writeToScreen("Connected to " + wsUri);
}

// For testing purposes
var output = document.getElementById("output");

function writeToScreen(message) {
if (output==null)
{output = document.getElementById("output");}
//output.innerHTML += message + "
";
output.innerHTML = message + "
";
}

function sendText(json) {
console.log("sending text: " + json);
websocket.send(json);
}

The function updateRoomDetails in moviemonitor.js does not do a whole lot. It parses the input parameter from plain text with JSON format to a JavaScript memory structure. The function handleRoomUpdate is invoked with that JavaScript data structure – an object with properties room and occupation. The function handleRoomUpdate locates the TD element with its id set to room# where # corresponds wit the room property in the roomDetails input argument. It then sets the innerHTML of this element to the value of the occupation property. The result is an instant update of the room occupation value displayed in the user interface.

function updateRoomDetails( json) {
var roomDetails = JSON.parse(json);
handleRoomUpdate(roomDetails);
}

function handleRoomUpdate( roomDetails) {
var roomId = roomDetails.room;
var occupation = roomDetails.occupation;

var roomCell = document.getElementById("room"+roomId);
roomCell.innerHTML = occupation;

document.getElementById("message").innerHTML = roomDetails;
}

 

The screenshot shows the situation after a number of JSON messages have been received over the web sockets and the user interface has been updated accordingly.

 

image

 

Resources

Download the source code for the example discussed in this article: Zip File.

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

JavaOne 2014: Roadmaps for the near future of Java

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

JavaOne 2014 is first of all a community gathering – with over 500 sessions that discuss Java related topics in tantalizing details. A meeting of minds, of kindred spirits etc. It is also the place where the roadmap for the near future of the various constituents of the Java platform is unfolded. The slides in this article are taken from the Java Keynote on Sunday 28th September 2014.

Java SE:

image

Java SE Embedded/Java ME Embedded:

image

Java EE 8 :

image

image

image

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

What is REST?

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

REST (Representational State Transfer) is a term often used in software engineering when talking about services or other API’s. A lot of these so-called RESTful services or interfaces are not RESTful at all. Even the author of REST gets frustrated by the common misuse; http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven: “I am getting frustrated by the number of people calling any HTTP-based interface a REST API.”. REST is often confused with an application of REST to HTTP and URI (uniform resource identifier). An URI is a unique identifier to identify a web resource. HTTP is a transport protocol used to interact with the web resource. REST however is a bit more then just that. The division between resource identification and resource interaction is important. The application of REST uses the URI to identify a concept. In this blog post I will elaborate a bit more on REST while trying to avoid literally repeating other sources like Wikipedia. I will also provide some background and examples I found during my short study of the concept. In addition, this post will be introductory to other more (Oracle) implementation focused articles.

Continue reading

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Dear Java/JEE developer – why should you care about ADF?

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page

Whether you are deciding on the framework to use for your next Java web or mobile project or whether you are contemplating your next career step as a Java EE developer, ADF should be on your short list for consideration. With a new free version, deployment on many application servers including Tomcat, JBoss and GlassFish, extended support in both Eclipse and JDeveloper, and a Java based mobile solution for both iOS and Android – ADF has a lot to offer.

ADF is the Java EE Application Development Framework from Oracle. ADF allows developers to develop rich, enterprise grade Java web and mobile applications in a very productive manner. Many of the generic (plumbing) concerns that need to be addressed for most Java/JEE applications are taken care of by the framework – allowing developers to focus on the business specific functional areas. The initial 80% of the application is created through largely declarative development – 4GL style. The remaining 80% Winking smile is dealt with in a fairly ordinary coding style, similar to plain Java EE development or to development using other frameworks.

This article discusses the question why any Java/JEE developer should know and care about ADF. It will first explain in more detail what ADF is. Then it tries to state the reasons for Java developers to get interested and hopefully get involved with ADF and finally it provides pointers as to how Java/JEE developers can get started.

My motivation for writing this article is simple: I have worked with ADF since its initial incarnation back in 2004. I have developed Java Web applications with a range of other frameworks and libraries. I have been part of ferocious debates about which tools, IDEs, frameworks and technologies to use in projects. I have felt the disinterest and uninformed dislike of self proclaimed J2EE specialists in solutions like ADF because they were from Oracle. I have seen the struggle of many Java developers to find their way in rapidly evolving Java frameworks and to become productive. I have witnessed the rise of the Spring Framework and the drastic turnaround in Java EE 5. Though arguably less than it was 10 years ago, today’s Java Web development arena is still a colorful collection of libraries, frameworks, specifications, tools, personal preference and some emotional attachments (some would say engrained biases).

I strongly believe that organizations, projects and developers who make choices about the buildings blocks, frameworks and architecture of their Java EE web applications should be aware of ADF and what it can mean to them. Just like they should know about other solutions in the Java EE space to have a holistic understanding of the Java EE market including JBoss Hibernate, Seam and Rich Faces. And about the Spring Framework and Spring MVC. And PrimeFaces. And many other solutions. Of course I believe that in many situations ADF will be a valuable technology to apply. But more importantly I believe that a rational decision making process should include the strategic and proven development framework from one of the largest players in the JEE arena, if only to decide in an informed way to not use [any part of]it. Especially now ADF is free and can be deployed on GlassFish, Tomcat and JBoss application servers.

Continue reading

Share this on .. Tweet about this on TwitterShare on LinkedInShare on FacebookShare on Google+Email this to someoneShare on TumblrBuffer this page