Wednesday Jun 29, 2016

Integrating Brazil into Oracle JET

Using the info from yesterday, it's easy to integrate Brazil into Oracle JET:

To get started, get the brazil-states.geojson file and put it in your project as described yesterday. Below, the file has been renamed to "brazil.json".

Then here's your viewModel:

define(['text!../brazil.json',
        'ojs/ojcore',
        'knockout',
        'ojs/ojthematicmap'],
        function (geo, oj, ko) {
            function mainContentViewModel() {
                var self = this;
                self.mapProvider = {
                    geo: JSON.parse(geo),
                    propertiesKeys: {
                        id: 'name',
                        shortLabel: 'name',
                        longLabel: 'sigla'
                    }
                };
            }
            return new mainContentViewModel();
        });

And the view: 

<h1>Brazil Map</h1>
<div id='thematicmap1'
     data-bind="ojComponent:{
    component: 'ojThematicMap',
        basemap: 'Foo',
        mapProvider: mapProvider,
         areaLayers:
          [{
            layer: 'Bar',
            areaDataLayer: {
                id: 'adl1',
                areas: [
                    {id: 'a1', 'location': 'Tocantins', color: 'rgb(255, 181, 77)'},
                    {id: 'a2', 'location': 'Amazonas', color: 'rgb(255, 120, 90)'},
                    {id: 'a3', 'location': 'Mato Grosso', color: 'rgb(120, 30, 87)'},
                    {id: 'a4', 'location': 'Bahia', color: 'rgb(20, 80, 120)'}
                ]}
          }]
        }"
     style="width:100%; height:600px;"/>

Take note of "Foo" and "Bar" above, i.e., these labels can be anything you like, they're just there as identifiers. The same is true for the "id" in the "areaDataLayer". The colors above are just an example of what you can do, i.e., they don't signify anything in this example. 

All this is pretty cool, isn't it?

Tuesday Jun 28, 2016

GeoJSON and Oracle JET Maps

In the latest Oracle JET release notes, you'll find the Oracle JET maps now support GeoJSON. There's even a complete sample in the Oracle JET Cookbook

Let's see how to set that up within an Oracle JET module, rather than via the require block, which is how it is defined in the Oracle JET Cookbook. 

  1. Get the JSON file from the sample above here. Put it into your project structure, somewhere, e.g., here in the QuickStart Basic:



  2. In 'requirejs.config', in "main.js", include "proj4js", which is part of Oracle JET 2.0.2, read about it here:
    'proj4': 'libs/proj4js/dist/proj4'
  3. Define "home.js", or one of your other JET module's viewModels, as follows, slightly restructured from the Oracle JET Cookbook:
    define(['text!../uk_epsg27700.json',
            'proj4',
            'ojs/ojcore',
            'knockout',
            'ojs/ojthematicmap'],
            function (geo, proj4, oj, ko) {
                function mainContentViewModel() {
                    var self = this;
                    self.mapProvider = {
                        geo: JSON.parse(geo),
                        propertiesKeys: {
                            id: 'id',
                            shortLabel: 'sLabel',
                            longLabel: 'lLabel'
                        }
                    };
                    var storeLocations = [
                        {'long': 0.1278, 'lat': 51.5074, 'city': 'London'},
                        {'long': -1.257677, 'lat': 51.752022, 'city': 'Oxford'},
                        {'long': -0.460739, 'lat': 52.136436, 'city': 'Bedford'},
                        {'long': -7.318268, 'lat': 55.006763, 'city': 'Londonderry'},
                        {'long': -8.630498, 'lat': 52.668018, 'city': 'Limerick'},
                        {'long': -6.251495, 'lat': 53.353584, 'city': 'Dublin'}
                    ];
                    this.cities = [];
                    for (var i = 0; i < storeLocations.length; i++) {
                        var store = storeLocations[i];
                        // Call proj4js API with the proj4 projection mapping for EPSG:2770 and the long/lat coordinates.
                        var coords = proj4("+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs", [store.long, store.lat]);
                        this.cities.push({id: store.city, x: coords[0], y: coords[1], shortDesc: store.city});
                    }
    
                }
                return new mainContentViewModel();
            });
    
  4. Define "home.html", or one of your other JET module's view, as follows, all from the Oracle JET Cookbook:
    <h1>Home Content Area</h1>
    <div id='thematicmap1'
    data-bind="ojComponent:{
        component: 'ojThematicMap',
            basemap: 'unitedKingdom',
            mapProvider: mapProvider,
            areaLayers:
              [{
                layer: 'countries',
                areaDataLayer: {
                    id: 'adl1', 
                    areas: [{id: 'a1', 'location': 'GBR', color: 'rgb(255, 181, 77)'}]}
              }],
            pointDataLayers: [{id: 'pdl1', markers: cities}]
            }"
        style="width:100%; height:600px;"/>

That's it, you're done. Will be exploring this functionality more in upcoming blog entries.

Saturday Jun 18, 2016

Structuring ECMAScript 6 Modules via Babel and Webpack

Continuing from yesterday, since transpilation of ECMAScript 6 to ECMAScript 5 is needed because of the current state of browsers, and we've chosen Babel for that, and since we're using Webpack to bundle our generated ECMAScript 5 files... what would be a logical way to structure our source code so that you can easily find the code with which you need to work?

Let me suggest the following entries in package.json:

"scripts": {
    "babel": "babel public_html/js/src --presets babel-preset-es2015 --out-dir public_html/js/gen",
    "webpack": "webpack public_html/js/gen/main.js public_html/js/bundle.js"
},

What the above gives you is this:

In the "gen" folder is the output of Babel. And 'bundle.js', in the root of the "js" folder, is the result of Webpack's bundling process. They're green above because I haven't (and won't) commit them into my repo, they're simply generated whenever I need them to be. And that's a clear structure and let's me easily see what's what.

Does anyone have other/different/complementary/conflicting ideas about this structure? 

Friday Jun 17, 2016

ECMAScript 6, Modules, Babel, Webpack, and NetBeans IDE

I've been talking about modularity via Require.js in enterprise JavaScript applications for some time (because Oracle JET bundles Require.js as its modularity solution). ECMAScript 6 provides its own built-in module system and, since NetBeans IDE now provides ECMAScript 6 support (in development builds), let's rewrite my Require.js application to use ECMAScript 6 modularity.

The complete sample discussed below is here:

https://github.com/GeertjanWielenga/RequireJSSamples/tree/master/ECMAScript6Sales

Handily, you can compare the ECMAScript 6 sample above to the Require.js implementation:

https://github.com/GeertjanWielenga/RequireJSSamples/tree/master/ModularSales

...as well as the non-modular Sales application:

https://github.com/GeertjanWielenga/RequireJSSamples/tree/master/SimpleSales

Here's 'main.js':

import { buyThing } from './buyer';
name = 'John Smith';
console.log(name + " enters the system");
var result = buyThing(name);
console.log("sale success " + result);

In NetBeans, the above looks as follows:

