Mittwoch, 9. Oktober 2013

Carte and Crosssite-Scripting? Come on!

Welcome to my very first blog-post!

I would like to start off with a short "Thank you" to all of the Pentaho OpenSource-community
out there. Being a very responsive and supportive community I experienced a warm and healthy
environment ever since starting using the Pentaho BI-Suite (PDI, BI-Server and many of the infamous plugins) about 6 years ago.

This being said I would like to dive straight ahead into the post...


Lately I came across an intersting situation which this blogpost will talk about:
Making PDI/Carte-"services" available for other websites.

In short, this post will assume you to have some basic knowledge about:
  • jQuery/AJAX requests/responses
  • PDI and the Carte-slaveserver

To start, I would like to describe my intial problem, then display the solution I came up with while
being on-site at the customer.
As almost always, there´s many ways to solve a given problem, so please don't be picky about me if you and your solution are fit better for your needs.

Ok, now let´s go!

Problem
My situation was, to have the Carte-Service running on a given computer with also having a JBoss AS server deployed on another (or maybe the same) machine.
So we end up with Carte listening on http://localhost:8888 (Basic-Authentication active) and the
JBoss listening on http://localhost:8080 and having a bunch of apps deployed on the JBoss.

From inside one of these apps my goal was to access some KTRs on Carte via Web (e.g.: http://localhost:8888/kettle/executeTrans/?trans=getMeSomeData.ktr)
and return the data from the KTR to the requesting jQuery/AJAX call, which basically is something like this:

  $.ajax({
    url : 'http://localhost:8888/kettle/executeTrans/?trans=getMeSomeData.ktr',
    method : 'GET',
    success: function(data) {
      doSomethingWithTheData(data)
    }
  })

 
However, having the originating request coming from localhost:8080 and requesting data from localhost:8888 causes an obvious problem:
"Crosssite scripting"!
Thus, jQuery requires you to use "CORS/JSONP" which just did not work for me.


Solution

So I ended up thinking about another approachand "proxy'ing" came to my mind.
In the end, I did some research and since I did not want to setup proxy-rules inside my JBoss I was looking for a more "light-weight" proxy.

Now this is my result:

proxy.jsp:
<%@ page
    import="java.io.*,
            java.util.*,
            java.net.*"
    contentType="text/plain; charset=UTF-8"
%><%
StringBuffer sbf = new StringBuffer();
//Access the page
try {
  // Add additional parameters to the requested url
  String requestedUrl = request.getParameter("url");
  Map<String,String> additionalParameters = request.getParameterMap();
  Iterator parameterIterator = additionalParameters.keySet().iterator();
  while( parameterIterator.hasNext() ) {
    String nextParamName = (String) parameterIterator.next();
    // no need to repeat "url" as this is our first and really mandatory request parameter
    if ( !"url".equals(nextParamName) ) {
      requestedUrl += "&"+nextParamName+"="+request.getParameter(nextParamName);
    }
  }
  requestedUrl = requestedUrl.replace(" ","+");
  URL url = new URL(requestedUrl);
  HttpURLConnection conn=(HttpURLConnection)url.openConnection();
  conn.setRequestMethod("GET");
  String authHeaderValue = "Basic Y2x1c3RlcjpjbHVzdGVy";
  conn.setRequestProperty("Authorization",authHeaderValue);
  // Copy the proxied content-type to own response
  response.setContentType(conn.getContentType());
  BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
  String inputLine;
  while ( (inputLine = in.readLine()) != null) {
    sbf.append(inputLine);
  }
  in.close();
} catch (MalformedURLException e) {
} catch (IOException e) {
}
%><%= sbf.toString()%>


To get the file directly, you can download it at github: proxy.jsp

After creating this JSP and putting it into the folder of one of the JBoss apps I registered this proxy-servlet inside the
web.xml of an existing app (in the example inside the "myapp" as to be seen in the jQuery-call below)

    <servlet>
      <servlet-name>proxy</servlet-name>
      <jsp-file>/proxy.jsp</jsp-file>
   </servlet>
   <servlet-mapping>
      <servlet-name>proxy</servlet-name>
      <url-pattern>/proxy</url-pattern>
   </servlet-mapping>

  
After re-deploying the JBoss app I was then able to use the Carte-service by using this kind of AJAX-Call:

  $.ajax({
    url : '/myapp/proxy?url=http://localhost:8888/kettle/executeTrans/?trans=getMeSomeData.ktr',
    method : 'GET',
    success: function(data) {
      doSomethingWithTheData(data)
    }
  })


...which seems to be a more flexible and reuseable way to me of creating such proxy-requests without having to change the server-setup extensively.

Notes:
  • Please note that the default "cluster//cluster" credentials for the Carte-Service are "hard-coded" inside the proxy.jsp on line String authHeaderValue = "Basic Y2x1c3RlcjpjbHVzdGVy"; So if you need to/had changed that, check out http://decodebase64.com/ for a convenient site to create the new hash (enter them in the following format: <username>:<password>, e.g: cluster:cluster)

Upcoming in the next post I'd like to recreate the same approach for the PHP-Guys out there who would like to use Carte and PDI as JSON-Datasource in their PHP-applications.
Thanks for reading and best regards,

Tom

[Update]
The internet is great! Just some 16 hours after posting this blog I received feedback from another Pentaho-integrator (yes, that´s you Harris! ;-) ) claiming that the proxy did obviously default to "text/plain" as Content-Type, removing the possibility to properly deal with Carte's own "Status-Interfaces" (which are XML-Responses)
So now I am happy to present you version2 of my proxy.jsp, now with parameter and Content-Type support.

Keine Kommentare:

Kommentar veröffentlichen