Blog do projektu Open Source JavaHotel

niedziela, 31 stycznia 2016

Byliśmy na koncercie i operze

8 stycznia 2016 roku byliśmy na koncercie na Zamku Królewskim w wykonaniu orkiestry Warszawskiej Opery Kameralnej, ale wyszliśmy z mieszanymi uczuciami. Repertuar tworzyła muzyka barokowa, w pierwszej części utwory Vivaldiego, zaś w drugiej części dodatkowo muzyka Georga Philippa Telemanna. Co powodowało uczucie niedosytu u słuchaczy pięknej muzyki we wspaniałym wnętrzu Sali Wielkiej warszawskiego Zamku ? W pierwszej części wykonawcy wyraźnie się męczyli z instrumentami i partyturą, strona wykonawcza pozostawiała wiele do życzenia. Znacznie lepiej się słuchało w drugiej części, gdy artyści się rozgrzali. Wspaniale zwłaszcza zabrzmiał koncert na dwie altówki Telemana, brzmienie rzadziej spotykane. Także brawurowy koncert h-moll Vivaldiego na czworo skrzypiec ogromnie się podobał. Ale nierówny poziom wykonawczy zostawił nas z mieszanymi wrażeniami.

18 stycznia 2016 byliśmy na przedstawieniu mało popularnej opery Mozarta "Łaskawość Tytusa" w Teatrze Wielkim, podobało nam się ogromnie. Sam spektakl, w innym wykonaniu, można obejrzeć na Opera Platform.
"Łaskawość Tytusa" zawsze przedstawiała kłopot dla miłośników Mozarta, dla wielu najlepiej by było, gdyby Mozart do tej opery nigdy by nie usiadł. Jedni twierdzą, że kunszt kompozytora wzniósł się ponad statyczny i ociekający patosem tekst, inni uważają, że geniusz Mozarta poległ w starciu z tą materią, "nec Hercules contra plures". Najlepiej jednak zdanie wyrobić sobie samemu kupując bilety.
Mimo widocznych mielizn libretta kto kupił bilety na pewno nie żałował. Od strony muzycznej i wokalnej słuchało się znakomicie. Trochę słabo brzmiała Anna Bonitatibus w roli Sesta, chociaż dużo nadrabiała ekspresją aktorską. Doskonale wypadł majestatyczny i dostojny Charles Workman w tytułowej roli. Także ogromie się podobała Anna Bernacka śpiewająca jako Annio.
Trudny natomiast do odczytania był zamysł inscenizatorski, aczkolwiek od strony estetycznej i kolorystycznej oglądało się bardzo dobrze. Na scenie patrzyliśmy na współczesne wnętrze zamożnego domu, śpiewacy odczytują wiadomości na tabletach, rozmawiają przez komórki i wysyłają SMSy. Gdy ujawniany jest spisek przeciwko Tytusowi na scenie pojawiają się detektywi oznaczający ślady policyjnymi marketami.
Jednak takie współczesne odczytanie tekstu nie przydawało tutaj żadnego znaczenia, sprawiało wrażenie wystawiania pustych gadżetów. Namiętności targające bohaterami, miłość, namiętność, zdrada, przyjaźń, szlachetność, w osobie Vitelli przemawia żądza władzy, polityczne wyrachowanie, to wszystko brzmiało blado, tekst nie miał oparcia w inscenizacji.
Znacznie lepiej byłoby wrócić do źródeł, niech Tytus nadal będzie rzymskim cesarzem, Publio dowódcą Gwardii Pretorianów, Senat senatem a nie widownią telewizyjnego show, zaś w spisku na życie cesarza niech pojawią się Kapitol, spiskowcy, sztylety i zbuntowany rzymski motłoch. W ten sposób tekst miałby oparcie w inscenizacji i przedstawienie nabrałoby kolorów. Główna oś dramatyczna opery, konflikt między prawem nakazującym skazać zdrajcę na śmierć a wzdragającym się przed przemocą miłosierdziem, znacznie mocniej by wybrzmiał, gdyby tymi rozterkami był miotany cesarz, od którego podpisu na dokumencie zależy los milionów poddanych, niż gość pięciogwiazdkowego hotelu czy lokator luksusowego apartamentu.
Ale przedstawienie powinno być na pewno obowiązkowym punktem każdego melomana w Warszawie, trochę więc dziwiło, że widownia świeciła pustkami.

niedziela, 3 stycznia 2016

IBM InfoSphere Streams, PowerBI operator

Introduction
Previously I created a simple Java helper package to get access to PowerBI REST API. Now the time has come to develop Streams operator shipping data out  to PowerBI. Full source code is available here as StreamsStudio project.
Operator description, parameters
Operator (Java source code is available here) accepts seven parameters, five mandatory and two optional.
  • oauth_username
  • oauth_password
  • oauth_clientid: Parameters are required to receive access token to PowerBI. More details about PowerBI REST API and authentication is available here.
  • datasetName
  • tablename: Defines the table in PowerBI namespace.
  • flushsize: optional (default 1). Specifies the buffer size threshold before pushing data to PowerBI. Increasing the value improves performance but there is a delay between data is received and stored to PowerBI. The default is 1, every tuple is sent immediately to PowerBI. Value 0 has special meaning. Data is pushed to PowerBI when punctuation marker is received.
  • cleanfirstly: optional (default false). A logical parameter, if true then PowerBI table is truncated at the beginning.
Operator description, data loading
Pushing data is very simple, just create input streams reflecting the table structure in PowerBI and let the tuples flow. Only primitive data types are allowed (source).

private Map createTableSchema(OperatorContext context) throws PowerException {
  StreamSchema sche = context.getStreamingInputs().get(0).getStreamSchema();
  Map bischema = new HashMap();
  for (String name : sche.getAttributeNames()) {
   Type.MetaType ty = sche.getAttribute(name).getType().getMetaType();
   String biType = null;
   switch (ty) {
   case RSTRING:
   case USTRING:
   case ENUM:
    biType = PowerBI.STRING_TYPE;
    break;
   case INT8:
   case INT32:
   case INT64:
   case UINT8:
   case UINT16:
   case UINT32:
   case UINT64:
   case INT16:
    biType = PowerBI.INT64_TYPE;
    break;
   case DECIMAL128:
   case DECIMAL32:
   case DECIMAL64:
   case FLOAT32:
   case FLOAT64:
    biType = PowerBI.DOUBLE_TYPE;
    break;
   case BOOLEAN:
    biType = PowerBI.BOOL_TYPE;
    break;
   case TIMESTAMP:
    biType = PowerBI.DATETIME_TYPE;
    break;
   default:
    break;
   }
   if (biType == null)
    failure("Attribute " + name + " type " + ty.getLanguageType() + " not supported");
   log.log(TraceLevel.DEBUG, "Attribute " + name + " type " + ty.getLanguageType() + " mapped to " + biType);
   bischema.put(name, biType);
  }
  return bischema;
 }

Usage example

namespace application.test ;

use com.ibm.streams.powerbi::PowerBI ;

composite Main
{
 graph
  (stream
   Beacon_1_out0 as O) as Beacon_1 = Beacon()
  {
   logic
    state : mutable int32 i = 0 ;
   param
    iterations : 100 ;
    period : 0.1f ;
   output
    O : name = "Name " +(rstring)(i ++), num =(uint32)(i ++), flo = 1.23, log =
     i / 2 == 0 ? true : false, ti = createTimestamp(1000l, 100u) ;
  }

  () as PowerBI_2 = PowerBI(Beacon_1_out0)
  {
   param
    oauth_clientid : "22dfcddc-d5b4-4e8a-8358-1658fabdad0b" ;
    oauth_password : "Szczuja123" ;
    oauth_username : "szczodry@szczerbek.onmicrosoft.com" ;
    datasetName : "InfoS6" ;
    tablename : "table6" ;
    flushsize : 10 ;
  }

  () as Custom_3 = Custom(Beacon_1_out0 as inputStream)
  {
   logic
    onTuple inputStream :
    {
     println(inputStream) ;
    }

  }

}
More examples.
Loading modes
Three loading modes are supported. It is specified by parameter flushsize, described above.

  • flushsize = 1 (default). The incoming tuple is immediately pushed to PowerBI. Data is immediately available but there is a performance penalty.
  • flushsize > 1. Sending tuples to PowerBI is held off until the threshold is exceeded and then the whole buffer is shipped out in a single operation. It is more efficient but there is a gap between the time when data arrive and are available in PowerBI.
  • flushsize = 0. Tuples are buffered and sent to PowerBI when punctuation marker is received.
