Blog do projektu Open Source JavaHotel

niedziela, 29 czerwca 2014

JavaHotel, new version

Introduction
I deployed next version of JavaHotel application. Demo version (Google App Engine) is available here (U/P user/user) and source code here.
Searching
I added a searching (so far new reservation was activated after clicking in the hotel room/day cell).
After clicking "Search" button a list of hotel rooms available is displayed :
and the user can select a room and a reservation can be started.
Reservation extension
After reservation is booked it is possible to extend it by adding another room to the reservation or extend the reservation period for the room already booked.  This option is available after clicking "Extend reservation" button.
 It is the same searching mechanism as above but after clicking "Add to reservation" button the room selected is added to the existing reservation.
Reservation modification
Also more detailed modification of existing reservation is possible after clicking "Modify reservation" button.

The user can modify reservation for the single day/room line, remove one line from the reservation and add a new line.
Customer/guest info
This option is available through "Upper menu" -> "Administrator" -> "List of customers" -> "Info".
This window shows the activity of the particular customer.
Room/service info
"Upper menu" -> "Administrator" -> "Services" -> "Modify" -> "Show rooms"
List of hotel rooms sharing this service.
Additional changes
  • Modifying list of guests (checked in) also during a stay.
  • "Administrator" - blockage for removal objects (rooms, customers, services, price list) if there exists any dependent object (for instance: cannot remove a hotel room if there is exist a reservation for this room). Future: add option "out of use" or "deactivate" the object.
  • "Spinner" widget (look above) to make user interface more user friendly.
Next step
Implements charts (I'm going to use Google Charts ) to visualize room, service or customer activity.

piątek, 20 czerwca 2014

MVP Jython framework, new enhancements

Introduction
I added several enhancement to MVP Jython framework. Sample application is available here, source code here.
URL parameters
Access to URL parameters in the Jython backing code. It allows passing some additional parameters to the application.  Description is available here. In the sample application : Different lists -> URL params.
Additional standard toolbars for list
It is the small enhancement but allows to avoid repeating the same sequence of buttons. Description is available here.
Application parameters
This enhancement allows injecting some application parameters into the dialog definition. Description is available here.
Spinner widget
Spinner widget allows entering integer values without typing keyboard. It is convenient method if the range limit is not very big. Sample application : Status -> Spinner and Status -> List with spinner. For dialog form spinner range values can be modified dynamically. Description is available here.
JavaScript backing method
JavaScript function can be defined as a response action. Because JavaScript is executed at the client side and it makes user interface more agile. Defining JavaScript response action is possible only if server side action (for instance database querying) is not required. Sample application : Alert -> Dialog with JS. Description is available here.

środa, 18 czerwca 2014

Byliśmy na operze

W dniu 24 maja 2014 byliśmy w Warszawskiej Operze Kameralnej na przedstawieniu "Napój miłosny" Gaetano Donizettiego, podobało nam się bardzo, chociaż nie wszystkim byliśmy zachwyceni.
"Napój miłosny" to jedno z najbardziej znanych dzieł Donizetttiego, stało się sukcesem niemal od pierwszego wystawienia i po dziś dzień jest jednym z najbardziej popularnych i najchętniej wystawianych dzieł z operowego repertuaru.
"Napó̉j miłosny" jest obecny  w repertuarze Warszawskiej Opery Kameralne od wielu lat, całe przedstawienie jest bardzo dobrze przygotowane i zaśpiewane. Czasami się tylko wydawało, ze głosy śpiewaków rozsadzają skromną scenę teatru. Znakomicie się zaprezentowała młoda śpiewaczka, Agnieszka Kozłowska w roli Aldiny, bardzo dobrze jej partnerowali pozostali soliści.
Natomiast nie spodobała się scenografia. Scenę, oprócz solistów, ożywiają mimowie ubrani w stroje z włoskiej komediu dell'arte, na scenie widzimy postacie przypominające Arlekina i Kolombinę. Mimowie gestami i ruchami podkreślają rozwój akcji, czasami biorąc w niej aktywny udział. Bardzo dobrze się to sprawdza w początkowej scenie, gdy Adina głośno opowiada przeczytaną historię Tristana i Izoldy i mimowie na drugim planie odgrywają tę scenę. Ale taka konwencja nadaje całemu przedstawieniu wyraz ogromnej sztuczności, umowności. "Napój" to przecież historia wielkiej miłości biednego, niepiśmiennego i nieśmiałego wieśniaka Nemorina do bogatej i kapryśnej dziedziczki Adiny. Z pozoru wszystko przemawia przeciwko Nemorinowi, ale jego uczucie jest tak gorące i szczere, że "doktor" oszust Dulcamara przemienia się w prawdziwego cudotwórcę, fałszywa mikstura staje się magicznym napojem, sierżant Belcore, istny bóg wojny Mars któremu pada w ramiona nawet bogini miłości Wenus, poddaje się i rejteruje, sam Nemorino dziedziczy majątek i na końcu zdobywa uczucie pięknej Adiny. Rzeczy fałszywe i niemożliwe stają się prawdziwe i osiągalne.
Jest to historia bajkowa, ale przecież nie baśniowa, nierealistyczna, i to właśnie trochę zgrzyta ze scenografią.
Od premiery tej opery w Warszawskiej Operze Kameralnej minęło już 7 lat, może czas najwyższy aby odświeżyć sceniczną koncepcję przedstawienia i dać lepszą oprawę do wspaniałej gry solistów.

piątek, 30 maja 2014

GWT, Spinner and HTML 5

GWT standard widget set does not contain number "spinner" but it is very easy to reuse HTML 5 "spinner" input control.

class SpinnerInt extends TextBoxBase {

    public static native InputElement createInputElement(Document doc,
            String type, String min, String max) /*-{
  var e = doc.createElement("INPUT");
  e.type = type;
  e.min = min;
  e.max = max;
  return e;
    }-*/;

    SpinnerInt(int min, int max) {
        super(createInputElement(Document.get(), "number",
                Integer.toString(min), Integer.toString(max)));
        this.addStyleName("spinner-style");
    }

}

Using the same technique one can also implement others HTML 5 widgets not implemented in GWT so far (like "time")

środa, 21 maja 2014

JavaHotel, Jython 2.7 and Google App Engine

Introduction

I decided to go ahead and upgrade Jython I'm using from Jython 2.5 (Jython 2.5.4rc1) to Jython 2.7 (Jython 2.7beta2). It works without any problem but I was stuck after deploying to Google App Engine.  The exception error was as follows:

Caused by: java.lang.ExceptionInInitializerError
 at jnr.posix.POSIXFactory.(POSIXFactory.java:15)
 at org.python.modules.posix.PosixModule.(PosixModule.java:63)
 at org.python.modules.Setup.(Setup.java:29)
 at org.python.core.PySystemState.initBuiltins(PySystemState.java:1317)
 at org.python.core.PySystemState.doInitialize(PySystemState.java:1056)
 at org.python.core.PySystemState.initialize(PySystemState.java:982)
 at org.python.core.PySystemState.initialize(PySystemState.java:938)
It was very nasty because it worked in Development Mode (local environment) and it is not easy to debug the application after deploying to the production environment.

Solution 

After spending some time, analyzing exception stack and digging through different versions jnr-posix module (used by Jython for system related stuff) especially Platform class I found the solution.  It was because Google App Engine does not set os.name property (it is null). So the solution was to enhance appengine-web.xml file.
  <system-properties>
    ......
    <property name="os.name" value="linux"/>
    <property name="os.arch" value="amd64"/>    
  </system-properties>
It works now but two questions are pending around me:
  • How it worked with Jython 2.5
  • While Google App Engine set this variables in Development Mode and does not set in Production Mode.

piątek, 9 maja 2014

Glassfish to JBoss (WildFly) migration

Introduction
I decided to migrate JavaHotel application to WildFly (former JBoss) keeping Glassfish version still alive. The reason was very simple. I was unable to call remote EJB deployed to Glassfish. It works from another Glassfish, also from Java standalone application  (following instructions under this link) but I failed to call EJB from another web container (Tomcat or Jetty). I tried to follow instructions from this blog. But the list of jars is valid for Glassfish 3.0, in 4.0 the jars are different and after spending a lot of time combining different sets of jars I finally gave up and switch to WildFly. I was successful but had to overcome some problems related not only to migration from one web container to another but also related to migration from EclipseLink JPA to Hibernate JPA.
Last but not least - to call remote EJB deployed to WifdFly server it is enough to add jboss-client.jar to classpath regardless of the client type.
I'm ignoring trivial tasks related to migration like: data source migration, EJB addressing, JNDI variables etc.
Several persistence-units in the single persistence.xml
Problem
I used to have a single persistence.xml file containing all persitence-units I'm using in different configurations. For instance "RESOURCE-LOCAL" persistence-unit for Tomcat and "JTA" fo JEE container. EclipseLinks at the moment of invoking Persistence.createEntityManagerFactory reads only persistence-unit requested, Hibernate reads the whole content of persistence.xml just failing if any of the persistence-units cannot be initialized.
Solution
Very simple : just split the single persistence.xml into several containing only persitence-unit necessary in the given context.
Different strategies for @Id @GeneratedValue(strategy=GenerationType.AUTO)
Problem
EclipseLink and Hibernate use different strategies for generating global object IDs. EclipseLink creates a separate table (sequence) and Hibernate JPA uses sequence hibernate_sequence (checked for Postresql and ApacheDerby). So after switching from one framework to another on existing table one can run into trouble (duplicate primary key) when both methods overlaps. It costed me a lot of time to discover the problem.
Solution
Use different @GeneratedValue strategy or do not switch framework.
@Lob column for PostgresSql
Problem
@Entity
public class LobRecord implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
    @Lob
    private byte b[];

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @return the b
     */
    public byte[] getB() {
        return b;
    }

    /**
     * @param b the b to set
     */
    public void setB(byte[] b) {
        this.b = b;
    }
    
}
Unfortunately, Postgresql does not have direct BLOB or LOB column type. EclipseLink translates the @Lob column to bytea (what I expected) but Hiberbate generates oid column type which causes a lot of trouble.
Solution
There is no universal solution to this problem.
  1. Remove @Lob annotation. Both EclipseLink and Hibernate generates bytea column type. But after removing this annotation for ApacheDerby this column is generated as VARCHAR () FOR BIT DATA (not BLOB) which is not valid
  2. Add direct specification @Column(columnDefinition="bytea". But it will not work for ApacheDerby (or any other database, it is PostreSql specific)
  3. Adjust annotation any time database or framework is changed. 
Hiberate does not like hierarchical references in DELETE command
Problem
@Entity
public class CountryRecord implements Serializable {
....
}

@Entity
public class ProvinceRecord implements Serializable {
...    
    @JoinColumn(nullable=false)
    private CountryRecord country;
}

@Entity
)
public class CityRecord implements Serializable {

...
    @JoinColumn(nullable = false)
    private ProvinceRecord prov;
...
}

@NamedQueries({
    @NamedQuery(name = "removeCities", query = "DELETE FROM CityRecord r WHERE r.prov.country= ?1")
p.country= ?1)")
}

EclipseLink does not complain but Hibernate throws an exception
ERROR: HHH000177: Error in named query: removeCities
org.hibernate.QueryException: could not resolve property: country of: teste.entity.CityRecord [DELETE FROM teste.entity.CityRecord r WHERE r.prov.country= ?1]
 at org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:83)
 at org.hibernate.persister.entity.AbstractPropertyMapping.toType(AbstractPropertyMapping.java:77)

What is more intriguing - Hibernate has nothing against this type of hierarchy in SELECT statement.
Solution
Replace hierarchical reference by nested SELECT.
@Entity
@NamedQueries({
    @NamedQuery(name = "removeCities", query = "DELETE FROM CityRecord r WHERE r.prov in (SELECT p FROM ProvinceRecord p WHERE p.country= ?1)")
}

Hibernate does not like duplicated named query

@Entity
@NamedQueries({
    @NamedQuery(name = "removeCities", query = "DELETE FROM CityRecord r WHERE r.prov in (SELECT p FROM ProvinceRecord p WHERE p.country= ?1)"),
    @NamedQuery(name = "removeCities", query = "DELETE FROM CityRecord r WHERE r.prov in (SELECT p FROM ProvinceRecord p WHERE p.country= 
}

EclipseLink accepts it.  
Different default column naming for class references
Problem
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = { "name", "company" }))
public class PersonRecord implements Serializable {


    private String name;

    @JoinColumn(nullable = false)
    private CompanyRecord company;
,,,,
}

It works for Hibernate but failed for EclipseLink. This is because Hibernate generates company as column name for company reference class but EclipseLink generates company_id column name so the unique constraint fails. After replacing company with company_id in the UniqueConstraint annotation the result is opposite.
Solution
Unfortunately, Hibernate ignores name parameter in JoinColumn annotation. To redefine column name it is necessary to add Column annotation. But EclipseLink does not accept both JoinColumn and Column annotation for the same field. So the only solution for both frameworks is to add name parameter to JoinColumn annotation which is ignored by Hibernate but works for EclipseLink
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = { "name", "company" }))
public class PersonRecord implements Serializable {

    private String name;

    @JoinColumn(nullable = false,name="company")
    private CompanyRecord company;

Different resource filepath
Problem
Assume we want to deploy a simple .war to web container containing  resource/param.txt and want to read this file.
That's pretty simple just:

public class Listener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("Start");
        URL u = Listener.class.getClassLoader().getResource("resource/param.txt");
        System.out.println("U=" + u.getFile());
...
But sometimes is was convenient for me to read this file using :

InputStream i = u.openStream 
and sometimes to read this resource like regular file in the file system using getFile method.
There is no problem on all webcontainers (even Google App Engine infrastructure). For instance, on Tomcat getFile() method returns:

U=/home/hotel/.netbeans/8.0/apache-tomcat-8.0.3.0_base/webapps/ReadRes/WEB-INF/classes/resource/param.txt
But on WildFly it returns something like:
U=/content/ReadRes.war/WEB-INF/classes/resource/param.txt
It does not reflect the place where resource is unpacked, it should be something like:
.../{standalone}/tmp/vfs/temp/temp577a2348203a1442/content-34f69a82c7d2649f/WEB-INF/classes/resource/param.txt
Solution
Of course, it is necessary to read resource file using OpenStream (or any other method) and read file in the file system using file path. 
But I cannot use it for Jython code deployed as a resource which needs file path and there is no simple way to overcome it. The only solution is to deploy Jython code outside .war file and use directory file path.

niedziela, 4 maja 2014

Enhancement to MVP GWT/Jython framework

Introduction
I added several enhancement to my MVP framework. Source code is available here, the demo version (deployed to Google App Engine) is available here. I'm planning to use this enhancements in the JavaHotel application. The description is available here.
Disclosure panel
It is an utilization of GWT DisclosurePanel. Demo version: link -> Different lists -> Dialog with disclosure. I'm planning to use it for more advanced room booking options (group booking). Because in most cases a single room is booked it is not necessary to expose group booking options all the time. The implementation also raises Jython action events before opening and closing the disclosure panel.
Autohide dialog
It is a GWT DialogBox widget with 'autoHide' constructor parameter set to true. Demo version: link -> Autohide dialog. I'm using this feature while exposing the link for PDF downloading. This link is enclosed in autohide dialog just making removing this small dialog more convenient.
Modeless dialog
It is a GWT DialogBox with 'modal' constructor parameter set to false. link -> Modeless dialog ->Modeless. After displaying the last dialog it is still possible to enter values into underlying dialog.I'm planning to use it in the JavaHotel application for more sophisticated searching.
Unfortunately, working with modeless dialog causes some complications and questions. For instance : whether modeless dialog should be removed while underlying dialog is closed ? It requires more elaboration.
Programmatic switching active line in lists
As a default the active line in the list immediately after creation is not specfied. The user should manually navigate to the other lines if necessary. I added the programmatic way of changing the active line - for instance - to implement more advanced searching. link -> Different lists -> List with custom search. I'm planning to use this option for more advanced booking.
Programmatic change active line in dateline panel
This problem is similar to above but is related to dateline widget. link -> Panel date -> To date.
URL query string
Allows passing parameters from query string to Jython backing code. More details.
Strange thing while deploying JSPWiki for Clouds application
I spent some time trying to resolve log message:
Unsupported major.minor version 51.0
The meaning of this  message is obvious: one is trying to execute java code in JVM not supporting this Java version. For instance: byte code created with JDK 1.8 in JVM 1.7. I indeed launched Eclipse with JDK 1.8 but JSPWiki code was compiled having 1.7 compatibility level. This message was throws from JSP (not pure Java) code.
I discovered that this message is thrown after executing the application on local machine in Development mode before deployment to Google App Engine. Probably the JSP pages are compiled at that moment and later I was unable to find any way to force recompiling it in 1.7 compatibility mode. After relaunching the Eclipse with JDK 1.7, running the application locally and redeploying it to Google App Engine again the problem disappeared.