Take note that there's the "import" keyword above, enabling me to import "buyThing" from 'buyer.js', since here there's an "export" keyword:

import { verifyName } from './authenticator';
import { sendThing } from './thing';
import { sendApology } from './thing';
export function buyThing(name) {
console.log(name + " is trying to buy"); var verified = verifyName(name); if(verified){ sendThing(name); } else { sendApology(name); } return verified; }

In NetBeans, the above looks as follows:

Notice that there are three imports above, the first of which is for 'authenticator.js', which is below...

export function verifyName(name) {
    var requiredNameLength = 1;
    console.log("authenticating " + name);
    return name.length > requiredNameLength;
}

...and here's the above in NetBeans...

...and the other two are for for 'thing.js':

export function sendThing(name){
    console.log("send thing to " + name);
}
export function sendApology(name){
    console.log("say sorry to " + name);
}

Here's the above in NetBeans:

In my 'package.json', I'm using Babel and Webpack: 

"scripts": {
    "babel": "babel public_html/js/src --presets babel-preset-es2015 --out-dir public_html/js",
    "webpack": "webpack public_html/js/main.js public_html/js/bundle.js"
},
"devDependencies": {
  "babel-cli": "^6.10.1",
  "babel-preset-es2015": "^6.9.0",
  "webpack": "^1.8.5"
}

You'll now have this:

You can run "npm install" directly in NetBeans:

You can run Babel and Webpack directly in NetBeans:

When I run Babel, the JavaScript files are transpiled to ECMAScript 5, since that's what browsers support right now. When Webpack is used, the entire application is bundled into "bundle.js", which avoids problems with the "require" keyword that you get from Babel transpilation.

Now, I refer to that one single file, i.e., "bundle.js", that you see above, in my "index.html" file.

Related reading:

Wednesday Jun 15, 2016

Track the Closest Beacon on Android via Cordova

I've blogged a series of articles on getting started with beacons on Android via Cordova, starting here.

The tricky bit comes in when you want to keep track of the closest beacon. Here is an approach to doing so, based on a variety of resources on-line.

References:

The code below is a subset of this example:

https://github.com/mikaelkindborg/evo-demos/tree/master/Demos2015/cordova-ibeacon

Code in the JavaScript (i.e., "viewModel") side of a JET module:

define(['ojs/ojcore', 'knockout', 'jquery'],
    function (oj, ko, $) {
        function IncidentsViewModel() {
            var self = this;
            var mNearestBeacon = null;
            var mNearestBeaconDisplayTimer = null;
            self.beaconName = ko.observable();
            self.beaconDescription = ko.observable();
            self.proximity = ko.observable();
            self.accuracy = ko.observable();
            self.rssi = ko.observable();
            var uuid1 = '0ea75ed4-ce75-4a09-bca8-f0565bae82bb';
            var major1 = 5;
            var minor1 = 2;
            var uuid2 = '0ea75ed4-ce75-4a09-bca8-f0565bae82bb';
            var major2 = 5;
            var minor2 = 3;
            var beacon1 = uuid1 + ":" + major1 + ":" + minor1;
            var beacon2 = uuid2 + ":" + major2 + ":" + minor2;
            var mRegions =
                    [
                        {
                            id: 'region1',
                            uuid: uuid1,
                            major: major1,
                            minor: minor1
                        },
                        {
                            id: 'region2',
                            uuid: uuid2,
                            major: major2,
                            minor: minor2
                        }
                    ];
            self.handleAttached = function () {
                document.addEventListener('deviceready', onDeviceReady);
            };
            self.handleDetached = function () {
                document.removeEventListener('deviceready', onDeviceReady);
                clearInterval(mNearestBeaconDisplayTimer);
                mNearestBeaconDisplayTimer = null;
            };
            function onDeviceReady()
            {
                startRanging();
                mNearestBeaconDisplayTimer = setInterval(displayNearestBeacon, 1000);
            }
            function startRanging()
            {
                function onDidRangeBeaconsInRegion(result)
                {
                    updateNearestBeacon(result.beacons);
                }
                var delegate = new cordova.plugins.locationManager.Delegate();
                cordova.plugins.locationManager.setDelegate(delegate);
                delegate.didRangeBeaconsInRegion = onDidRangeBeaconsInRegion;
                startRangingRegions(mRegions);
            }
            function startRangingRegions(regions)
            {
                for (var i in regions)
                {
                    startRangingRegion(regions[i]);
                }
            }
            function startRangingRegion(region)
            {
                var beaconRegion = new cordova.plugins.locationManager.BeaconRegion(
                        region.id,
                        region.uuid,
                        region.major,
                        region.minor);
                cordova.plugins.locationManager.startRangingBeaconsInRegion(beaconRegion)
                        .fail()
                        .done();
            }
            function getBeaconId(beacon)
            {
                return beacon.uuid + ':' + beacon.major + ':' + beacon.minor;
            }
            function isSameBeacon(beacon1, beacon2)
            {
                return getBeaconId(beacon1) === getBeaconId(beacon2);
            }
            function isNearerThan(beacon1, beacon2)
            {
                return beacon1.accuracy > 0
                        && beacon2.accuracy > 0
                        && beacon1.accuracy < beacon2.accuracy;
            }
            function updateNearestBeacon(beacons)
            {
                for (var i = 0; i < beacons.length; ++i)
                {
                    var beacon = beacons[i];
                    if (!mNearestBeacon)
                    {
                        mNearestBeacon = beacon;
                    } else
                    {
                        if (isSameBeacon(beacon, mNearestBeacon) ||
                                isNearerThan(beacon, mNearestBeacon))
                        {
                            mNearestBeacon = beacon;
                        }
                    }
                }
            }
            function displayNearestBeacon()
            {
                if (!mNearestBeacon) {
                    return;
                }
                var beaconId = getBeaconId(mNearestBeacon);
                if (beaconId === beacon1) {
                    self.beaconName('Rembrandt van Rijn');
                    self.beaconDescription('Nachtwacht');
                } else if (beaconId === beacon2) {
                    self.beaconName('Vincent van Gogh');
                    self.beaconDescription('Sunflowers');
                }
                self.proximity(mNearestBeacon.proximity);
                self.accuracy(mNearestBeacon.accuracy);
                self.rssi(mNearestBeacon.rssi);
            }
        }
        return new IncidentsViewModel;
    }
);

The above enables you to create a simple view, like this:

<h2><span data-bind="text: beaconName"></span></h2>
<h3><span data-bind="text: beaconDescription"></span></h3>
<hr>
<p><b><span data-bind="text: proximity"></span></b></p>
<p>Distance: <b><span data-bind="text: accuracy"></span></b></p>
<p>RSSI: <b><span data-bind="text: rssi"></span></b></p>

The next step is to use Oracle Mobile Cloud Service 2.0 as a beacon registry so that the UUID information does not need to be hardcoded, i.e., so that it can be managed in the Cloud.

The big problem with the code above is that it takes about 5 seconds for my app to detect the closest beacon. Maybe that means I need to finetune the beacons or maybe there's something sub-optimal about the code or maybe the beacons themselves. 

