Blog do projektu Open Source JavaHotel

niedziela, 24 listopada 2013

How to quickly create and remove Oracle database

Introduction
Sometimes it is necessary for me to quickly create an Oracle database, load data, perform some test and analysis and clear when it is done. I realized that it is much better to create a new database (instance) instead of constantly fleshing out the existing one. By removing this instance I can also reclaim disk space.
Create a database 
I want to create a tempdb database.
1. Prepare initialization parameter file.
tempdb.__oracle_base='/home/oracle/app/oracle'#ORACLE_BASE set from environment
*.db_create_file_dest='tempdb'
*.db_create_online_log_dest_1='tempdb'
*.db_name='tempdb'
*.db_recovery_file_dest='tempdb'
*.db_recovery_file_dest_size=10G
*.diagnostic_dest='tempdb'
2. Put this file in proper place (/home/oracle/app/oracle/product/11.2.0/dbhome_1/dbs/inittempdb.ora in my environment)
vi /home/oracle/app/oracle/product/11.2.0/dbhome_1/dbs/inittempdb.ora 
3. Make directory for instance data
mkdir /home/oracle/app/oracle/product/11.2.0/dbhome_1/dbs/tempdb 
4. Set ORACLE_SID
export ORACLE_SID=tempdb
5. Start sqlplus as DBA
sqlplus / as sysdba 
6. Create database, test user and run system scripts.
create spfile from pfile; 
startup nomount 
create database tempdb; 
create user test identified by secret;
grant all provileges to test;
@?/rdbms/admin/catalog.sql
@?/rdbms/admin/catproc.sql
@?/sqlplus/admin/pupbld.sql
shut
7. Prepare settings for remote access (file /home/oracle/app/oracle/product/11.2.0/dbhome_1/network/admin/tnsnames.ora) Add entries like:
LISTENER_TEMPDB =
  (ADDRESS = (PROTOCOL = TCP)(HOST = think)(PORT = 1521))

TEMPDB =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = sb-ThinkPad-T42)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = tempdb)
    )
  )
8. Add entry to /etc/oratab
tempdb:/home/oracle/app/oracle/product/11.2.0/dbhome_1:Y
9. Start database
dbstart
10. Prepare entry for sqldeveloper
11. Connect and act

Remove database
It is pretty easy (look also).

export ORACLE_SID=mydb
sqlplus / as sysdba
startup force mount
alter system enable restricted session;
drop database;
Remove also entries from /home/oracle/app/oracle/product/11.2.0/dbhome_1/network/admin/tnsnames.ora Reclaim disk space
rm -rf /home/oracle/app/oracle/product/11.2.0/dbhome_1/dbs/tempdb

piątek, 15 listopada 2013

Clojure, tautology and logical expression

Introduction
I decided to create a simple Clojure project for verifying logical tautologies. The source code is available here.

For instance:

 ((A \land B) \to C) \Leftrightarrow (A \to (B \to C)).
The algorithm is simple:
  1. Parse the logical expression
  2. Generate all possible combination of values for literals in the expression
  3. Evaluate the results for any value
  4. If every result is true then we have a tautology
It is not an optimal method for proving the tautology because the complexity is exponential but if we have a small number of literals it can run pretty fast.

Definition
Grammar for logical expression to be analyzed:

<expression> ::=
    [~] < expression >
         | <literal>
         | ( < expression > <operator> <expression>)

< literal > ::= chain of letter and digits starting from letter
< operator > ::= 
    & : logical AND
    ^ : logical OR
    == : logical equivalence
    => : logical implication
    ~ : logical NOT

Remarks:
  • I assume that every subexpression (except literals) should be enclosed in parenthesis. For instance: (A & B) but not (A & B ^ C). This I'm avoiding a problem related to operator precedence. Because any logical expression can be "normalized" that way it is not a limitation which matters.
  • The tautology quoted above can be put down that way: (( A & B) => C) == (A => (B => C))
  • One of De Morgan's Laws: ~(A & B) == (~A ^ ~B)
Parser
The first thing to do is the "tokenizer" - creates a flow of separated components. It can be achieved simply by means of regular expression.

  [seqw (re-seq #"[A-Za-z][\w]*|[\^\(\)&~]|=>|==" str)]

Source code for parser is available here.
 
defn parse
"parse string expression
 returns three element vector (Polish notation: (operator, left operand, right operand)
 left/right operand can be an atomic operand (variable) or 
 subexpression (another triada vector)"
[str]

Parser decomposes input string to "Polish notation". Every part of the expression is transformed to a triad [operator, left operand, right operand]. In the case of negation (one parameter operator) the right operand is nil.

Evaluation

The evaluation (counting value) is very simple while the expression is decomposed.

(defn evaluate 
    [expr val]

"evaluate (calculate value) of the parsed expression using current values (as hash-map)
 returns true or false
"
    (defn getval [subexpr]
    (let 
      [operator (first subexpr)
       first (fnext subexpr)
       second (nth subexpr 2)
       firstval (if (vector? first) (getval first) (get val first))
       secondval (if (vector? second) (getval second) (get val second))
      ] 
     (cond 
        (= operator nil) firstval
        (= operator "&") (and firstval secondval)
        (= operator "^") (or firstval secondval)
        (= operator "==") (= firstval secondval)
        (= operator "=>") (if (and firstval (not secondval)) false true)
        (= operator "~") (not firstval)
        :else nil
     )
     )
     )
     (getval expr)
)
Just scan the parsed expression and calculate. "Values" is the map: { "literal" : true/false }.
Verify the tautology
 By "verifying the tautology" I mean calculating the value for every combination of literals and making sure that it evaluates as "true" all the time. First thing is to extract all literals from the parsed string. It is received by a simple statement (assuming that 'e' is parsed expression)
distinct (filter #(and (not (nil? %)) (Character/isLetter  (first %))) (flatten e))
The number of all literal combination is 2 power {number of literals}. So generating all combinations can be achieved by iterating all integers from 0 to 2 power {number of literal} and extracting literal values from binary representation of the current integer (1 as true and 0 as false.
(defn genvalues [lvals num ]
     (defn getsinglevalue [k]
        [ (nth lvals k) (odd? (bit-shift-right num k)) ]
     )
     (map getsinglevalue (range 0 (count lvals)))
  )

The last but not least is the main "verify" function. The function returns a vector of values producing false. So the expression is tautology if the output list is empty.
(defn varifytautology

"verify if logical expression is tautology
 returns the collection of values giving false result
 if list is empty then expression is tautology
"

  [str]
  (let [ vals (calculateall str)]
;    (seq (for [x vals] (println x)))
    (reduce #( if (not (second %2 )) (conj %1 %2) %1) [] vals)
  )
)

Unit tests
The series of unit tests is provided with the source. Example
; DeMorgan's Low
(deftest parse-taut2
  (let [sent "(~(p & q) == (~p ^ ~q))"
        lfalse (logical.expression/varifytautology sent)
       ]
;   (println lfalse)   
    (is (empty? lfalse))
  )
)

; DeMorgan's Low
(deftest parse-taut3
  (let [sent "(~(p ^ q) == (~p & ~q))"
        lfalse (logical.expression/varifytautology sent)
       ]
;   (println lfalse)   
    (is (empty? lfalse))
  )
)

(deftest parse-taut4
  (let [sent "((a&b)=>c) == (a=>(b=>c))))"
        lfalse (logical.expression/varifytautology sent)
       ]
;   (println lfalse)   
    (is (empty? lfalse))
  )
)

; the Distribution Law
(deftest parse-taut5
  (let [sent "(p^(q&r)) == ((p^q)&(p^r))"
        lfalse (logical.expression/varifytautology sent)
       ]
;   (println lfalse)   
    (is (empty? lfalse))
  )
)
I'm happy to announce the De Morgan's Laws still holds true.

środa, 30 października 2013

Java properties and UTF-8

Introduction
There is an evergreen problem how to read Java properties from UTF-8 files. The source file should be ISO 8859-1 oriented and even overmighty Hercules cannot contend that.
Solution
But I found simply method which works for me. Just read a file in standard way, change all characters above 128 to Unicode escaped sequence and push through Properties.load method. Source file is available here.

public class ReadUTF8Properties { 

        private static String getFileContent(String name) throws IOException {
// does not work in Google App Engine, use Guava goodies            
//              return new String(Files.readAllBytes(Paths.get(name)));
            return Files.toString(new File(name),  Charsets.UTF_8);
        }

        private static String toLatin1(String s) {
                StringBuilder b = new StringBuilder();

                for (char c : s.toCharArray()) {
                        if (c >= 256 && c < 1000)
                                // 3 digits, add one leading 0
                                b.append("\\u0").append(Integer.toHexString(c));
                        else if (c >= 1000)
                                // 4 digits
                                b.append("\\u").append(Integer.toHexString(c));
                        else
                                b.append(c);
                }
                return b.toString();
        }

        public static Properties readProperties(String propName) throws IOException {

                Properties prop = new Properties();
                String p = toLatin1(getFileContent(propName));
                prop.load(new StringReader(p));
                return prop;
        }

}

wtorek, 15 października 2013

JavaHotel, first version

First version
I uploaded the first version of JavaHotel to Google App Engine. Test version is available: Java hotel for user (U/P user/user) and Java hotel for administrator (U/P admin/admin). Source files are available here. It is Google App Engine version but I'm planning to use it only for demo purpose. Also regular JEE version is created as a primary target. It will be available for Tomcat and Glassfish. Tomcat version is a single web application, Glassfish version is divided into persistence layer available as Session Stateless Bean and business logic layer. It was tested with Derby, Postgress and DB2.
It is the first version and everything is rough. But basic functionality is covered.
  • Prepare database: hotel, users, room, services, pricelist
  • Reservation panel
  • Booking
  • Check-in
  • Registering additional services during the stay
  • Billing
Reservation panel

Empty places denote free rooms, green booked and yellow with guests currently checked-in. Clicking at the empty place allows to make reservation starting from this day forward, clicking at the green allows to make check-in if customer just arrived and clicking at the yellow gives access to options related to stay (for instance: billing)
Reservation

It is the reservation dialog available after clicking at the empty room. User can modify number of days (default is one), calculate rating and enter the customer data. It is also possible to select the customer from the list of customers already registered.
Check-in
Check-in dialog pops up after clicking at the green (reservation) place. It allows transforming reservation to stay and starting billing the guest. In the check-in dialog it is also possible to register additional guests if the booking was done for more then one person.
Registering additional services

After stay has been started it is possible to append additional services to the stay. User can select a standard service prepared before or add a new services ad-hoc.
Billing
During and after the stay a bill can be issued. It is possible to issue more then one bill for one stay and every bill having a different payer. In the tab panel 'Not paid yet' page contains services not paid yet (without the bill issued for them) and 'All billable' contains the list of all services. The payment can be specified as 'Pay now' or 'Not paid now'. There is also possibility to register payment later for bills not paid now.
Conclusion
It is the first version so everything is basic and rough. But the most important functionality is implemented already. Next steps:

  • Booking of more then one room,  searching. For instance: implement scenario like: "Find the first available reservation for 5 persons: two double rooms and one single".
  • Issuing and printing the invoice.
  • Synchronization with calendar. Alerts when reservation or stay expires. 

niedziela, 22 września 2013

MVP framework, new features

Introduction
I added several enhancements to the MVP Jython framework. Source code is available here, sample application developed for Google App Engine can be launched using this web URL.
New types of main menu
So far only left side list of button has been available. Two new types of main menus have been added: "Up" menu and stack menu. "Up" menu is nothing more then image at the top status bar which expands after clicking giving a list of choices. Stack menu is an implementation of GWT StackPanel. "Up" menu can coexist with tab menu and stack menu but stack menu and tab menu excludes each otther. An example of stack menu is available here. More technical description.
"Up" menu

Stack menu














Image column
Image column contains icon or list of icons instead of text or number. Image list can be static or dynamic. In the second case a JavaScript code is executed to determine the list of images displayed depending on the row content. More detailed description. Icons plays also a role of buttons. After user clicks the icon a Jython server side action is raised. This way it is possible to implement an action depending on the row content and which icon has been clicked.

A running example is available here. Click "up" menu -> "List with icon". The up arrow is displayed for positive number and down arror for negative number. Left and right arrow is displayed depending if number is odd or even.
Different type for column and footer
As a default the footer type (and other properties like align and decimal position) are copied from column type. But it is possible to change this behavior.


A running example is available here. "Status" -> "List footer". The footer is under string column but the footer content is a number. Also align schema is different. More detailed description is available here.
Other changes
  • Dynamically modify the content of the status bar. The the status bar content can be customized depending on the context. More detailed description is available here. Running example can be launched. "Status" - > "Status text".
  • Load tab panel page on demand. An action is raised after clicking the page header. This way the tab panel page can be modified dynamically. More detailed description.
  • XML usage. XML format is often connected with love-hate relationship. But it is very convenient if data schema is not stable or can be changed. It is also very convenient if we want to pack a lot information in one entry without creating a table with tens or hundreds of columns. So I added some automation for packing and unpacking forms using XML file. More information is available here.  A running example (although nothing spectacular is visible) can be launched here. Click on "Dialog base xml".


środa, 28 sierpnia 2013

Clojure and polynomials

Introduction
I started reading through Clojure tutorial and found very interesting implementation of calculating polynomial and its first derivative. So I decided to stretch my math muscle and perform more polynomial arithmetic in Clojure basing on this article.
I'm assuming all the time that polynomial in Clojure is a list of coefficients from left to right (like in the written form). So list [2 8 9]  means 2x² + 8x + 9, [7 0 1 7] means: 7x³ + x + 7
Addition
Almost nothing more than (map + ...) function. The only problem is to expand shorter list (lower degree polynomial). Also improvement should be added to reduce the leading zeros from the result. Example:
(2x² + 8x + 9) + (-2x² + 2x + 4) = 0x² + 10x + 13

(defn addpol
  [pol1 pol2]
  ; degree of the sum polynomial 
  (let [nsize (max (count pol1) (count pol2) ) ]
  ; expand polynomial to the new degree by adding 0 coefficient at the frot
    (defn expandpol 
        [pol] 
            (concat ( repeat (max 0 (- nsize (count pol))) 0 ) pol )
    )
  )
  ; having both polynomials at the same degree just sum them
  (map + (expandpol pol1) (expandpol pol2))
)
Subtraction
Just use the previous function and negate the subtrahend.
(defn subtractpol
  [minuend subtrahend]
  ; negate subtrahend and run summing
  (addpol minuend ( map #(- 0 %) subtrahend))
)
Product
Multiply every coefficient of the first polynomial by every coefficient of the second polynomial. Recursion is used to iterate but this algorithm is a good candidate for loop->recur for better performance.

(defn mulpol
  [pol1 pol2]
  ( let
    ; level of the product
      [newlevel (- (+ (count pol1) (count pol2)) 1)]  
    ; multiply polynomial by digit and add zeros at the frond and end keeping it at the degree necessary
    (defn muldigit 
       [beg coeff end]
       (concat (repeat beg 0) (map #(* % coeff) pol2) (repeat end 0))
    )
    ; recursive as replacement for iteration
    (defn muladd
       ; 'beg' number of left zeros
       ; 'currentsum' list of coefficient multiplications peformed so far
       ; 'restpol1' list of coefficients not used yet
       ; 'end' number of right zeros
       [beg currentsum restpol1 end]
       (condp = restpol1
       ; end of recursion
       [] currentsum
       ; multiply and move to the next digit
       (
         map
            +
            currentsum
            (muladd 
               (+ beg 1)
               (muldigit beg (first restpol1) end)
               (rest restpol1)
               (- end 1)
            )
       )
       )
     )
  ; start of the recursion
  ( muladd 0 (repeat newlevel 0) pol1 (- newlevel (count pol2)))
  )
)
Division
Is more complicated because two results are expected: quotient and remainder. So to bring the result a map is used. Polynomial long division is implemented as easier. This function should be expanded to exclude division by zero (empty divisor) and reduce leading zeros from divisor. Example: (0 4 5)

; returns a map with two keys :quotient and :remainder
(defn divpol 
   [divident divisor]
   ; perform (lead(r)/lead(divisor)) * divisor
   (defn multerm
     [r resdiv]
       (concat (map #(* resdiv %) divisor) (repeat (- (count r) (count divisor)) 0))
   )
   ; recursive
   (defn div 
     [mapd]
   ( let [ q (get mapd :quotient)
           r (get mapd :remainder) 
           dv (/ (first r) (first divisor)) 
         ]
   ; end of recursion, pass down the result
   (if (or (< (count r) (count divisor)) (empty? r))
       mapd
   ; pull down the next digit from divident
       (div ( hash-map 
              :quotient (conj q dv)
              :remainder (rest (map - r (multerm r dv)))
            )
       )
   )
   )
   )   
   ; beginning of the recursion
   (div ( hash-map :quotient [], :remainder divident))
)   

niedziela, 18 sierpnia 2013

MVP Jython framework, html panel and javascript code

Introduction
The dialog is displayed using default layout. The default layout is convenient at the beginning but later probably something more useful and better looking is necessary. So it is possible to replace default layout by means of GWT HTMLPanel widget. Just add to the dialog definition file containing dialog layout in shape of html page and new display is visible. Also custom Javascript code can be added to the page.
TabPanel is very useful way of displaying a lot of data on the screen without scrolls. TabPanel can be built by combining html, css and javascript code but GWT contains TabPanel widget.
HTMLPanel and JavaScript custom code

Sample application is available here (click at "Dialog HTML panel"). The html, css and JavaScript code was downloaded from this page. It does not make a lot of sense here, it is only a presentation how default layout can be enriched with html, css and custom Javascript code. More detailed description is available here.
Adding HTMLPanel definition impacts only presentation part. It does not involve any change in backing Jython code.
TabPanel

Sample application is available here (click at "Dialog tab panel"). The usage of the TabPanel is limited currently. For instance it is not possible to disperse buttons between different tab pages. More detailed description is available here.  
Problems
  • Current implementation of HTMLPanel does not allow to use custom (internationalized) labels. It will be added later.
  • Current implementation of TabPanel is limited. More flexible approach will be added later.