29

Java and JavaScript Integration in OSGI

 5 years ago
source link: https://www.tuicool.com/articles/hit/6bYBR3R
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

If you, like myself, are mainly developing OSGI-based applications, then you will have probably felt the same feeling I have, which is that the progress made elsewhere in user interfaces is lagging behind in Eclipse. Sure, the E4 platform is well underway, and the team from RAP has done a magnificent job in exposing the traditional Eclipse Rich Client to the server side, but even then the developments in especially JavaScript-based libraries such as Bootstrap or Vue.js are currently developing so fast, that it is almost impossible to keep track.

After reviewing a number of my own projects, I came to the conclusion that my approach to front-end development in Eclipse OSGI had to change. Up until recently, I would create a RAP RCP 3.x application , which would provide the main User Interface to my application. This would give me access to the various SWT widgets that I am accustomed to working with, and I could add JavaScript-based libraries, such as OpenLayers or Google Maps in a browser object. This approach works fine, but it does come with the price of limitations in styling and sizing for various platforms. Of course, Eclipse RAP provides all the necessary code to implement this functionality, but at the current state of technology, I would just want a bootstrap-ish library to take care of the boilerplate code, instead of me having to code this myself.

Then I realized that I had to make a mind-shift. Instead of trying to push JavaScript/HTML in the straight jacket of the current possibilities of Java-based applications, it would be better to start with HTML as soon as possible and integrate the SWT widgets into the front-end if this is required. Eclipse RAP is quite agnostic to this approach, so currently we at Condast are running our first projects to test this out. As these experiments force us to build the UI bundles from the ground up, I thought that our efforts may be of help for other programmers, so this series of tutorials is aimed at helping OSGI programmers who, like us, want to put the power of JavaScript libraries in our server-based Rich Clients. Maybe your feedback can provide us with new insights!

In this tutorial, I will develop an (Equinox) OSGI Bundle that can control an OpenLayers map, which provides Google Maps-like functionality. I will be describing various options you may have in order to achieve this, and try to spell out the pros and cons of these approaches. This tutorial assumes that you are well-versed in OSGI and Java programming. As we will be programming a Rich Client, the Eclipse IDE for RCP and RAP Developers provides all the necessary bundles to get you started.

Getting Started

After installing and opening the Eclipse IDE, you can create a new Plug-In Project. We will call our bundle test.openlayers.map . Choose the intended target platform to be Eclipse (the most recent version), and select the radio button that tells the system that this bundle will "make contributions to the UI." This will allow you to select one of three standard RAP RCP templates. We will choose the "RAP Hello World" template, which is the simplest of the three.

NOTE: We normally run our applications on a Virgo JAVA Application Server, version 3.7.2. As this version does not support Java 8, the bundles must be configured to minimally work with Java 7. In this tutorial the Manifest.MF is configured as such for this reason.

After the bundle is created, Eclipse will also ask if the target should be configured to run RAP. If you accept this, then you should be able to launch the Hello World application without too much hassle. You should get a button and a text box in your browser (inline or external, depending on your settings) with "Hello World." So fast, everything works in a standard way!

The first thing we need to change if we want to support a JavaScript library such as OpenLayer is to:

  1. Make the bundle a Servlet Container.
  2. Create a folder for servlet resources.

In Equinox OSGI, we can do this through a plugin extension. First, add a folder called 'web' to your bundle, then create a plugin.xml file with the following contents:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.equinox.http.registry.resources">
      <resource
            alias="/openlayer"
            base-name="/web">
      </resource>
   </extension>
</plugin>

Note:Don't forget to include and activate org.eclipse.equinox.http.registry in your target environment!

If you activate org.eclipse.equinox.http.registry.resources in the launch configuration, then the bundle will serve all the resources in the 'web' folder and display them at the designated alias. For instance, if your launch configuration opens at,  http://localhost:10080 , then the web resources will be found at http://localhost:10080/openlayer/ .

You can now add a file, for instance, index.html, with the following content:

<!doctype html>
<html lang="en">
  <body>
    <p>Hello World</p>
  </body>
</html>

We've created a "Hello World" HTML page, which serves at http://localhost:10080/openlayer/index.html !

But there is also another way to access this file. Change the BasicEntryPoint to:

package test.openlayers.map;

import java.io.InputStream;
import java.util.Scanner;

import org.eclipse.rap.rwt.application.AbstractEntryPoint;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;