Tuesday Jun 14, 2016

Integrating Mapbox with Oracle JET

Mapbox is a developer platform for creating and using maps. It is based on Leaflet, which I discussed recently here.

In this article I'll show how to get started integrating Mapbox into an Oracle JET application. The benefit of Mapbox is that it gives you maps, and a lot of related concepts, such as markers, while the benefit of Oracle JET is that it gives your application a well structured architecture based on open source libraries. By following the steps below, you'll end up with an application that looks like this:

Take the following steps:

  1. Use the "Oracle JET QuickStart Basic" template to set up your Oracle JET application. For example, in NetBeans IDE 8.1, with the Oracle JET Support plugin installed, you'll find it in the Samples category in the New Project window.

  2. Use Bower to install "mapbox.js", e.g., from the tools provided in NetBeans IDE 8.1, e.g., shown below:



  3. Add to the "requirejs.config" section in "main.js":
    'mapbox': 'libs/mapbox.js/mapbox'
  4. In the "index.html" file, refer to the "mapbox.css" file, as follows:
    <link href="js/libs/mapbox.js/mapbox.css" rel="stylesheet" type="text/css"/>
  5. Next, create a new CSS file, in the "css" folder, with any name, e.g., "styles.css", with this content:
    #map { width:100%; height:100%; }
  6. Rewrite "home.html" to the following:
    <STYLE TYPE="text/css">
    <!--
      @import url(css/styles.css);
    -->
    </STYLE>
    
    <div id='map'></div>
  7. Rewrite "home.js" to the following:
    define(['ojs/ojcore', 'knockout', 'mapbox'],
        function (oj, ko) {
            function mainContentViewModel() {
                var self = this;
                self.handleAttached = function () {
                    L.mapbox.accessToken = your-access-token-goes-here';
                    var map = L.mapbox.map('map', 'mapbox.dc-markers');
                    map.featureLayer.on('click', function (e) {
                        map.panTo(e.layer.getLatLng());
                    });
                };
            }
            return new mainContentViewModel();
        });

That's it, you've taken your first steps in bringing Mapbox into your Oracle JET application.

Next steps might include communicating with other modules the geolocation of the selected marker, so that the related address and other information can be displayed in some way, using knockout-postbox (as explained here):

Here's the publishing side, i.e., here we are communicating the currently selected marker:

L.mapbox.accessToken = 'pk.eyJ1IjoiZ2VlcnRqYW53aWVsZW5nYSIsImEiOiJjaXBkcWQxYTcwMDA3dXFuZmVlZTA2aXQyIn0.h7YKD28ii2TbOEzfvns5JQ';
var map = L.mapbox.map('map', 'mapbox.dc-markers');
map.featureLayer.on('click', function (e) {
    var latLng = e.layer.getLatLng();
    map.panTo(latLng);
    ko.postbox.publish("publishLatLng",
        {
            'lat': latLng.lat,
            'lon': latLng.lng
        });
});

And here we are using Knockout Postbox to pick up and process the geolocations:

define(['ojs/ojcore', 'knockout', 'knockout-postbox'],
    function (oj, ko) {
        function navContentViewModel() {
            var self = this;
            self.street = ko.observable();
            self.streetNumber = ko.observable();
            self.placename = ko.observable();
            self.postalcode = ko.observable();
            self.adminName1 = ko.observable();
            self.handleAttached = function () {
                ko.postbox.subscribe("publishLatLng", function (newValue) {
                    var lat = newValue.lat;
                    var lon = newValue.lon;
                    $.getJSON("http://api.geonames.org/findNearestAddressJSON?lat=+"+
                            lat+"+&lng=+"+
                            lon+"+&username=demo").
                            then(function (json) {
                                var address = json.address;
                                $.each(json, function () {
                                    self.street(address.street);
                                    self.streetNumber(address.streetNumber);
                                    self.placename(address.placename);
                                    self.postalcode(address.postalcode);
                                    self.adminName1(address.adminName1);
                                });
                            });
                });
            };
        }
        return new navContentViewModel();
    });

Finally, here's how to render the properties above, i.e., in the matching view expressed in HTML:

<p><b>Address Details</b></p>
<div class="oj-flex">
    <div class="oj-flex-item oj-panel">
        <p>
            <span data-bind="text: street"></span>
            <span data-bind="text: streetNumber"></span>
        </p>
        <p><span data-bind="text: placename"></span></p>
        <p><span data-bind="text: postalcode"></span></p>
        <p><span data-bind="text: adminName1"></span></p>
    </div>
</div>

Monday Jun 13, 2016

Visualizing 3DCityDB in an Oracle JET Application

Let's take 3dcitydb-web-map and integrate it into an Oracle JET application, so that the map ends up contained within a module (defined by Require.js) in the helpful architecture that Oracle JET provides out of the box, all of it free and open source.

At the end of the instructions that follow, you'll have a browser-based application defined by Oracle JET and containing a 3D map:



Take the following steps:

  1. Clone everything from this repo: https://github.com/3dcitydb/3dcitydb-web-map.git

  2. Create a standard HTML/JavaScript application and let's start by setting up a small sample application that reorganizes the various pieces from the repo above into a working scenario. What we'll do is recreate the "b3dmBerlinExample" example, which is in the "examples" folder of the repo above. Start by copying the complete "index.html" from that example and pasting it over the default "index.html" file in your application, copy the folders as you see them below and change the references in the "index.html" file to point to the new locations.



    If the above works, i.e., you can run it and see a 3D map, everything's good and you can continue to the next step wherein you'll port the above structure into module within an Oracle JET application.

  3. The aim now is to migrate the standard HTML/JavaScript application above to an Oracle JET application. The starting point for the below is the "Quick Start Basic" template, which provides an out of the box architecture, including several modules.



    To get to the above point, copy the files from your earlier application, following the structure shown in the screenshot above.

  4. In the "requirejs.config" block, add the following to the "paths":
    'cesium': 'libs/Cesium-1-14-3dtiles/Cesium',
    'webmap': '3dcitydb-web-map',
    'b3dmlayer': 'b3dmlayer'
  5. Define the view as follows:
    <STYLE TYPE="text/css">
        <!--
        @import url(js/libs/Cesium-1-14-3dtiles/Widgets/widgets.css);
        -->
    </STYLE>
    
    <div id="cesiumContainer"></div>
    
  6. Here is the viewModel, i.e., the JavaScript side of the JET module:
    define(['ojs/ojcore', 'knockout', 'cesium', 'webmap', 'b3dmlayer'],
        function (oj, ko, cesium, webmap, b3dmlayer) {
            function mainContentViewModel() {
                var self = this;
                self.handleAttached = function () {
                    Cesium.BingMapsApi.defaultKey = someNumber'
                    var terrainProvider = new Cesium.CesiumTerrainProvider({
                        "url": "./terrain",
                        "requestVertexNormals": true
                    });
                    var viewer = new Cesium.Viewer('cesiumContainer', {baseLayerPicker: false,
                        terrainProvider: terrainProvider
                    });
                    var extent = new Cesium.Rectangle.fromDegrees(1,1,1,1);
                    viewer.camera.viewRectangle(extent);
                }
            }
            return new mainContentViewModel();
        });
    

And that's all you need to do to get started with 3dcitydb-web-map in an Oracle JET application.

Sunday Jun 12, 2016

Indoor Maps with Leaflet and Oracle JET

Let's take Leaflet Indoor and create an Oracle JET application (i.e., an enterprise-ready application based on Require, Knockout, JQuery, and more) that provides indoor map features:


Here are the steps:

  1. Clone everything from the repo at this location: https://github.com/cbaines/leaflet-indoor

  2. Put "leaflet-indoor.js", "leaflet-src.js", and "osmtogeojson.js" into the "js/libs" folder of your project.

  3. Add to the "paths" in "requirejs.config":
    'leaflet-indoor': 'libs/leaflet-indoor',
    'leaflet-src': 'libs/leaflet-src',
    'osmtogeojson': 'libs/osmtogeojson'
  4. Add to the "shim" in "requirejs.config":
    'leaflet-indoor': {
        deps: ['leaflet-src'],
        exports: 'L'
    }
  5. Copy "leaflet.css" into "css" and reference it in the "index.html".

  6. We'll now port one of the samples from the repo that you cloned in part 1, which uses OpenStreetMap, into a JET module. In the view of your JET module:
    <div id="map"></div>
  7. In the viewModel of your JET module:
    define(['ojs/ojcore',
        'knockout',
        'osmtogeojson',
        'leaflet-indoor',
        'leaflet-src'
    ], function (oj, ko, osmtogeojson) {
        function leafletContentViewModel() {
            var self = this;
            self.handleAttached = function () {
                var osmUrl = '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                        osm = new L.TileLayer(osmUrl, {
                            maxZoom: 22,
                            attribution: "Map data © OpenStreetMap contributors"
                        });
                map = new L.Map('map', {
                    layers: [osm],
                    center: new L.LatLng(49.41873, 8.67689),
                    zoom: 19
                });
                var query = '(relation(1370729);>>->.rels;>;);out;';
                $.get("//overpass-api.de/api/interpreter?data=" + query, function (data) {
                    var geoJSON = osmtogeojson(data, {
                        polygonFeatures: {
                            buildingpart: true
                        }
                    });
                    var indoorLayer = new L.Indoor(geoJSON, {
                        getLevel: function (feature) {
                            if (feature.properties.relations.length === 0)
                                return null;
                            return feature.properties.relations[0].reltags.level;
                        },
                        onEachFeature: function (feature, layer) {
                            layer.bindPopup(JSON.stringify(feature.properties, null, 4));
                        },
                        style: function (feature) {
                            var fill = 'white';
                            if (feature.properties.tags.buildingpart === 'corridor') {
                                fill = '#169EC6';
                            } else if (feature.properties.tags.buildingpart === 'verticalpassage') {
                                fill = '#0A485B';
                            }
                            return {
                                fillColor: fill,
                                weight: 1,
                                color: '#666',
                                fillOpacity: 1
                            };
                        }
                    });
                    indoorLayer.setLevel("0");
                    indoorLayer.addTo(map);
                    var levelControl = new L.Control.Level({
                        level: "0",
                        levels: indoorLayer.getLevels()
                    });
                    // Connect the level control to the indoor layer
                    levelControl.addEventListener("levelchange", indoorLayer.setLevel, indoorLayer);
                    levelControl.addTo(map);
                });
                $(window).on("resize", function () {
                    $("#map").height($(window).height()).width($(window).width());
                    map.invalidateSize();
                }).trigger("resize");
            };
        }
        return leafletContentViewModel;
    });
    

That's all you need to do. Also take a look at this example:

http://jsfiddle.net/fPEaV/

Saturday Jun 11, 2016

Getting Started Integrating LuciadRIA into Oracle JET

So you need Geospatial Situational Awareness in your Oracle JET application and you've decided that LuciadRIA is the right choice for you. LuciadRIA offers Geospatial Situational Awareness in the browser. LuciadRIA enables the development of advanced and easy-to-use browser applications, using web technologies, such as JavaScript, WebGL, and HTML5.

You've obtained LuciadRIA and now you're wondering how to integrate it into an Oracle JET application. The aim here is to get this result, as a kind of "hello world" scenario:

Here are the steps, with thanks to Tom Mahieu from Luciad, who provided me with an Oracle JET application that incorporates LuciadRIA and from which I have extrapolated the instructions below.

  1. In "js/libs", include "dijit", "dojo", "luciad", and "luciadui":



  2. In the "paths" of your "require.config" section, reference the libraries you added above:
    'dojo': 'libs/dojo',
    'dijit': 'libs/dijit',
    'luciad': 'libs/luciad',
    'luciadui': 'libs/luciadui'
  3. In the "css" folder, include "luciad.css" and reference it in your "index.html" file.

  4. In the view of one of your JET modules, include this:
    <div class="claro" 
         id="luciadMap" 
         style="width: 100%; height: 500px; background-color: black; position: relative;">
      <div id="logo"/>
    </div>
  5. In the viewModel of one of your JET modules, include this:
  6. define([
      'ojs/ojcore',
      'knockout',
      'jquery',
      'luciad/view/WebGLMap',
      'luciad/reference/ReferenceProvider',
      'luciad/model/tileset/FusionTileSetModel',
      'luciad/view/tileset/RasterTileSetLayer',
      'luciad/view/LayerType',
      'luciad/shape/ShapeFactory',
      'luciadui/layertreecontrol/FoldableLayerControl'
    ], function(oj, ko, $, WebGLMap, ReferenceProvider,
                FusionTileSetModel, RasterTileSetLayer,
                LayerType, ShapeFactory,
                FoldableLayerControl) {
      function createElevationLayer() {
        var tileSetReference = ReferenceProvider.getReference("EPSG:4326");
        var elevationParameters = {
          reference: tileSetReference,
          level0Columns: 4,
          level0Rows: 2,
          levelCount: 24,
          bounds: ShapeFactory.createBounds(tileSetReference, [-180, 360, -90, 180]),
          url: "http://fusion.3d.luciad.com/lts",
          coverageId: "1baecde9-0209-4570-b110-90eff3263e9b",
          tileWidth: 64,
          tileHeight: 64,
          dataType: FusionTileSetModel.DataType.ELEVATION,
          samplingMode: FusionTileSetModel.SamplingMode.AREA
        };
        return new RasterTileSetLayer(
                new FusionTileSetModel(elevationParameters),
                {label: "Elevation"}
        );
      }
      function createFusionBackgroundLayer() {
        var tileSetReference = ReferenceProvider.getReference("EPSG:4326");
        var elevationParameters = {
          reference: tileSetReference,
          level0Columns: 4,
          level0Rows: 2,
          levelCount: 24,
          bounds: ShapeFactory.createBounds(tileSetReference, [-180, 360, -90, 180]),
          url: "http://fusion.3d.luciad.com/lts",
          coverageId: "4ceea49c-3e7c-4e2d-973d-c608fb2fb07e",
          tileWidth: 256,
          tileHeight: 256
        };
        return new RasterTileSetLayer(
            new FusionTileSetModel(elevationParameters), {
              label: "Satellite imagery",
              layerType: LayerType.BASE
            }
        );
      }
      function LuciadContentViewModel() {
        var self = this;
        self.handleAttached = function() {
          var node = document.getElementById("luciadMap");
          self.map = new WebGLMap(node, {
            reference: ReferenceProvider.getReference("EPSG:4978")
          });
          new FoldableLayerControl(self.map, node);
          self.map.layerTree.addChild(createElevationLayer());
          self.map.layerTree.addChild(createFusionBackgroundLayer(), "bottom");
        };
        self.handleDetached = function() {
          self.map.destroy();
        };
      }
      return new LuciadContentViewModel();
    });
    

That's it, you're done. Make sure to include your license files in "libs/luciad/licenses" and then run the application and you should see the result with which this blog entry started!

Friday Jun 10, 2016

Geospatial Situational Awareness via LuciadRIA in Oracle JET

Luciad provides components that empower organizations to inject real-time Geospatial Situational Awareness into their software.

Part of their product offering is LuciadRIA, which offers Geospatial Situational Awareness in the browser. The fluency promised by LuciadRIA in the browser is that of the speed of their offerings for desktop applications. LuciadRIA enables the development of advanced and easy-to-use browser applications, using web technologies, such as JavaScript, WebGL, and HTML5.

Here's how components provided by LuciadRIA look when they are embedded in an Oracle JET application:

So, does your Oracle JET application need Geospatial Situational Awareness built into it? Take a look at LuciadRIA, it integrates very nicely into Oracle JET!

Thursday Jun 09, 2016

Referencing CSS from an Oracle JET Module

Here's the "home.html" of the "home" JET module my application. Here you can see how to reference a CSS file within a view of a JET module, rather than in the "index.html" file, where the standard CSS references is used. In this way, you can import fragments of CSS specific to a JET module and, in that way, create a modular approach to your CSS stylings:

<STYLE TYPE="text/css">
<!--
  @import url(css/styles.css);
-->
</STYLE>

<div id="circle"></div>

And here's the content of the CSS file referred to above: 

#circle {
    border-radius: 50px/50px;
    border:solid 21px #f00;
    width:50px;
    height:50px;
}

Wednesday Jun 08, 2016

Tools for MVC in Java EE 8 (Part 3)

Part 2 of this series was... November 12, 2014, i.e., Devoxx in Belgium in 2014, during which time I worked with Manfred Riem to create a plugin for NetBeans IDE to support his Devoxx presentation on MVC. Fast forward to today and here I am at Devoxx UK with Ivar Grimstad, working on the same plugin, though the MVC spec has changed quite a bit, requiring some new work on the plugin.

Ivar has a set of MVC samples here:

https://github.com/ivargrimstad/mvc-samples

The simplest one looks like this in NetBeans IDE:

After some discussion at the Devoxx UK hackergarden run by Heather VanCura, Ivar and I have scoped out what the plugin should do. (And in the meantime there's also Gaurav Gupta's great JPA Modeler, which provides a bunch of related features too.)

Take a look at the "RESTful Web Services" node in the screenshot above. Right below that node, we want to create a "MVC Controllers" node. Within that node, we'd like to list all Java source files that have a class-level or at least one method-level annotation of type "javax.mvc.annotation.Controller". Within each of the Controller nodes, we want to list all the MVC methods, which are either (1) all the public methods in the class if @Controller is used at class-level or (2) the specific methods within the class annotated with @Controller, if the class does not have the @Controller annotation.

Also, as a secondary feature, a further simplified application based on the above shown application will be in the New Project wizard, instead of the old one that is currently there. 

Here's the code from the original plugin, created with Manfred Riem, which will be the basis of the features described above:

https://github.com/GeertjanWielenga/BasicMVCSupport

A quick first attempt, simply tweaking a little bit without going into things too deeply, gives us the following:

As you can see, while the MVC Controller methods are shown, correctly, for "HelloController" (and can be double-clicked to get to the related source code), the other two Java source files shouldn't be there since they don't contain a @Controller annotation at all. So, right now (1) all the Java source files are shown regardless of whether they have the required annotation, while (2) methods are shown only if they have the annotation on them, not if the annotation is set on class-level.

So, it's a work in progress, though some way along already. 

Update 1: As you can see below, the two other Java source files are now not shown anymore, because the plugin parses the Java source file and determines whether methods have a @Controller annotation and, if a Java source file has one such method, it is included in the "MVC Controllers" node, otherwise it is excluded.

The next step will be to add functionality to not only work with method-level @Controller annotations, but class-level @Controller annotations as well. 

Update 2: Next, I can now display all the class members within a Java source file that has a class-level @Controller annotation:

The final step will be to only display public methods, not all class members, within a Java source file that has a @Controller annotation.

Update 3: Now it's working and here you can see the controllers within the class that contains them and nothing else:

Here's the plugin, install it into NetBeans IDE 8.1 to get the feature above:

http://plugins.netbeans.org/plugin/67897

Here it is in action on Ivar's system with a more complex sample app: 

Tuesday Jun 07, 2016

Piggybacking Foreign Beacons

The Android store has several rather effective beacon scanners:

With the above apps, and a few other similar ones, installed on my Android device, I rode my bicycle around Amsterdam yesterday. I didn't pick up very many beacons, though definitely some, in reasonably specific locations, e.g., near a group of restaurants and near a school. 

The information a beacon scanner provides is very specific, i.e., the complete UUID of the beacon is provided by the scanner. You can then create an app that piggybacks on that beacon, though that's of course only the first step. Next, you need to get people in the area to actually install that app for it to have any effect. Imagine that a museum has been beaconized, for example. You could then create a new app that piggybacks all the beacons in the museum and, somehow get visitors to install it (that shouldn't be too difficult), and then provide new/different information whenever the visitor approaches the beacon. Or think about beacons near a retail store, which you could piggyback and provide undermining advertizing, e.g., "No, don't come to this store, it is overpriced."

Are the UUIDs in a beacon scanner real or somehow masked? That depends on the security system that has been used. Right now, in Schiphol, using my beacon scanners, it turns out there are heaps of beacons around, far more than in the center of Amsterdam where I rode my bicycle yesterday. These are the beacons in the proximity of the spot where I am sitting while waiting to board a plane:

And, believe me, those UUIDs are real. You can't see the complete UUID in the screenshot above, though there's a very handy details tab for each beacon in the list above containing the complete UUID. I have built an app that detects them! I can walk around and get different info at different points in Schiphol. Now to get others to install my app, all I need to do is put up some posters with a barcode for installing my app together with a description along the lines of 'Awesome Schiphol Airport Discount Localizer'.

How ethical is all of this? Well, think of beacons as transmitters of airwaves. TV stations also transmit airwaves. Is it unethical to create a receiver for airwaves? On the other hand, think of beacons as hooks into mobile devices. If those hooks are unsecured and freely accessible, as clearly at least a subset of them are, there would appear to be a security risk that could be exploited in multiple different ways. I wonder to what extent these issues have been examined by those interested in promoting beacon technology. Randomly and regularly changing ("shuffling") the connection data transmitted by beacons so that the UUID, as well as other data, known as 'majors' and 'minors', cannot be relied on by external parties would appear to be a logical solution, take a look at kontakt.io, for example, such as their article Beacons Have Been Vulnerable For Too Long. It’s Time We Fixed It, where it turns out that piggybacking is the least of your worries when using beacons. :-)

In short, beacons have amazing potential, but make sure to secure them. Employ a team of hackers to try to hack into your beacons and wreak havoc, so that you know the vulnerabilities and can work to fix them.

Monday Jun 06, 2016

Monitoring Multiple Beacons on Android with Oracle JET

Following on from part 1 and part 2, let's now create the basis of a beacon solution for museums. The museum visitor walks into the museum and installs the museum app on their device. As they walk around the museum, the museum app detects beacons located near the visitor and, since each beacon has a unique ID, the app is able to distinguish between beacons. Each beacon is attached to a specific painting in the museum. Beacons are small, so can be neatly tucked away behind a painting. The museum app identifies the beacon and provides the related information about the painting to the museum app.

The below is an actual Android app installed on my Android device, i.e., the screenshot below is of a functioning Android app that implements the ideas sketched out above. 

(Yes, I know the above is not by Vincent van Gogh. All this is a simulation of a real museum app, including the content of the app itself.)

Something else to quickly point out—what we are talking about here has some interesting terminology: (1) the beaconizing of a museum and (2) creating a museum experience and (3) developing a beacon platform for museums and (4) modernizing the museum experience and (5) Museum 2.0 and (6) etc. Nice power terms can be used to orientate a potential customer to beacon technology.

We'll code this in two phases in an Oracle JET module. In the first phase, we won't incorporate images, just text. 

The simple UI:

<div class="oj-hybrid-padding">
    <h3>Museum</h3>
    <hr/>
    <div>
        Status: <span data-bind="text: indicator"></span>
        <br/><br/>Painter:<br/><br/>
        <h2><span data-bind="text: beaconSpecificMessage"></span></h2>
        <hr/>
        <br/><br/><button data-bind="click: detectBeacons">Enter here.</button>
    </div>
</div>

And the business logic:

var self = this;
self.indicator = ko.observable();
self.beaconSpecificMessage = ko.observable();
self.beacons = [
    {
        uuid: 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0',
        identifier: 'Rembrandt van Rijn',
        minor: 1,
        major: 1
    },
    {
        uuid: 'A164DFF8-9C6B-4FB5-A131-F5328DF48664',
        identifier: 'Vincent Van Gogh',
        minor: 1,
        major: 1
    },
    {
        uuid: '74278BDA-B644-4520-8F0C-720EAF059935',
        identifier: 'Jacob van Ruisdael',
        minor: 1,
        major: 1
    }
];
self.beaconRegions = [];
self.detectBeacons = function () {
    for (var i in self.beacons) {
        var b = self.beacons[i];
        self.beaconRegions[b.identifier] = new cordova.plugins.locationManager.BeaconRegion(b.identifier, b.uuid, b.major, b.minor);
    }
    var delegate = new cordova.plugins.locationManager.Delegate();
    delegate.didStartMonitoringForRegion = function (pluginResult) {
        self.indicator('Started!');
    };
    delegate.didDetermineStateForRegion = function (pluginResult) {
        var painter = pluginResult.region.identifier;
        self.beaconSpecificMessage(painter);
    };
    cordova.plugins.locationManager.setDelegate(delegate);
    for (var i in self.beaconRegions) {
        var b = self.beaconRegions[i];
        if (b === undefined)
            continue;
        cordova.plugins.locationManager.startMonitoringForRegion(b);
    }
};

The above is based on this:

https://github.com/petermetz/cordova-plugin-ibeacon/issues/190

Now, let's do the second phase, where we incorporate images. Based on the above, a different image could be provided matching the painting that the beacon represents:

delegate.didDetermineStateForRegion = function (pluginResult) {
    var painter = pluginResult.region.identifier;
    if (painter === "Rembrandt van Rijn"){
        self.beaconSpecificImage("css/images/rijn.png");
    } else if (painter === "Vincent Van Gogh") {
        self.beaconSpecificImage("css/images/gogh.jpg");
    } else {
        self.beaconSpecificImage("css/images/ruisdael.jpg");
    }
    self.beaconSpecificMessage(painter);
};

Maybe even neater than the if/else approach would be to integrate the images into the "self.beacons" array, i.e., add a new item in there called "image" and read it out via "pluginResult.region.image".  And here's the rewritten view, incorporating the images:

<div class="oj-hybrid-padding">
    <h3>Museum</h3>
    <hr/>
    <div>
        <input type="image"
               style="border:1px solid black"
               data-bind="attr:{src: beaconSpecificImage}"
               width="250"
               height="250"
               alt="No picture found."/>
        <br/><br/>Painter:<br/><br/>
        <h2><span data-bind="text: beaconSpecificMessage"></span></h2>
        <hr/>
        <br/><br/><button data-bind="click: detectBeacons">Enter!</button>
        Status: <span data-bind="text: indicator"></span>
    </div>
</div>

The key point here is that the beacon does not provide messages such as the name of the painter or the related image. Instead, the beacon simply provides its unique ID. It is up to the app to decide what to do with that information. In this particular case, each unique ID is mapped to a painter's name and related image, though it could be anything else as well.

Note: In doing all of the above, and while you don't have actual beacons, the software provided by Radius Networks is immensely helpful. Without something like MacBeacon, how are you going to simulate the multiple beacons you will be detecting and monitoring via the museum app described in this article? Take a look at the UI below and now you'll understand its relevance much better than in the earlier article where I introduced it, since you can switch from one simulated beacon to the other, switching a different one on, one at a time, simulating the experience of a visitor walking around a museum:

Finally, bear in mind that Bluetooth is used here and throughout everything that you do with beacons—the user of the app does not need to be connected to a Wifi network to use the museum app. However, somehow the museum app needs to be installed, though even that could be done without a Wifi network, e.g., via a barcode scanner.

Sunday Jun 05, 2016

Detecting and Monitoring Beacons on Android with Oracle JET

Now that we have a beacon simulator, let's use it in setting up a simple "hello world" scenario with Oracle JET and beacon detection. We'll end up with a basic Android application that detects the beacon we're simulating in the previous blog entry.

1. Set Up the Application. Create a hybrid Oracle JET application for Android:




2. Install the Cordova Beacon Plugin. After creating the application structure using the dialogs shown above, use the Terminal window (or simply the command line), to install cordova-plugin-ibeacon into the "hybrid" folder in your application:


3. Verify the Cordova Beacon Plugin Installation. In the "hybrid/plugins" folder, you should see the plugin has been installed after the previous step:


4. Code the Beacon Detector. We'll use the "incidents" module in the application, defined by "src/js/views/incidents.html" and "src/js/viewModels/incidents.js" to set up some basic beacon detection functionality. In "incidents.html", remove all the content and add this instead:

<div class="oj-hybrid-padding">
    <h3>Incidents Content Area</h3>
    <div>
        <button data-bind="click: detectBeacons">Start Monitoring</button>
        <ul data-bind="foreach: messages">
            <li><span data-bind="text: $data"></span></li>
        </ul>
        <button data-bind="click: clearList">Clear List</button>
    </div>
</div>
In "incidents.js", add the following right below "var self = this;":
self.messages = ko.observableArray();
self.clearList = function () {
    self.messages.removeAll();
};
self.detectBeacons = function () {
    var delegate = new cordova.plugins.locationManager.Delegate();
    delegate.didStartMonitoringForRegion = function (pluginResult) {
        self.messages.push("Started monitoring: " + JSON.stringify(pluginResult));
    };
    delegate.didDetermineStateForRegion = function (pluginResult) {
        self.messages.push("Found a beacon: " + JSON.stringify(pluginResult));
    };
    var uuid = 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0';
    var identifier = 'Hello World';
    var minor = 1;
    var major = 1;
    var beaconRegion = new cordova.plugins.locationManager.BeaconRegion(identifier, uuid, major, minor);
    cordova.plugins.locationManager.setDelegate(delegate);
    cordova.plugins.locationManager.startMonitoringForRegion(beaconRegion)
            .fail(function (e) {
                console.error(e);
            })
            .done();
};

5. Build, Deploy, and Use. Build ("grunt build platform=android") and run ("grunt serve platform=android destination=device") the application and, after it has installed, when you click the "Start Monitoring" button, you should see the messages you provided in the code above:


Above, you see a screenshot of my actual Android phone (i.e., this is not an emulator but my real device), with the UI created as defined in the HTML view shown above, which has its business logic created in JavaScript, as shown in the previous step too. 

The next step is to create a prettier user interface and Oracle JET has a lot of components (all free and open source) to help with that.

Continue to part 3 of this series...

Saturday Jun 04, 2016

Getting Started Detecting and Monitoring Beacons with Oracle JET

Steven Davelaar did an interesting session last week at the AMIS conference about beacons. (Get his excellent slides here.) The world of beacons is split into iBeacons, AltBeacons, and URIBeacons (read here), though there'll probably be more.

There's quite a few articles out there about incorporating beacon identifiers into MAF applications:

Of course, would be great to have a similar scenario for Oracle JET applications, especially since there's a nice Cordova plugin available that provides all the low level technology:

https://github.com/petermetz/cordova-plugin-ibeacon

However, the first stumbling block is that you actually need to HAVE a beacon in order to be able to develop applications for detecting and monitoring them. I mean, how do you test your beacon detecting app if you don't have a beacon to test with? So, before even setting up an Oracle JET project, you need to get yourself a beacon... or a beacon simulator.

In the Android app store I found a whole bunch of beacon scanners and beacon transmitters. The problem is that you don't want to simulate beacon transmissions from your phone, since that's also the place where your app will be installed. You need the transmission to be from a different device to where you'll be testing/using your beacon detection app. I have yet to find a free beacon transmission simulator for computers, i.e., they're only available for mobile devices. What I need to be able to do is simulate transmission from a laptop (ideally Windows, since that's what I'm on, or Mac OSX, which is my wife's laptop).

In the end, Radius Networks to the rescue. Not free, but hey, sometimes it makes sense to pay. I started out by buying MacBeacon, though didn't read the instructions and the Mac OSX I'm on (Mavericks) is not supported, though they kindly sent me a version of the app that works on Mavericks too. Here's how it looks and it works perfectly, though for some reason one cannot simulate more than one beacon at a time:

Now, on my Android, when I scan for beacons, I can detect the above simulated beacon. That's great because now I can create an app using Oracle JET and the Cordova plugin referred to above, install the app on my Android, and try it out via the simulated beacon provided by the app above.

I also bought QuickBeacon for Windows, so that I can use my development laptop to simulate beacons, though I'll need to wait to get a related USB, not sure why the Mac OSX app doesn't need a USB, while the Windows app does need it.

But, anyway, I'm on my way into the world of beacons via hybrid Oracle JET apps using Cordova.

Continue to part 2 of this series...

Friday Jun 03, 2016

Application Container Cloud, Oracle JET, and NetBeans IDE (Part 2)

In part 1, yesterday, we deployed a Node.js application, with static resources architected on Orace JET, to the Application Container Cloud Service (ACCS). However, ACCS is also applicable to Java SE applications.

There are several use cases for running Java SE applications on ACCS:

http://docs.oracle.com/cloud/latest/apaas_gs/apaas_tutorials_create_sample_java_se_applications.htm

I followed this scenario:

http://www.oracle.com/webfolder/technetwork/tutorials/obe/cloud/apaas/griz-jersey-intro/Grizzly-Jersey-Intro.html

However, I wanted to serve up JSON, rather than Strings, so I rewrote "getAllCustomers" in "CustomerService" to the following:

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/all")
public GenericEntity> getAllCustomers() {
    List list = CustomerList.getInstance();
    return new GenericEntity>(list) {};
}

More info in this example for JSON-related scenarios:

http://www.oracle.com/webfolder/technetwork/tutorials/obe/cloud/apaas/basic_grizzly_jersey/jersey-grizzly-json-service.html

Also, I needed a ContainerResponseFilter, to handle CORS:

import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
@Provider
public class CORSFilter implements ContainerResponseFilter {
    @Override
    public void filter(ContainerRequestContext request,
            ContainerResponseContext response) throws IOException {
        response.getHeaders().add("Access-Control-Allow-Origin", "*");
        response.getHeaders().add("Access-Control-Allow-Headers",
                "origin, content-type, accept, authorization");
        response.getHeaders().add("Access-Control-Allow-Credentials", "true");
        response.getHeaders().add("Access-Control-Allow-Methods",
                "GET, POST, PUT, DELETE, OPTIONS, HEAD");
    }   
}

My application now looks like this: 

I created a cloud-ready package with an uber JAR (in my case named "CustomerAwesomizer.jar"), as described in the documentation, and then uploaded it to ACCS: 

Here's the upload form that follows from the above:

After a bit, the application is available in my dashboard, as can be seen below, with a Java SE icon:

Next, now that the application is contained and deployed by ACCS, when I go to the REST endpoint in the browser I can see the JSON payload:

The final step is to create a user interface in JET:

Here's the JavaScript side of my JET module:

define(['ojs/ojcore', 'knockout', 'ojs/ojtable', 'ojs/ojdatacollection-common'
], function (oj, ko) {
    function GeneratedContentViewModel() {
        var self = this;
        self.data = ko.observableArray();
        $.getJSON("https://foo.oraclecloud.com/myapp/customers/all").
                then(function (json) {
                    $.each(json, function () {
                        self.data.push({
                            birthday: this.birthday,
                            city: this.city,
                            firstName: this.firstName,
                            lastName: this.lastName
                        });
                    });
                });
        self.datasource = new oj.ArrayTableDataSource(
                self.data,
                {idAttribute: 'id'}
        );
    }
    return GeneratedContentViewModel;
});

And here's the HTML: 

<h1>Customer View</h1>

<table id="table"
       data-bind="ojComponent: {
   component: 'ojTable',
      data: datasource,
      columns: [
            {headerText: 'Name',  field: 'firstName'},
            {headerText: 'Surname',  field: 'lastName'},
            {headerText: 'Birthday',  field: 'birthday'},
            {headerText: 'City',  field: 'city'},
]}">
</table>

The above is a simple end-to-end scenario of ACCS providing the backend and Oracle JET the frontend of an enterprise application.

Thursday Jun 02, 2016

Application Container Cloud, Oracle JET, and NetBeans IDE (Part 1)

At the AMIS Conference in Katwijk, the Netherlands, I attended a hackathon yesterday and learned a lot from my colleague Shaun Smith, who is the Oracle product manager working on Application Container Cloud Service (ACCS). 

What I wanted to achieve was this—create an Oracle JET application that renders data made available by an application running on ACCS. Here's Shaun and I setting up my environment and deploying an application to it:

In the end, we got everything working:

  • We have a Node.js application running on my instance of ACCS.
  • Using REST, it exposes underlying data.
  • The data is consumed in an Oracle JET application.

Here's the simple UI of the Oracle JET application. What's nice about it is that it consists of three different modules and that the table is defined by the Oracle JET "ojTable" component.

Here's the definition of the HTML view of the JET module that provides the ojTable:

<table id="table"
       data-bind="ojComponent: {
   component: 'ojTable',
      data: datasource,
      columns: [
            {headerText: 'Problem',  field: 'problem'},
            {headerText: 'Description',  field: 'description'},
            {headerText: 'Status',  field: 'status'},
            {headerText: 'Address',  field: 'formattedAddress'}
]}">
</table>

...and here's the JavaScript side providing the business logic of the HTML view shown above:

define(['ojs/ojcore', 'knockout', 'ojs/ojtable', 'ojs/ojdatacollection-common'
], function (oj, ko) {
    function GeneratedContentViewModel() {
        var self = this;
        self.data = ko.observableArray();
        $.getJSON("/incidents?technician=charlie").
                then(function (json) {
                    var metrics = json.result;
                    var location = json.result.location;
                    $.each(metrics, function () {
                        self.data.push({
                            problem: this.problem,
                            description: this.description,
                            status: this.status,
                            formattedAddress: this.location.formattedAddress
                        });
                    });
                });
        self.datasource = new oj.ArrayTableDataSource(
                self.data,
                {idAttribute: 'problem'}
        );

    }
    return GeneratedContentViewModel;
});

A special aspect of the solution is that the Oracle JET application is bundled as static resources within the Node.js application. We did this to overcome CORS problems we were struggling with. Below, the "client" folder, within my Node.js application, which is named "ifixitfast", contains my complete Oracle JET application:

The "client" folder is a static folder within my Node.js application thanks to this bit related to the Express framework on top of Node.js:

var express = require("express");
var app = express();
app.use(express.static('client'));

That line in bold is all that is needed, in this case in a file named "web.js".

What is also a useful piece of knowledge is how the ZIP file is created, which is uploaded via the UI on ACCS into my ACCS instance:

{
  "name": "ifixitfast",
  "version": "1.0.0",
  "description": "ifixitfast REST Service",
  "main": "web.js",
  "dependencies": {
    "debug": "^2.2.0",
    "express": "^4.13.4"
  },
  "scripts": {
    "start": "node web.js",
    "prepublish": "zip -r ifixitfast-dist.zip app client node_modules manifest.json package.json *.js"
  }
}

Notice the "prepublish" line at the end, where the name of the ZIP file is defined, which will be created, containing all the folders and files listed there, when "npm install" is run (which can be done from within NetBeans IDE).

Finally, the ZIP file is uploaded to ACCS, where magically everything is unpacked and set up such that when I go to the "index.html" of the URL, I automatically am served up the application from ACCS:

Alternatively, instead of running a Node.js application on ACCS, you can run Java SE applications, too, which could serve up JSON or XML in a similar way to how the Node.js application is doing in the example outlined above.

Wednesday Jun 01, 2016

Svalbard -- JourneyZone

One of the best experiences of my life was last year at JourneyZone, a trip for a selected subset of speakers from JavaZone, one of the main Java conferences in Scandanavia (in my mind, THE main conference!), held each year in Oslo, Norway.

It is hard to describe how awesome it was. We flew from Oslo to Svalbard, one of the wildest and least hospitable places I have yet been in, in my life. In Dutch, it is known as "Spitsbergen", since Svalbard has a Dutch history, in addition to a Russian one, as well as connections with the US, as the mining industry started after an American tourist named John Longyear came across some minerals, kicking off the mining industry. The capital, Longyearbyen, is named after him.

In open ribboats, we traveled to various parts around Svalbard, as can be seen below, which was extremely exciting.

We had a really brilliant time, networking and programming, in between visiting dog kennels where sleighdogs are bred and managed, as well as amazing meals and walks in barren terrains.

But no polar bears! Despite all our desire to find them. Only in the museum (below), as well as a similar one at the airport!

Maybe next time!

Many thanks to the organizers of JavaZone for the brilliant unforgettable experience.

Thursday May 26, 2016

Oracle JET and NetBeans IDE in Japan

I spent the first half of this week in Tokyo. I had never been there before and it was an excellent time, especially getting to know local Oracle colleagues and, of course, seeing parts of the city and culture.

The main focus of my trip was to speak at Java Day Tokyo, which was on Tuesday, May 24. I participated in the keynote, where I demoed the powerful new Java 9 tools in NetBeans IDE for working with Jigsaw modules, as well as Oracle JET and Application Builder Cloud Service. Together with that, I did two sessions, one on Oracle JET and one on NetBeans IDE, while I also spent some time at the Oracle JET booth doing small demos with passersby.

If I understand these Tweets correctly, attendees were impressed by JavaScript support, and other tools, in NetBeans IDE! 

Here's a pic of me talking about Oracle JET:

And here I am showing how NetBeans IDE interacts with the Chrome browser via the NetBeans Chome Connector plugin: 

I also met quite some Oracle partners and customers in Tokyo, introducing Oracle JET to them, together with the tools for creating enterprise JavaScript applications with NetBeans IDE. 

It was a great time in Tokyo, thanks to all the nice people I met there!

About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Categories
  • NetBeans IDE
Archives
« July 2016
SunMonTueWedThuFriSat
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
      
Today