Dependency
In addition to PowerBI Java package, additional dependency is imposed by this library. It is specified by pom.xml file. The dependencies are stored in imp/lib directory in operator structure. The directory is defined by Lib annotation in operator body.

Additional remarks
  • Only one table can be defined per dataset. I cannot tell if it is a limitation of PowerBI or defect in PowerBI REST API
  • If a table does not exist then it is created during initialization of the operator. The table schema mirrors the schema of the operator input stream. Table schema cannot be changed later. If the table is already created then changing operator input stream schema later will cause a crash. 


czwartek, 31 grudnia 2015

New version of JavaHotel application

Introduction
The new version of JavaHotel application has been deployed. Demo version (U/P user/user), full source code is available here. A new option is a journal, a register of daily activities of the user. The journal gives the manager an insight into daily business and workload of all users.
Journal
Journal is available from upper menu: Administrator->Journal

A subset of user action is recorded, the actions related to changes in the status of database and reservation or some customer actions like sending a confirmation note or registering a payment.
Filtering
Also, a convenient journal filtering is implemented. Activities can be filtered on users and activity type.


Future

  • Graphic presentation of journal
  • More granular reporting related to the time of the day, answering the question: "at what time of the day some type of action is more frequent ?"

niedziela, 29 listopada 2015

Power BI, Java and IBM InfoSphere Streams

Introduction
Power BI is part of Microsoft Azure package enabling real-time analytics and visualization. Power BI can be fed by data from multiple sources. Very interesting feature is REST API interface, thereby allowing integration with any tool or application that supports REST calls.
IBM InfoSphere Streams
Although Streams does not contain any direct support for REST interface, it can be easily implemented by com.ibm.streamsx.inet toolkit.
Authentication using com.ibm.streamsx.inet toolkit
To start working with Power BI REST API, we have to authenticate and receive authentication token used later for other REST API calls. Authentication can be done through HTTPPost operator.

use com.ibm.streamsx.inet.http::HTTPPost ;
use com.ibm.streamsx.inet.http::* ;
use com.ibm.streamsx.inet.http::HTTPGetStream ;

boolean responseOk(HTTPResponse r)
{
 return(r.errorMessage == "OK" && r.responseCode == 200) ;
}

composite BiTest
{
 type
  INPUT = tuple<rstring username, rstring password, rstring client_id,
   rstring grant_type, rstring resource> ;
 graph
  (stream<HTTPResponse> HTTPPost_1_out0) as HTTPPost_1 = HTTPPost(Custom_2_out0
   as InputStreamName0)
  {
   param
    url : "https://login.windows.net/common/oauth2/token" ;
  }

  (stream<INPUT> Custom_2_out0) as Custom_2 = Custom()
  {
   logic
    onProcess :
    {
     println("Submit") ;
     submit({ username = "szczodry@szczerbek.onmicrosoft.com", password =
      "Szczuja123", client_id = "22dfcddc-d5b4-4e8a-8358-1658fabdad0b",
      grant_type = "password", resource =
      "https://analysis.windows.net/powerbi/api" }, Custom_2_out0) ;
     println("Done") ;
    }

  }

  (stream<rstring token> Custom_3_out0) as Custom_3 = Custom(HTTPPost_1_out0 as
   S)
  {
   logic
    onTuple S :
    {
     println(S) ;
     if(! responseOk(S))
     {
      println("Error") ;
      submit(Sys.FinalMarker, Custom_3_out0) ;
     }

     rstring token = getJSONField(S.data, "access_token") ;
     submit({ token = token }, Custom_3_out0) ;
    }

  }
}
To extract authentication token from JSON response, a simple Java function is used
/* Generated by Streams Studio: November 6, 2015 12:16:07 AM GMT+01:00 */
package application;