public class BasicEntryPoint extends AbstractEntryPoint {
private static final long serialVersionUID = 1L;

private static final String S_INDEX_LOCATION = "/openlayer/index.html";

@Override
    protected void createContents(Composite parent) {
        parent.setLayout(new GridLayout(2, false));
        Browser browser = new Browser(parent, SWT.NONE);
        browser.setUrl( S_INDEX_LOCATION );
        browser.setLayoutData(new GridData( SWT.FILL, SWT.FILL, true, true ));
    }
}

And start the application. If all went well, you will also see the Hello World message at:

http://localhost:10080/openlayers/home  

Improving the Bundle

The solution depicted above works fine but also has a few drawbacks. If this bundle is deployed on a server, then anyone can access the resources. The index.html can be opened by anyone who types in the correct URL. Therefore it is wise to add a filter to your servlet container, which only allows access from local URLs. For this we have to create the filter:

package test.openlayers.map.servlet;

import java.io.IOException;
import java.util.logging.Logger;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MapFilter implements Filter {

private Logger logger = Logger.getLogger( this.getClass().getName() );

@Override
public void init(FilterConfig arg0) throws ServletException {
/* NOTHING */
}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        if (isForbidden(request)){
logger.warning("Attempting to access openlayer from: " + request.getRemoteAddr() + ": ");
logger.warning("Local address: " + request.getLocalAddr());
                return;
        }
        else
            chain.doFilter(request, response);
    }

/**
 * If the first three numbers of the ip address match, then 
 * the call is considered local
 * @param req
 * @return
 */
private boolean isForbidden( ServletRequest req ){
String local = req.getLocalAddr();
if( local.lastIndexOf(".") > 0){
local = local.substring(0, local.lastIndexOf("."));
return !req.getRemoteAddr().startsWith(local);
}
return !req.getRemoteAddr().equals(local);
}

@Override
public void destroy() {
  /* NOTHING */
}
}

and register it in the plugin.xml file, which becomes as follows:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.equinox.http.registry.resources">
      <resource
            alias="/openlayer"
            base-name="/web">
      </resource>
   </extension>
   <extension
         id="mapFilter"
         point="org.eclipse.equinox.http.registry.filters">
      <filter
            alias="/openlayer"
            class="test.openlayer.map.servlet.MapFilter">
      </filter>
    </extension> 
</plugin>

The filter blocks all access from outside the server, so only local resources can access it.

Another precautionary measure to improve the security is to move the index.html file to a different folder, say 'resources' and modify the BasicEntryPoint.java file slightly:

package test.openlayers.map;

import java.io.InputStream;
import java.util.Scanner;

import org.eclipse.rap.rwt.application.AbstractEntryPoint;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;

public class BasicEntryPoint extends AbstractEntryPoint {
private static final long serialVersionUID = 1L;

private static final String S_INDEX_LOCATION = "/resources/index.html";

@Override
    protected void createContents(Composite parent) {
        parent.setLayout(new GridLayout(2, false));
        Browser browser = new Browser(parent, SWT.NONE);
        browser.setText( readInput(getClass().getResourceAsStream(S_INDEX_LOCATION)));
        browser.setLayoutData(new GridData( SWT.FILL, SWT.FILL, true, true ));
    }

protected String readInput( InputStream in ){
StringBuffer buffer = new StringBuffer();
Scanner scanner = new Scanner( in );
try{
while( scanner.hasNextLine() )
buffer.append( scanner.nextLine() );
}
finally{
scanner.close();
}
return buffer.toString();
}
}

Now the index.html file is parsed and no longer accessible from anywhere except the browser widget.

Activating JavaScript

Next change the index.html as depicted in the OpenLayer tutorial at: https://openlayers.org/en/latest/doc/quickstart.html

<!doctype html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.2.0/css/ol.css" type="text/css">
    <style>
      .map {
        height: 400px;
        width: 100%;
      }
    </style>
    <script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.2.0/build/ol.js"></script>
    <title>OpenLayers example</title>
  </head>
  <body>
    <h2>My Map</h2>
    <div id="map" class="map"></div>
    <script type="text/javascript">
      var map = new ol.Map({
        target: 'map',
        layers: [
          new ol.layer.Tile({
            source: new ol.source.OSM()
          })
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([37.41, 8.82]),
          zoom: 4
        })
      });
    </script>
  </body>
</html>

With this, we've got a basic functional open layer map at our disposal!

Wrapping Up

In this first session, we've made an Eclipse OSGI bundle that can work with HTML and JavaScript files. We've also paid some attention to securing the resources. In the next tutorial, we will add some functionality to the map and access it from Java.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK