středa 3. dubna 2013

Glassfish JMS broker perzistující zprávy do Sybase ASE

Co budeme potřebovat:
  1. Oracle Glassfish 3.1.2.2 Dále je předpokládáno umístění v ~/glassfish3122
  2. Sybase Adaptive Server Enterprise 15.5
  3. JMS broker (integrovaný v Glassfishi)
  4. JDBC konektor pro Sybase ASE - jConnect 7.0.7
Postup:
1) Ve webové administraci serveru Glassfish otevřeme Configurations->server-config->Java Message Service. JMS Service Type nastavíme na hodnotu LOCAL. Poté v Additional Properties postupně nastavíme následující:
 <property name="imq.persist.store" value="jdbc"></property>  
     <property name="imq.persist.jdbc.sybase.driver" value="com.sybase.jdbc4.jdbc.SybDriver"></property>  
     <property name="imq.persist.jdbc.sybase.user" value="db_user"></property>  
     <property name="imq.persist.jdbc.sybase.password" value="db_user_passwd"></property>  
     <property name="imq.persist.jdbc.dbVendor" value="sybase"></property>  
     <property name="imq.brokerid" value="testBroker"></property>  
     <property name="imq.autocreate.reaptime" value="1"></property>  
     <property name="imq.instanceconfig.version" value="300"></property>  
     <property name="imq.jmsra.managed" value="true"></property>  
     <property name="imq.persist.jdbc.sybase.closedburl" value="jdbc:sybase:Tds:IPAddress:port/database"></property>  
     <property name="imq.persist.jdbc.sybase.opendburl" value="jdbc:sybase:Tds:IPAddress:port/database"></property>  
 Toto je samozřejmě možné zadat i v souboru config/domain.xml příslušné domény.
Pokud se rozhodnete namísto com.sybase.jdbc4.jdbc.SybDriver použít com.sybase.jdbc4.jdbc.SybDataSource nejspíš vás v logu bude čekat tato chyba:
 ERROR [B3000]: Could not open persistent message store:  
 com.sun.messaging.jmq.jmsserver.util.BrokerException: [B3026]: Failed to get connection to jdbc:sybase:Tds:IPAddress:2030/database  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.CommDBManager.newConnection(CommDBManager.java:796)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.CommDBManager.getNewConnection(CommDBManager.java:757)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.DBConnectionPool.createConnection(DBConnectionPool.java:483)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.DBConnectionPool.<init>(DBConnectionPool.java:352)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.DBConnectionPool.<init>(DBConnectionPool.java:233)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.DBManager.getDBManager(DBManager.java:184)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.JDBCStore.<init>(JDBCStore.java:112)  
      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
      at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)  
      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)  
      at java.lang.reflect.Constructor.newInstance(Constructor.java:525)  
      at java.lang.Class.newInstance0(Class.java:372)  
      at java.lang.Class.newInstance(Class.java:325)  
      at com.sun.messaging.jmq.jmsserver.persist.StoreManager.getStore(StoreManager.java:169)  
      at com.sun.messaging.jmq.jmsserver.Globals.getStore(Globals.java:995)  
      at com.sun.messaging.jmq.jmsserver.Broker._start(Broker.java:955)  
      at com.sun.messaging.jmq.jmsserver.Broker.start(Broker.java:456)  
      at com.sun.messaging.jmq.jmsserver.Broker.main(Broker.java:2151)  
 Caused by: java.sql.SQLException: JZ0PN: Specified port number of -1 was out of range. Port numbers must meet the following conditions: 0 <= portNumber <= 65535  
      at com.sybase.jdbc4.jdbc.SybConnection.getAllExceptions(SybConnection.java:2780)  
      at com.sybase.jdbc4.jdbc.SybConnection.handleSQLE(SybConnection.java:2648)  
      at com.sybase.jdbc4.jdbc.SybConnection.tryLogin(SybConnection.java:479)  
      at com.sybase.jdbc4.jdbc.SybConnection.handleHAFailover(SybConnection.java:3109)  
      at com.sybase.jdbc4.jdbc.SybConnection.<init>(SybConnection.java:328)  
      at com.sybase.jdbc4.jdbc.SybDriver.createConnection(SybDriver.java:681)  
      at com.sybase.jdbc4.jdbc.SybDriver.connect(SybDriver.java:620)  
      at com.sybase.jdbc4.jdbc.SybDriver.connect(SybDriver.java:653)  
      at com.sybase.jdbc4.jdbc.SybDataSource.getConnection(SybDataSource.java:320)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.CommDBManager.newConnection(CommDBManager.java:782)  
      ... 17 more|#]  