import java.io.IOException;

import com.ibm.json.java.JSONObject;
import com.ibm.streams.function.model.Function;

/**
 * Class for implementing SPL Java native function. 
 */
public class GetJSONFieldImpl  {

    @Function(namespace="application", name="getJSONField", description="", stateful=false)
    public static String getJSONField(String JSON,String field) throws IOException {
     JSONObject obj = JSONObject.parse(JSON);
     return (String) obj.get(field);
    }
    
}
Using HTTPost operator and wrapper Java function to extract JSON data we can implement all POST Power BI REST API calls.
So far so good, but what about GET method to receive, for instance, a list of all data sets in Power BI dashboard or DELETE method to remove all rows from Power BI table?
Unfortunately, I was unable to make GET REST API call through  HTTPGetStream operator. This is because the HTTPGetStream operator is designed to provide a constant flow of input data, it is not designed for a single request-response action. Although it is possible to make use of this operator, I found it very artificial and decided to go the other way. Needless to say, HTTP DELETE method is not available at all.
Next approach, Java callouts
Prior to InfoSphere Streams application, I implemented a small Java utility class for accessing Power BI REST API calls. To avoid any complications, Java class is designed as a set of static stateless methods, without class instances or local variables. It is the client responsibility to store and pass Power BI authentication token. This way Java methods can be executed in parallel, which is essential for Streams development.
Source code for Java class is available here. The only dependency is GSON library and Apache HttpComponents. Usage examples are available here.
Power BI table schema definition
Table schema is defined as a Map<String, String> table schema. Map key is column name and value is column data type. Data types available are enumerated in PowerBI.java.

/** PowerBI column types */
 public static final String INT64_TYPE = "Int64";
 public static final String STRING_TYPE = "string";
 public static final String DOUBLE_TYPE = "Double";
 public static final String BOOL_TYPE = "bool";
 public static final String DATETIME_TYPE = "DateTime";
Using Java map is very convenient, the only disadvantage we cannot control the order of columns. It is ordered by column names alphabetically.
List of rows to be uploaded to Power BI table
A single row cell is defined by TableValue class. The table constructor determines the cell types. Example for integer value:

public TableValue(long intvalue) {
   this.intvalue = intvalue;
   vType = valueType.isint;
   stringvalue = null;
   doublevalue = 0;
   boolvalue = false;
   timeValue = null;
   timeS = null;
  }
Null value is supported as well. One row is specified by Map<String, TableValue>. The list of rows, chunk of data to be loaded, is defined by list of rows List<Map<String, TableValue>> 
List of methods
  • getAuthToken Authentication and procuring the authentication token
  • getDataSets Get list of datasets
  • getDataSetId Get data set id of a particular dataset
  • getDataSetTables Get list of tables of a particular dataset
  • createDataSet Create data set if does not exist and create a table belonging to this dataset
  • addTableRows Push data into the table
  • clearTable Remove content from the table
  • updateTableSchema Update schema of existing table. Important: this REST API call seems not working
  • checkTableDataSet Check if dataset and table exist, creates if necessary
Remarks
  • Only one table per dataset is created.
  • Columns in table schema is ordered alphabetically
  • Update table schema is not working
Next steps
Create Streams operator for pushing data into Power BI table.

niedziela, 18 października 2015

Byliśmy na koncerce

21 października byliśmy na koncercie muzyki organowej w kościele pod wezwaniem św. Antoniego Padewskiego , podobało nam się bardzo. Wykonawcą był włoski wirtuoz Gabrielle Catalucci. Na program składała się muzyka barokowa głównie kompozytorów włoskich, od wczesnego baroku, który reprezentował  Girolamo Frescobaldi aż do J.S. Bacha. Alessandro Scarlatii  głównie zasłynął jako autor oper i muzyki kameralnej, ale zostawił po sobie także kilka utworów na organy. Jego syn, równie sławny Domenico Scarlati, oprócz kilkuset sonat na instrumenty klawiszone, dzisiaj wykonywanych na klawesynie lub fortepianie, napisał także cztery sonaty na organy, z których jedna została wykonana na koncercie.  Antonio Vivaldi nie tworzył muzyki na organy solo, ale trafił na koncert za pośrednictwem niemieckiego kompozytora Johanna Gottfrieda Walthera. J.G. Walther zasłynął dzięki transkrypcjom na organy utworów innych kompozytorów, na koncercie w kościele św Antoniego została wykonana transkrypcja koncertu skrzypcowego Vivaldiego E minor, op 4, No 2 RV 279. Bardzo dynamiczny i efektowny utwór nic nie stracił w wersji organowej. Walther był współczesny J.S. Bachowi i to prawdopodobnie za jego przykładem Bach napisał własne transkrypcje kompozycji Vivaldiego na organy i klawesyn, a także tranksypcje utworów innych kompozytorów, wzbogacając w ten sposób swój warsztat muzyczny. Pomimo, że nie Bach był tutaj głównym autorem, to te transkrypcje są bardzo chętnie wykonywane także dzisiaj.
Koncert, podobnie jak całą epokę baroku w muzyce, dopięło kilka kompozycji J.S. Bacha.

czwartek, 8 października 2015

New features in Jython MVP framework

Introduction
I added several improvements to Jython MVP framework. The source code is available here. Demo version running in  Google App Engine is here.
Change focus
This feature allows an automatic jump to the particular field in the form. Is useful at the beginning allowing user start entering data in the proper field or, in case of error detected, move to the wrong field. This option is demonstrated in the demo application, Alerts -> Dialog with focus.

More detailed description.
Field with dynamic list of images
This option was created firstly for the editable list. But now it is extended to form as well. Declaration and handling are very similar. More details. The option is showed in the demo application, Alerts -> Dialog with image field.

sobota, 26 września 2015

MSSQL -> DB2 migration, useful awk scripts

Introduction
Database migration is a painstaking task, mostly manual, but with a little help of simple awk scripts it can be alleviated. As an example, I'm using Adventure Works Databases 2012 sample data and by means of "Generate scripts" tool I'm creating text scripts containing all SQL objects.
First task
Output scripts are encoded as UTF-16, therefore one can spend a lot of time running grep command without any result. Thus, the first thing to do is to transform scripts to UTF-8 encoding and change CRLF line break to LF.
iconv -f UTF-16 $1 | dos2unix >`basename $1`
List of objects
Before starting any activity, it is a good idea listing all objects to migrate and prepare a working spreadsheet to track migration progress (listobjects.awk)
awk -v objtype=ALL -f listobjects.awk iscript.sql  >list.csv


Tables
For tables migration very useful is free tool DCW (Data Conversion Workbench). This tool extracts table schema directly from MSSQL and transforms them to DB2 format. Important: the tool does not handle foreigns key correctly, but it can be improved by a Python tool.
Split objects into files
Although MSSQL "Generate scripts" tool allows exporting objects into separate files, very often we have the objects in one big text file.  Nevertheless, during migration it is very convenient to have a single object in a single file and migrate them one after one. obj.awk script executes this task.
awk -v extracttype=ALLOTHERS -f obj.awk iscript.sql 
As a result, all objects except tables, are put into directories: views, triggers, types, procedures and function.
Example: view directory
sbartkowski@linuxmachine:~/work/testb$ ls views
humanresources_vemployeedepartmenthistory.sql  production_vproductanddescription.sql           sales_vindividualcustomer.sql
humanresources_vemployeedepartment.sql         production_vproductcatalogimages.sql            sales_vpersondemographics.sql
humanresources_vemployee.sql                   production_vproductcatalog.sql                  sales_vsalespersonsalesbyfiscalyears.sql
humanresources_vjobcandidateeducation.sql      production_vproductmodelcatalogdescription.sql  sales_vsalesperson.sql
humanresources_vjobcandidateemployment.sql     production_vproductmodelinstructions.sql        sales_vstorewithaddresses.sql
humanresources_vjobcandidate.sql               production_vworkorderrouting.sql                sales_vstorewithcontacts.sql
person_vadditionalcontactinfo.sql              purchasing_vvendorwithaddresses.sql             sales_vstorewithdemographics.sql
person_vstateprovincecountryregion.sql         purchasing_vvendorwithcontacts.sql              sales_vterritorysalesdrilldown.sql
production_vmanufacturinginstructions.sql      sales_vcompanysales.sql

Tables are split using another parameter. As an input file, the result of DCW tool is used.
awk -v extracttype=TABLES -f obj.awk tables.sql
Objects are put into tables and constraint directories.
sbartkowski@linuxmachine:~/work/testb$ ls tables
humanresources_department.sql                 production_location.sql                               purchasing_purchaseorderheader.sql
humanresources_employeedepartmenthistory.sql  production_productcategory.sql                        purchasing_shipmethod.sql
humanresources_employeepayhistory.sql         production_productcosthistory.sql                     purchasing_vendor.sql
humanresources_employee.sql                   production_productdescription.sql                     sales_countryregioncurrency.sql
humanresources_jobcandidate.sql               production_productdocument.sql                        sales_creditcard.sql
humanresources_shift.sql                      production_productinventory.sql                       sales_currencyrate.sql
person_address.sql                            production_productlistpricehistory.sql                sales_currency.sql
person_addresstype.sql                        production_productmodelillustration.sql               sales_customer.sql
person_businessentityaddress.sql              production_productmodelproductdescriptionculture.sql  sales_personcreditcard.sql
person_businessentitycontact.sql              production_productmodel.sql                           sales_salesorderdetail.sql
person_businessentity.sql                     production_productphoto.sql                           sales_salesorderheadersalesreason.sql
person_contacttype.sql                        production_productproductphoto.sql                    sales_salesorderheader.sql
person_countryregion.sql                      production_productreview.sql                          sales_salespersonquotahistory.sql
person_emailaddress.sql                       production_product.sql                                sales_salesperson.sql
person_password.sql                           production_productsubcategory.sql                     sales_salesreason.sql
person_personphone.sql                        production_scrapreason.sql                            sales_salestaxrate.sql
person_person.sql                             production_transactionhistoryarchive.sql              sales_salesterritoryhistory.sql
person_phonenumbertype.sql                    production_transactionhistory.sql                     sales_salesterritory.sql
person_stateprovince.sql                      production_unitmeasure.sql                            sales_shoppingcartitem.sql
production_billofmaterials.sql                production_workorderrouting.sql                       sales_specialofferproduct.sql
production_culture.sql                        production_workorder.sql                              sales_specialoffer.sql
production_document.sql                       purchasing_productvendor.sql                          sales_store.sql
production_illustration.sql
Tranformation
The obj.awk script also performs simple MSSQL->DB2 transformation (modifmsobj.awk)

BEGIN { PRINT = 1 }

function outputline(line) {
  if (PRINT == 1) print line
}

/^@/ { 
     PRINT = 0
     print $0
}

{
  line = gensub(/\[|\]/,"","g",$0)   
  line = gensub(/@/,"v","g",line)   
  line = gensub(/+/,"||","g",line)   
  line = gensub(/ISNULL/,"COALESCE","g",line)   

  outputline(line)
}
  • Removes []
  • Replaces @ variable marker with v character
  • MSSQL uses + for string concatenation. The script replaces + with ||. Important: all + are replaced, also + used for artihtmetic computation.
  • Replaces ISNULL with COALESCE
Also for types a simple tranformation is conducted (modiftypes.awk).
Conclusion
Although MSSQL->DB2 migration is still manual and mundane process by applying very simple automation we can speed it up and make it more organized. Very interesting object splitting combine with transformation. This transformation (take a look into the body of obj.awk script) can be enhanced regarding the needs.