Pokud se budete snažit použít některý ze starších ovladačů od Sybase, například verze 6.0, bude v logu chyba následující:
 Local broker:ERROR [B3000]: Could not open persistent message store:  
 com.sun.messaging.jmq.jmsserver.util.BrokerException: Failed to open persistent store.  
      at com.sun.messaging.jmq.jmsserver.persist.StoreManager.getStore(StoreManager.java:219)  
      at com.sun.messaging.jmq.jmsserver.Globals.getStore(Globals.java:995)  
      at com.sun.messaging.jmq.jmsserver.Broker._start(Broker.java:955)  
      at com.sun.messaging.jmq.jmsserver.Broker.start(Broker.java:456)  
      at com.sun.messaging.jmq.jmsserver.Broker.main(Broker.java:2151)  
 Caused by: com.sybase.jdbc3.utils.UnimplementedOperationException: The method com.sybase.jdbc3.jdbc.SybDatabaseMetaData.getSQLStateType() has not been completed and should not be called.  
      at com.sybase.jdbc3.jdbc.ErrorMessage.raiseRuntimeException(ErrorMessage.java:950)  
      at com.sybase.jdbc3.utils.Debug.notImplemented(Debug.java:387)  
      at com.sybase.jdbc3.jdbc.SybDatabaseMetaData.getSQLStateType(SybDatabaseMetaData.java:2261)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.CommDBManager.initDBMetaData(CommDBManager.java:177)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.DBManager.getDBManager(DBManager.java:185)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.JDBCStore.<init>(JDBCStore.java:112)  
      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
      at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)  
      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)  
      at java.lang.reflect.Constructor.newInstance(Constructor.java:525)  
      at java.lang.Class.newInstance0(Class.java:372)  
      at java.lang.Class.newInstance(Class.java:325)  
      at com.sun.messaging.jmq.jmsserver.persist.StoreManager.getStore(StoreManager.java:169)  
      ... 4 more|#]  

2) Abychom mohli používat JDBC ovladač pro Sybase ASE uvedený v kroku 1, musíme o něm dát JMS brokerovi vědět. To uděláme v souboru ~/glassfish3122/mq/etc/imqenv.conf přidáním řádku s cestou k ovladači: IMQ_DEFAULT_EXT_JARS=~/glassfish3122/glassfish/lib/jconn4-7.0.7.jar V uvedeném umístění samozřejmě musí tento soubor existovat.
Pokud cesta k ovladači nebude uvedena správně, objeví se v logu chyba:
 Local broker: ERROR [B3000]: Could not open persistent message store:  
 com.sun.messaging.jmq.jmsserver.util.BrokerException: [B3024]: Failed to load JDBC driver: com.sybase.jdbc4.jdbc.SybDriver  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.CommDBManager.initDBDriver(CommDBManager.java:495)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.DBManager.<init>(DBManager.java:259)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.DBManager.getDBManager(DBManager.java:182)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.JDBCStore.<init>(JDBCStore.java:112)  
      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
      at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)  
      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)  
      at java.lang.reflect.Constructor.newInstance(Constructor.java:525)  
      at java.lang.Class.newInstance0(Class.java:372)  
      at java.lang.Class.newInstance(Class.java:325)  
      at com.sun.messaging.jmq.jmsserver.persist.StoreManager.getStore(StoreManager.java:169)  
      at com.sun.messaging.jmq.jmsserver.Globals.getStore(Globals.java:995)  
      at com.sun.messaging.jmq.jmsserver.Broker._start(Broker.java:955)  
      at com.sun.messaging.jmq.jmsserver.Broker.start(Broker.java:456)  
      at com.sun.messaging.jmq.jmsserver.Broker.main(Broker.java:2151)  
 Caused by: java.lang.ClassNotFoundException: com.sybase.jdbc3.jdbc.SybDriver  
      at java.net.URLClassLoader$1.run(URLClassLoader.java:366)  
      at java.net.URLClassLoader$1.run(URLClassLoader.java:355)  
      at java.security.AccessController.doPrivileged(Native Method)  
      at java.net.URLClassLoader.findClass(URLClassLoader.java:354)  
      at java.lang.ClassLoader.loadClass(ClassLoader.java:423)  
      at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)  
      at java.lang.ClassLoader.loadClass(ClassLoader.java:356)  
      at java.lang.Class.forName0(Native Method)  
      at java.lang.Class.forName(Class.java:186)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.CommDBManager.initDBDriver(CommDBManager.java:467)  
      ... 14 more|#]  

3) Jelikož JMS broker nepodporuje Sybase ASE je třeba pro Sybase ASE vytvořit databázové schema. Na konec souboru ~/glassfish3122/glassfish/domains/domain1/imq/instances/imqbroker/props/config.properties je třeba přidat následující položky:
 # Beginning of properties to plug in a Sybase ASE database  
 #  
 # User name used to open database connection. Replace username.  
 #imq.persist.jdbc.sybase.user=<username>  
 # Optional property to specify whether the database requires a password.  
 #imq.persist.jdbc.sybase.needpassword=[true|false]  
 # Vendor specific JDBC driver.  
 imq.persist.jdbc.sybase.driver=com.sybase.jdbc3.jdbc.SybConnectionPoolDataSource  
 # Vendor specific properties.  
 # Vendor specific database url to get a database connection.  
 # Replace hostname, port and sid in imq.persist.jdbc.oracle.property.url.  
 # imq.persist.jdbc.oracle.property.url=jdbc:oracle:thin:@<hostname>:<port>:<sid>  
 imq.persist.jdbc.sybase.property.implicitCachingEnabled=true  
 imq.persist.jdbc.sybase.property.maxStatements=25  
 # Properties to define the tables used by the broker. Do not modify the schema.  
 # Version table  
 imq.persist.jdbc.sybase.table.MQVER41=\  
      CREATE TABLE ${name} (\  
        STORE_VERSION INTEGER NOT NULL,\  
        LOCK_ID VARCHAR(100),\  
        PRIMARY KEY(STORE_VERSION))  
 # Configuration change record table  
 imq.persist.jdbc.sybase.table.MQCREC41=\  
      CREATE TABLE ${name} (\  
        RECORD IMAGE NOT NULL,\  
        CREATED_TS NUMERIC NOT NULL,\  
        PRIMARY KEY(CREATED_TS))  
 # Broker table  
 imq.persist.jdbc.sybase.table.MQBKR41=\  
      CREATE TABLE ${name} (\  
        ID VARCHAR(100) NOT NULL,\  
        URL VARCHAR(100) NOT NULL,\  
        VERSION INTEGER NOT NULL,\  
        STATE INTEGER NOT NULL,\  
        TAKEOVER_BROKER VARCHAR(100),\  
        HEARTBEAT_TS NUMERIC,\  
        PRIMARY KEY(ID))  
 # Store session table  
 imq.persist.jdbc.sybase.table.MQSES41=\  
      CREATE TABLE ${name} (\  
        ID BIGINT NOT NULL,\  
        BROKER_ID VARCHAR(100) NOT NULL,\  
        IS_CURRENT INTEGER NOT NULL,\  
        CREATED_BY VARCHAR(100) NOT NULL,\  
        CREATED_TS BIGINT NOT NULL,\  
        PRIMARY KEY(ID))  
 imq.persist.jdbc.sybase.table.MQSES41.index.IDX1=\  
      CREATE INDEX ${index} ON ${name} (\  
        BROKER_ID)  
 imq.persist.jdbc.sybase.table.MQSES41.index.IDX2=\  
      CREATE INDEX ${index} ON ${name} (\  
        BROKER_ID, IS_CURRENT)  
 # Destination table  
 imq.persist.jdbc.sybase.table.MQDST41=\  
      CREATE TABLE ${name} (\  
        ID VARCHAR(100) NOT NULL,\  
        DESTINATION IMAGE NOT NULL,\  
        IS_LOCAL INTEGER NOT NULL,\  
        CONNECTION_ID NUMERIC NULL,\  
        CONNECTED_TS NUMERIC,\  
        STORE_SESSION_ID NUMERIC,\  
        CREATED_TS NUMERIC NOT NULL,\  
        PRIMARY KEY(ID))  
 imq.persist.jdbc.sybase.table.MQDST41.index.IDX1=\  
      CREATE INDEX ${index} ON ${name} (\  
        STORE_SESSION_ID)  
 # Interest (consumer) table  
 imq.persist.jdbc.sybase.table.MQCON41=\  
      CREATE TABLE ${name} (\  
        ID NUMERIC NOT NULL,\  
        CLIENT_ID VARCHAR(1024),\  
        DURABLE_NAME VARCHAR(1024),\  
        CONSUMER IMAGE NOT NULL,\  
        CREATED_TS NUMERIC NOT NULL,\  
        PRIMARY KEY(ID))  
 # Interest list (consumer state) table  
 imq.persist.jdbc.sybase.table.MQCONSTATE41=\  
      CREATE TABLE ${name} (\  
        MESSAGE_ID VARCHAR(100) NOT NULL,\  
        CONSUMER_ID NUMERIC NOT NULL,\  
        STATE INTEGER,\  
        TRANSACTION_ID NUMERIC,\  
        CREATED_TS NUMERIC NOT NULL,\  
        PRIMARY KEY(MESSAGE_ID, CONSUMER_ID))  
 imq.persist.jdbc.sybase.table.MQCONSTATE41.index.IDX1=\  
      CREATE INDEX ${index} ON ${name} (\  
        TRANSACTION_ID)  
 imq.persist.jdbc.sybase.table.MQCONSTATE41.index.IDX2=\  
      CREATE INDEX ${index} ON ${name} (\  
        MESSAGE_ID)  
 # Message table  
 imq.persist.jdbc.sybase.table.MQMSG41=\  
      CREATE TABLE ${name} (\  
        ID VARCHAR(100) NOT NULL,\  
        MESSAGE IMAGE NOT NULL,\  
        MESSAGE_SIZE NUMERIC,\  
        STORE_SESSION_ID NUMERIC NOT NULL,\  
        DESTINATION_ID VARCHAR(100),\  
        TRANSACTION_ID NUMERIC,\  
        CREATED_TS NUMERIC NOT NULL,\  
        PRIMARY KEY(ID))  
 imq.persist.jdbc.sybase.table.MQMSG41.index.IDX1=\  
      CREATE INDEX ${index} ON ${name} (\  
        STORE_SESSION_ID, DESTINATION_ID)  
 # Property table  
 imq.persist.jdbc.sybase.table.MQPROP41=\  
      CREATE TABLE ${name} (\  
        PROPNAME VARCHAR(100) NOT NULL,\  
        PROPVALUE IMAGE,\  
        PRIMARY KEY(PROPNAME))  
 # Transaction table  
 imq.persist.jdbc.sybase.table.MQTXN41=\  
      CREATE TABLE ${name} (\  
        ID NUMERIC NOT NULL,\  
        TYPE INTEGER NOT NULL,\  
        STATE INTEGER,\  
        AUTO_ROLLBACK INTEGER NOT NULL,\  
        XID VARCHAR(256),\  
        TXN_STATE IMAGE NOT NULL,\  
        TXN_HOME_BROKER IMAGE,\  
        TXN_BROKERS IMAGE,\  
        STORE_SESSION_ID NUMERIC NOT NULL,\  
        EXPIRED_TS NUMERIC NOT NULL,\  
        ACCESSED_TS NUMERIC NOT NULL,\  
        PRIMARY KEY(ID))  
 imq.persist.jdbc.sybase.table.MQTXN41.index.IDX1=\  
      CREATE INDEX ${index} ON ${name} (\  
        STORE_SESSION_ID)  
 # JMS Bridge TM LogRecord table  
 imq.persist.jdbc.sybase.table.MQTMLRJMSBG41=\  
      CREATE TABLE ${name} (\  
        XID VARCHAR(256) NOT NULL,\  
        LOG_RECORD IMAGE NOT NULL,\  
        NAME VARCHAR(100) NOT NULL,\  
        BROKER_ID VARCHAR(100) NOT NULL,\  
        CREATED_TS NUMERIC NOT NULL,\  
        UPDATED_TS NUMERIC NOT NULL,\  
        PRIMARY KEY(XID))  
 imq.persist.jdbc.sybase.table.MQTMLRJMSBG41.index.IDX1=\  
      CREATE INDEX ${index} ON ${name} (BROKER_ID)  
 imq.persist.jdbc.sybase.table.MQTMLRJMSBG41.index.IDX2=\  
      CREATE INDEX ${index} ON ${name} (NAME)  
 # JMS Bridges table  
 imq.persist.jdbc.sybase.table.MQJMSBG41=\  
      CREATE TABLE ${name} (\  
        NAME VARCHAR(100) NOT NULL,\  
        BROKER_ID VARCHAR(100) NOT NULL,\  
        CREATED_TS NUMERIC NOT NULL,\  
        UPDATED_TS NUMERIC NOT NULL,\  
        PRIMARY KEY(NAME))  
 # End of properties to plug in an Sybase ASE database  
Některé z víše uvedených položek je samozřejmě možné upravit podle vlastních potřeb nebo přidat další.
Kdybychom toto neudělali skončili bychom s touto chybou v logu:
 Local broker: ERROR [B3000]: Could not open persistent message store:  
 com.sun.messaging.jmq.jmsserver.util.BrokerException: Table definition not found for sybase  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.comm.CommDBManager.loadTableSchema(CommDBManager.java:1215)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.DBManager.getDBManager(DBManager.java:183)  
      at com.sun.messaging.jmq.jmsserver.persist.jdbc.JDBCStore.<init>(JDBCStore.java:112)  
      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
      at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)  
      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)  
      at java.lang.reflect.Constructor.newInstance(Constructor.java:525)  
      at java.lang.Class.newInstance0(Class.java:372)  
      at java.lang.Class.newInstance(Class.java:325)  
      at com.sun.messaging.jmq.jmsserver.persist.StoreManager.getStore(StoreManager.java:169)  
      at com.sun.messaging.jmq.jmsserver.Globals.getStore(Globals.java:995)  
      at com.sun.messaging.jmq.jmsserver.Broker._start(Broker.java:955)  
      at com.sun.messaging.jmq.jmsserver.Broker.start(Broker.java:456)  
      at com.sun.messaging.jmq.jmsserver.Broker.main(Broker.java:2151)|#]  
Více o konfiguraci JMS brokeru je uvedeno zde.
Nyní stačí zapnout Glassfish a zkontrolovat zda se v databázi vytvořili tabulky uvedené ve schematu. Poté je už možné využívat JMS.
V případě zájmu si můžete přečíst více o konfiguraci persistentního uložiště pro Oracle Glassfish Message Queue.
Nastaly problémy, které zde nejsou uvedeny? Podělte se o ně v diskuzi níže.

pátek 16. listopadu 2012

Několik tipů pro MyBatis 3

Ač je uživatelská příručka MyBatis 3 napsaná přehledně, lze se setkat s některými problémy či možnostmi, které jsou v ní uvedeny pouze okrajově nebo vůbec.
  1. Použil jsem připojení k databázi Sybase v které jsem volal proceduru. Na vstupu procedury byl parametr typu int. Mapování v xml vypadalo následovně:
    <select id="getPostOffice" statementType="CALLABLE" parameterType="biz.prodejna.pasport.PostRequest" resultType="java.lang.Integer">  
        {call search_post (#{psc})}  
    </select>
    psc bylo typu Integer. Mapoval jsem tedy java.lang.Integer z javy na int z databáze. Výsledkem byla následující chyba:
     Cause: java.sql.SQLException: JZ006: Caught IOException: java.io.IOException: JZ0SL: Unsupported SQL type 1111.  
     ; SQL []; JZ006: Caught IOException: java.io.IOException: JZ0SL: Unsupported SQL type 1111.; nested exception is java.sql.SQLException: JZ006: Caught IOException: java.io.IOException: JZ0SL: Unsupported SQL type 1111.  
    
    Řešením je explicitní uvedení databázového typu pomocí jdbcType:
     <select id="getPostOffice" statementType="CALLABLE" parameterType="biz.prodejna.pasport.PostRequest" resultType="java.lang.Integer">   
       {call search_post (#{psc,jdbcType=INTEGER})}   
     </select>  
    

    Pozor na přebytečné mezery. Pokud budou mezery např. kolem znaku "=" nebo za čárkou, vznikne chyba
     org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Improper inline parameter map format. Should be: #{propName,attr1=val1,attr2=val2}  
    

  2. Často se volá procedura/select, jejíž výsledek je třeba namapovat na třídu. Názvy členských proměnných třídy se však vždy neshodují s názvy sloupců jež jsou výstupem z databáze. K tomu účelu je v MyBatis anotace @Results. Zde je ukázka použití, kde místo předchozího mapování v xml je použita anotace v rozhranní mapperu:
     @Select("{call psc_detail_post (#{psc})}")  
       @Results(value = {  
         @Result(property = "psc"),  
         @Result(property = "premisesName", column = "nazev_provozovny"),  
         @Result(property = "municipality", column = "obec"),  
         @Result(property = "municipalityPart", column = "cast_obce"),  
         @Result(property = "street", column = "ulice"),  
         @Result(property = "descriptiveNumber", column = "cislo_popisne"),  
         @Result(property = "descriptiveCharacter", column = "cislo_popisne_znak"),  
         @Result(property = "landmarkNumber", column = "cislo_orientacni"),  
         @Result(property = "landmarkCharacter", column = "cislo_orientacni_znak"),  
         @Result(property = "email"),  
         @Result(property = "phone", column = "telefon")})  
       biz.prodejna.pasport.PostResponse getDetailPost(Integer psc);  
    

    Zde stojí za povšimnutí parametry psc a email. Jelikož se shodují názvy členských proměnných a výstupních sloupců, není třeba uvádět parametr column.

úterý 23. října 2012

Nastavení citlivosti myši v Ubuntu 12.04

Koupil jsem si novou myš s rozlišením1600 dpi. Po připojení k počítači byla ale příliš rychlá. Nastavení citlivosti v Unity nefunguje. Osvědčil se mi však následující postup:
  1. Spustit příkaz
    xinput --list --short
    Výpis bude podobný tomuto:
     ⎡ Virtual core pointer               id=2     [master pointer (3)]  
     ⎜  ↳ Virtual core XTEST pointer            id=4     [slave pointer (2)]  
     ⎜  ↳ ELECOM ELECOM LASER MOUSE             id=8     [slave pointer (2)]  
     ⎣ Virtual core keyboard               id=3     [master keyboard (2)]  
       ↳ Virtual core XTEST keyboard            id=5     [slave keyboard (3)]  
       ↳ Power Button                   id=6     [slave keyboard (3)]  
       ↳ Power Button                   id=7     [slave keyboard (3)]  
       ↳  USB Keyboard                  id=9     [slave keyboard (3)]  
       ↳  USB Keyboard                  id=10     [slave keyboard (3)]  
       ↳ USB 2.0 Camera                  id=11     [slave keyboard (3)]
    
    Povšimněme si názvu myši, zde "ELECOM ELECOM LASER MOUSE". 
  2. Spustíme příkaz pro zpomalení. Defaultně je hodnota 1. Čím vyšší číslo, tím větší zpomalení myši. Mě vyhovovala hodnota 4.
    xinput --set-prop "ELECOM ELECOM LASER MOUSE" "Device Accel Constant Deceleration" 4
  3. Vypnutí zrychlení myši. V mém případě byla 1 defaultní hodnota
    xinput --set-prop "ELECOM ELECOM LASER MOUSE" "Device Accel Velocity Scaling" 1
  4. Pokud uvedené řešení funguje, můžeme si nastavit spouštění skriptu při přihlášení. Stačí vytvořit soubor např.
     gedit ~/.xinitrc  
    
    s obsahem
     #!/usr/bin/env bash  
     xinput --set-prop "ELECOM ELECOM LASER MOUSE" "Device Accel Constant Deceleration" 4  
     xinput --set-prop "ELECOM ELECOM LASER MOUSE" "Device Accel Velocity Scaling" 1  
    
    Povolit jeho spouštění příkazem
    chmod +x ~/.xinitrc
    a přidat do "Aplikace po spuštění..."

čtvrtek 31. května 2012

RESTful webová služba 1. díl

  1. Nainstaluji si server glassfish. Ten je možné zdarma získat např. zde. Stačit vám bude ale i Tomcat nebo jiný servletový kontejner.
  2. Vytvořím si v Netbeans novou webovou aplikaci:
  3. V tomto okně je třeba dát Add a následně vybrat nainstalovaný servletový kontejner.
  4. Nyní je třeba nakonfigurovat aplikaci. To je možné dvěma způsoby:

    1.  Prostřednictvím souboru web.xml
    2.  <?xml version="1.0" encoding="UTF-8"?>  
       <web-app xmlns="http://java.sun.com/xml/ns/javaee"  
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
       http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">  
         <servlet>  
           <servlet-name>JerseyServlet</servlet-name>  
           <servlet-class>  
             com.sun.jersey.spi.container.servlet.ServletContainer  
           </servlet-class>  
           <load-on-startup>1</load-on-startup>  
         </servlet>  
         <servlet-mapping>  
           <servlet-name>JerseyServlet</servlet-name>  
           <url-pattern>/resources/*</url-pattern>  
         </servlet-mapping>  
       </web-app>  
      

    3. Pomocí anotace @ApplicationPath
    4. 1:  package biz.prodejna.examples.rest;  
      2:    
      3:  import javax.ws.rs.ApplicationPath;  
      4:  import javax.ws.rs.core.Application;  
      5:    
      6:  @ApplicationPath("resources")  
      7:  public class JaxRsConfig extends Application {  
      8:  }  
      

  5. A nyní konečně mohu napsat nějakou svoji třídu.
  6. RESTful webovou službu mohu volat jako bezparametrickou nebo s parametry. V druhém případě mám v podstatě 3 možnosti, jak parametry vkládat:
    • Jako součást URL. Příklad: http://localhost:8080/example-rest/resources/names/jmeno/prijmeni
    • Jako parametry metody GET. Příklad: http://localhost:8080/example-rest/resources/names?jmeno=Adam&prijmeni=Oliva
    • V těle HTTP requestu. Zde mohou být parametry naformátovány v libovolném MIME typu.

    V tomto bodě ukáži pouze první případ. Další případy si nechám do některého z dalších dílů.

    1:  package biz.prodejna.examples.rest;  
    2:    
    3:  import javax.ws.rs.GET;  
    4:  import javax.ws.rs.Path;  
    5:  import javax.ws.rs.PathParam;  
    6:  import javax.ws.rs.Produces;  
    7:    
    8:  @Path("names")  
    9:  public class Names {  
    10:    
    11:    @GET  
    12:    @Path("{first}/{last}")  
    13:    @Produces("text/plain")  
    14:    public String getFullName(  
    15:        @PathParam("first") String firstName, @PathParam("last") String lastName) {  
    16:      return firstName + " " + lastName;  
    17:    }  
    18:  }  
    

    Vysvětlím jednotlivé anotace:
    @Path u třídy udává, pod jakou URL bude služba dostupná.
    @GET definuje, jaká bude použita HTTP metoda.
    @Path u metody definuje, jaké bude pořadí parametrů. Nebo přesněji: jaký tvar bude mít zbytek URL.
    @Produces udává MIME typ v těle HTTP odpovědi.
    @PathParam mapuje parametry metody na části v anotaci @Path

  7. Nyní je už možné aplikaci deployovat na server a zkusit ji zavolat např. z webového prohlížeče: http://localhost:8080/example-rest/resources/names/Adam/Oliva

  8. Proč URL vypadá právě takhle? Vysvětlím:
    example-rest -název aplikace
    resources -definováno ve web.xml nebo anotací @ApplicationPath
    names -definováno anotací třídy @Path
Pokud máte zájem o pokračování tohoto blogu, informujte mě o tom. Co lze očekávat příště:
  • služba vracející odpověď jako xml
  • služba očekávající parametry jako parametry URL
  • tvorba klienta s využitím implementace Jersey.
  • služba očekávající na vstupu XML dokument.

pátek 30. března 2012

GlassFish V3 admin console se načítá příliš dlouho

Pokud máte nainstalovaný GlassFish V3 za proxy a chcete se přihlásit do administrátorské konzole, nejspíš musíte čekat na úvodní obrazovku dosti dlouhou dobu. V logu je pak výpis podobný tomuto:
 admin console: initSessionAttributes()  
 Cannot refresh Catalog : Connection timed out  
Tohoto neduhu se lze zbavit následujícím postupem:

1) Do souboru %GLASSFISH_HOME/glassfish/domains/domain1/domain.xml přidejte následující jvm-option:
 <jvm-options>-Dcom.sun.enterprise.tools.admingui.NO_NETWORK=true</jvm-options> 

2) Odstraňte (raději si ho někam zálohujte) update tool jar z umístění %GLASSFISH_HOME/glassfish/modules/console-updatecenter-plugin.jar

3) Pokud máte čistou instalaci, můžete si dovolit odstranit složky %GLASSFISH_HOME/glassfish/domains/domain1/osgi-cache a %GLASSFISH_HOME/glassfish/domains/domain1/generated. Postup mi fungoval i bez tohoto bodu.

4) %GLASSFISH_HOME/bin/asadmin [re]start-domain

čtvrtek 8. března 2012

EJB jar včetně závislostí

Potřeboval jsem vytvořit EJB jar v němž měly být obsaženy také všechny závislosti. V mém případě MyBatis a Spring Framework. Pro buildování používám maven. Do výsledného jaru samozřejmě nelze zabalit přímo další jary, je třeba je nejprv rozbalit. To vyřešil mavenovský plugin maven-dependency-plugin přidaný do pom.xml. Zajímavá je v tomto místě především jeho část execution:
 <execution>  
   <id>unpack-dependencies</id>  
   <phase>process-classes</phase>  
   <goals>  
     <goal>unpack-dependencies</goal>  
   </goals>  
   <configuration>  
     <!--Definuji co se ma rozbalit do vysledneho jaru-->  
     <includeGroupIds>org.springframework,org.mybatis</includeGroupIds>  
     <outputDirectory>${project.build.directory}/classes</outputDirectory>  
   </configuration>  
 </execution>  
Aby byly přibalené závislosti také v classpath, vypadala definice pluginu maven-ejb-plugin následovně:
 <plugin>  
   <groupId>org.apache.maven.plugins</groupId>  
   <artifactId>maven-ejb-plugin</artifactId>  
   <version>2.3</version>  
   <configuration>  
     <archive>  
       <manifest>  
         <addClasspath>true</addClasspath>  
       </manifest>  
     </archive>  
     <ejbVersion>3.1</ejbVersion>  
   </configuration>  
 </plugin>  
Výsledkem celého snažení ovšem byla nicneříkající chyba:
Caused by: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 9 in XML document from URL [file:.../META-INF/applicationContext.xml] is invalid; nested exception is org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans'.
Naštěstí jsem narazil na blogspot, který tento problém řeší následovně: "stačí" ze všech použitých springových jarů vybrat soubory META-INF/spring.handlers a META-INF/spring.schemas a sloučit je pouze do dvou. Tyto dva dát do adresáře META-INF a celé to zabalit do jaru. Výsledný jar, pak umístit do lib aplikačního serveru. V případě použití novější verze springu může být třeba tyto soubory doeditovat.

čtvrtek 12. ledna 2012

SSL na více virtuálních serverech v rámci jednoho serveru glassfish

Poblém, který následující řádky řeší je přístup přes https na více domén (druhého řádu) v rámci jednoho glassfishe, kdy domény jsou definovány v rámci virtuálního serveru. Používat více serverových certifikátů na jednom glassfishi v rámci jedné domény možné není. Více o problému zde.

Uvedený problém je řešitelný vydáním certifikátu odpovídající standardu RFC-2818 (http://www.ietf.org/rfc/rfc2818.txt) následujícím způsobem:
V CN bude název jednoho z požadovaných názvů virtuálního serveru. Ostatní názvy virtuálních serverů budou v položce "Alternativní jména subjektu certifikátu" (SubjectAltName), kde je možné umístit jednu nebo více položek dNSName případně iPAddress nebo uniformResourceIdentifier. Tomuto podobných řešení existuje více (např. využití regulárního výrazu v CN), uvedené má ovšem nejlepší interoperabilitu (http://wiki.cacert.org/VhostTaskForce#Interoperability_Test).
Podle zmíněného standardu je možné v dNSName využívat i hvězdičkovou konvenci. Navíc definuje, že CN je použito jako identita až v případě, že není definováno rozšíření subjectAltName typu dNSName. Používání CN je zastaralé a RFC-2818 namísto toho certifikačním autoritám doporučuje používat dNSName.