Subversion keywords sui file Java

Subversion (qui la guida) ha la capacità di sostituire delle keywords all’interno del file versionato. Le possibili keywords sono:

  • Date - descrive l’ultima volta che il file è stato modificato nel repository.
  • Revision - descrive l’ultima revisione nella quale il il file è stato modificato nel repository.
  • Author - descrive l’ultimo utente che ha modificato il file nel repository.
  • Id - è una combinazione delle altre keywords.

Utilizziamo le keywords all’interno della JavaDoc di un file Java. Un tipico header template può essere:

/**
 * CLASS DESCRIPTION
 *
 * @author Pasquale Marcoccia
 * @version $Revision$ on $Date$ by $Author$
 */

oppure:

/**
 * CLASS DESCRIPTION
 *
 * @author Pasquale Marcoccia
 * @version $Id$
 */

Per dire a Subversion se sostituire o meno le chiavi di un particolare file, bisogna utilizzare il sottocomando propset. La proprietà svn:keywords, quando settata su un file sotto controllo di versione, controlla quali chiavi dovranno essere sostituite su quel file. Il valore è una lista delle chiavi delimitata da spazi.

svn propset svn:keywords "Date Revision Author Id" MyClass.java

E’ possibile settare le keywords su tutti i file Java in una directory:

find myProject/ -type f -name '*.java' -exec svn propset svn:keywords "Date Revision Author Id" {}  \; -print

Invece, per i file aggiunti successivamente, è possibile settare automaticamente le keywords in base al file modificando il file di configurazione di Subversion (~/.subversion/config):

[miscellany]
enable-auto-props = yes
[auto-props]
*.java = svn:keywords=Date Revision Author Id

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

Add comment Sunday 5 April, 2009 at 11:10

Gestire i progetti con Maven

Per poter utilizzare a pieno le plugin (quelle core e non) consiglio di dare sempre uno sguardo alla descrizione dettagliata (che include tutti i possibili parametri) utilizzando il goal describe della plugin di help:

$ mvn help:describe -Dplugin=[plugin] -Ddetail

dove [plugin] è l’artifactId della plugin, per es. help stessa:

$ mvn help:describe -Dplugin=help -Ddetail

Quando si comincia un progetto ex-novo è possibile crearne la struttura tramite il goal create della plugin archetype come descritto nell’articolo precedente. Tuttavia in quel caso non è stato passato alcun valore per il parametro archetypeArtifactId, che rappresenta l’archetipo del progetto che si intende gestire. Ciò ha impatti sulla struttura della directory e sul contenuto base dei pom. Quando intendiamo utilizzare un archetipo diverso dal default (maven-archetype-quickstart) o ne conosciamo l’archetypeArtifactId oppure utilizziamo il goal generate che è la versione interattiva di create:

$ mvn archetype:generate

La prima richiesta necessita l’inserimento dell’archetipo dopo aver mostrato la lista di tutti quelli disponibili:

[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: internal -> appfuse-basic-jsf (AppFuse archetype for creating a web application with Hibernate, Spring and JSF)
2: internal -> appfuse-basic-spring (AppFuse archetype for creating a web application with Hibernate, Spring and Spring MVC)
3: internal -> appfuse-basic-struts (AppFuse archetype for creating a web application with Hibernate, Spring and Struts 2)
4: internal -> appfuse-basic-tapestry (AppFuse archetype for creating a web application with Hibernate, Spring and Tapestry 4)
5: internal -> appfuse-core (AppFuse archetype for creating a jar application with Hibernate and Spring and XFire)
6: internal -> appfuse-modular-jsf (AppFuse archetype for creating a modular application with Hibernate, Spring and JSF)
7: internal -> appfuse-modular-spring (AppFuse archetype for creating a modularapplication with Hibernate, Spring and Spring MVC)
8: internal -> appfuse-modular-struts (AppFuse archetype for creating a modularapplication with Hibernate, Spring and Struts 2)
9: internal -> appfuse-modular-tapestry (AppFuse archetype for creating a modular application with Hibernate, Spring and Tapestry 4)
10: internal -> maven-archetype-j2ee-simple (A simple J2EE Java application)
11: internal -> maven-archetype-marmalade-mojo (A Maven plugin development project using marmalade)
12: internal -> maven-archetype-mojo (A Maven Java plugin development project)
13: internal -> maven-archetype-portlet (A simple portlet application)
14: internal -> maven-archetype-profiles ()
15: internal -> maven-archetype-quickstart ()
16: internal -> maven-archetype-site-simple (A simple site generation project)
17: internal -> maven-archetype-site (A more complex site project)
18: internal -> maven-archetype-webapp (A simple Java web application)
19: internal -> jini-service-archetype (Archetype for Jini service project creation)
20: internal -> softeu-archetype-seam (JSF+Facelets+Seam Archetype)
21: internal -> softeu-archetype-seam-simple (JSF+Facelets+Seam (no persistence) Archetype)
22: internal -> softeu-archetype-jsf (JSF+Facelets Archetype)
23: internal -> jpa-maven-archetype (JPA application)
24: internal -> spring-osgi-bundle-archetype (Spring-OSGi archetype)
25: internal -> confluence-plugin-archetype (Atlassian Confluence plugin archetype)
26: internal -> jira-plugin-archetype (Atlassian JIRA plugin archetype)
27: internal -> maven-archetype-har (Hibernate Archive)
28: internal -> maven-archetype-sar (JBoss Service Archive)
29: internal -> wicket-archetype-quickstart (A simple Apache Wicket project)
30: internal -> scala-archetype-simple (A simple scala project)
31: internal -> lift-archetype-blank (A blank/empty liftweb project)
32: internal -> lift-archetype-basic (The basic (liftweb) project)
33: internal -> cocoon-22-archetype-block-plain ([http://cocoon.apache.org/2.2/maven-plugins/])
34: internal -> cocoon-22-archetype-block ([http://cocoon.apache.org/2.2/maven-plugins/])
35: internal -> cocoon-22-archetype-webapp ([http://cocoon.apache.org/2.2/maven-plugins/])
36: internal -> myfaces-archetype-helloworld (A simple archetype using MyFaces)
37: internal -> myfaces-archetype-helloworld-facelets (A simple archetype usingMyFaces and facelets)
38: internal -> myfaces-archetype-trinidad (A simple archetype using Myfaces and Trinidad)
39: internal -> myfaces-archetype-jsfcomponents (A simple archetype for create custom JSF components using MyFaces)
40: internal -> gmaven-archetype-basic (Groovy basic archetype)
41: internal -> gmaven-archetype-mojo (Groovy mojo archetype)
Choose a number:  (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41) 15:

Utilizziamo l’archetipo maven-archetype-webapp con il goal create:

mvn archetype:create -DgroupId=net.marcoccia \
    -DartifactId=mywebapp \
    -DpackageName=net.marcoccia.mywebapp \
    -DarchetypeArtifactId=maven-archetype-webapp

La directory del progetto è così costituita:

  • src
    • main
      • resources
      • webapp
        • WEB-INF
          • web.xml
        • index.jsp
  • pom.xml

Il pom.xml generato:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.marcoccia</groupId>
    <artifactId>mywebapp</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>mywebapp Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>mywebapp</finalName>
    </build>
</project>

Nella fase package, il war (notare il campo packaging) viene generato col nome uguale al body del campo finalName (in questo caso mywebapp.war).
Le dipendenze J2EE (di scope provided perchè rese disponibili dal container in cui verrà eseguita la webapp) devono essere inserite manualmente:

<dependencies>
    [...]
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.4</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Maven è anche in grado di gestire progetti con più moduli. Abbiamo un progetto di nome mymultimodule, che richiama i sotto-moduli example e mywebapp:

  • example
    • pom.xml
  • mywebapp
    • pom.xml
  • pom.xml

Il pom “padre” richiama i moduli tramite il campo modules:

<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
        http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.marcoccia</groupId>
    <artifactId>mymultimodule</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>
    <name>My Multimodule Project</name>

    <modules>
        <module>example</module>
        <module>mywebapp</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Mentre i pom “figli” fanno riferimento al “padre” tramite il campo parent:

<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
        http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.marcoccia</groupId>
    <artifactId>example</artifactId>
    <packaging>jar</packaging>
    <version>1.0</version>
    <name>Example Module</name>

    <parent>
        <groupId>net.marcoccia</groupId>
        <artifactId>mymultimodule</artifactId>
        <version>1.0</version>
    </parent>

    [...]
</project>

In questo caso Maven localizza i pom dei sottomoduli e li carica nel Maven Reactor, un componente in grado di analizzare le dipendenze tra i moduli e stabilire l’ordine di compilazione e packaging.

La gestione di un progetto multimodulo spesso porta ad informazioni ridondanti, per esempio possono esserci dei moduli che richiedono una medesima dipendenza. Nel caso in cui moduli “fratelli” condividano una dipendenza è possibile (e consigliato) spostare quest’ultima nel body dependencyManagement del pom “padre”:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.abc</groupId>
            <artifactId>xyz</artifactId>
            <version>2.0.0</version>
        </dependency>
    </dependencies>
<dependencyManagement>

non è necessario specificare lo scope qui, lo faranno i moduli (se necessario), che a loro volta non specificano la versione perchè è stato già fatto dal pom “padre”:

<dependencies>
    <dependency>
        <groupId>org.abc</groupId>
        <artifactId>xyz</artifactId>
    </dependency>
</dependencies>

Definito il progetto preoccupiamoci di reperire le dipendenze necessarie. Possiamo cercare le librerie direttamente sul repository pubblico di Maven http://www.mvnrepository.com. Cercando ad es. log4j troviamo diversi groupId e dopo averne selezionato uno si può direttamente copiare la sezione POM Dependency nelle dependencies del proprio pom.
Se le dipendenze richieste non sono disponibili su un repository Maven pubblico, la soluzione più adatta sarebbe quella di avere un repository aziendale. Poichè ciò non è sempre possibile, la soluzione che propongo è quella di avere un repository locale nel progetto stesso. Lo so, stiamo andando contro gli ideali Maven ma questa soluzione è senza dubbio immediata (e direi anche indolore) soprattutto nel caso che vi sto per illustare. Se ad es. l’organizzazione organization rilascia la versione 2.0.0 della libreria xyz che però ancora non è disponibile sul repository pubblico (e a sua volta questa organization non ha un proprio repository Maven pubblico) allora non vi resta che andare nella sezione download di http://xyz.organization.org e scaricare gli archivi:

  • xyz-2.0.0.jar
  • xyz-2.0.0-javadoc.jar
  • xyz-2.0.0-sources.jar

Generiamo per ogni archivio l’MD5 e lo SHA1 (solo se non si vuole ricevere dei warning), configuriamo l’xyz-2.0.0.pom:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>organization</groupId>
    <artifactId>xyz</artifactId>
    <version>2.0.0</version>
</project>

e mettiamo il tutto nella directory ${basedir}/lib/organization/xyz/2.0.0 (notare il percorso groupId/artifactId/version). Non ci resta che configurare il repository e la dipendenza nel pom.xml:

<repositories>
    <repository>
        <id>myproject.repository</id>
        <name>MyProject Repository</name>
        <url>file:///${basedir}/lib</url>
    </repository>
</repositories>
[...]
<dependencies>
    <dependency>
        <groupId>organization</groupId>
        <artifactId>xyz</artifactId>
        <version>2.0.0</version>
    </dependency>
</dependencies>

Lo ripeto (anche per auto-convincermi): questa dovrebbe essere DAVVERO l’ultima soluzione a eventuali problemi di dipendenze non soddisfatte.

Per mostrare tutte le dipendenze risolte (e quindi incluse nel classpath sempre in base allo scope) possiamo lanciare:

$ mvn dependency:resolve

Probabilmente il risultato sarà un numero di dipendenze maggiore di quelle configurate nel nostro pom, dovuto alle ulteriori dipendenze richieste a loro volta da queste ultime. Per vedere l’albero delle dipendenze e visualizzare “chi richiede cosa” possiamo lanciare:

$ mvn dependency:tree

Terminiamo con l’inserimento delle informazioni a contorno (totalmente opzionali).

  • La licenza d’uso:

    <licenses>
        <license>
            <name>marcoccia.net</name>
            <url>http://www.marcoccia.net/licenses/LICENSE-1.0.txt</url>
            <distribution>repo</distribution>
            <comments>An open-access license</comments>
        </license>
    </licenses>
    
  • Il gruppo di sviluppo:

    <organization>
        <name>Marcoccia.net</name>
        <url>http://www.marcoccia.net</url>
    </organization>
    
  • Gli sviluppatori:

    <developers>
        <developer>
            <id>pasquale.marcoccia</id>
            <name>Pasquale Marcoccia</name>
            <email>pasquale@marcoccia.net</email>
            <url>http://blog.marcoccia.net</url>
            <organization>Marcoccia.net</organization>
            <organizationUrl>http://www.marcoccia.net</organizationUrl>
            <roles>
                <role>developer</role>
            </roles>
            <timezone>+1</timezone>
        </developer>
        [...]
    </developers>
    

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

3 comments Sunday 1 March, 2009 at 16:46

Maven al primo utilizzo

Maven è un tool di project management che, basandosi sul concetto di Project Object Model (POM), permette di definire degli standard, il ciclo di vita e la gestione delle dipendenze di un progetto.
Maven adotta il paradigma convention over configuration e basterà seguire poche convenzioni anche solo per sfruttare una parte di ciò che offre (per es. nella fase di compilazione e packaging).
Qui il download con le istruzioni per l’installazione.

Per default Maven prevede la seguente struttura:

  • in ${basedir}/src/main/java il codice sorgente
  • in ${basedir}/src/main/resources i file di resource
  • in ${basedir}/src/test/java le classi di test
  • in ${basedir}/src/test/resources le risorse per le classi di test
  • in ${basedir}/target/classes le classi compilate
  • in ${basedir}/target il file di distribuzione (per default è un JAR)

La configurazione del progetto viene fatta tramite il file pom.xml nella root del progetto (${basedir}). Di seguito un esempio:

<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
        http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.marcoccia</groupId>
    <artifactId>example</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>example</name>
    <url>http://example.marcoccia.net</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

E’ possibile creare tutta la struttura iniziale del progetto, con relativo pom.xml tramite il comando:

$ mvn archetype:create \
    -DgroupId=net.marcoccia \
    -DartifactId=example \
    -DpackageName=net.marcoccia.example

Il poker di valori groupId, artifactId, packaging e version identificano univocamente un progetto. I campi name e url sono puramente descrittivi. Il blocco dependencies definisce invece le dipendenze richieste dal progetto (in questo caso JUnit per l’esecuzione delle classi di test).
Come può Maven con queste poche righe, compilare, testare, impacchettare, creare la javadoc e un sito di documentazione? Semplice, il pom.xml in realtà estende un “super” pom. Per visualizzare il pom effettivo basta lanciare:

$ mvn help:effective-pom

La sintassi dei comandi Maven visti finora è sempre la stessa:

$ mvn [plugin]:[goal]

dove una plugin può offrire più di un goal (per es. la plugin di compilazione prevede un goal per la compilazione del codice sorgente e un altro per la compilazione dei casi di test).

Un’altra tipologia di comandi Maven è:

$ mvn [phase]

Maven offre tutte le fasi del ciclo di vita di un progetto: dalla validazione dell’integrità al deploy del deliverable in produzione. L’esecuzione di una determinata fase richiama l’esecuzione di eventuali fasi che lo precedono. Ogni fase prevede l’esecuzione di determinati [plugin]:[goal].

Se si lancia la fase package:

$ mvn package

vengono eseguite le seguenti fasi con relative coppie “plugin:goal”:

  • process-resources - resources:resources, che copia tutti i file di resources nella directory di output
  • compile - compiler:compile, che compila il codice sorgente
  • process-test-resources - resources:testResources, che copia tutti i file di resources di test nella directory di output di test
  • test-compile - compiler:testCompile, che compila le classi di test
  • test - surfire:test, esegue le classi di test terminando il processo in caso di fallimento
  • package - jar:jar, crea il jar di distribuzione nella directory di output

Pertanto avremmo avuto lo stesso risultato lanciando:

mvn resources:resources \
        compiler:compile \
        resources:testResources \
        compiler:testCompile \
        surefire:test \
        jar:jar

Maven gestisce le plugin e le dipendenze di un progetto tramite i repository. Esistono due tipi di repository:

  • locale - di default in ~/.m2/repository e contiene tutte le plugin e i pacchetti delle dipendenze dei progetti scaricati in locale
  • remoto - quello di default è http://repo1.maven.org/maven2 e permette il download di plugin e dipendenze esterne

Ogni dipendenza esterna è raggiungibile sul repository tramite il percorso:

/<groupId>/<artifactId>/<version>/<artifactId>-<version>.<packaging>

Eseguendo per esempio la fase install si potrà vedere il proprio deliverable nel repository locale.
Nel pom.xml visto precedentemente la dipendenza con JUnit viene risolta scaricando il pom di rilascio all’indirizzo:

http://repo1.maven.org/maven2/junit/junit/3.8.1/junit-3.8.1.pom

Maven controlla eventuali dipendenze necessarie per JUnit descritte nel pom (effettuandone il download), quindi scarica l’archivio:

http://repo1.maven.org/maven2/junit/junit/3.8.1/junit-3.8.1.jar

Per ogni dipendenza è possibile definire uno scope tra quelli disponibili:

  • compile (default) - le dipendenze sono disponibili in tutti i classpath del progetto
  • provided - è simile a compile, ma prevede che a runtime le dipendenze siano rese disponibili dall’ambiente di esecuzione (per es. le JavaEE APIs per un’applicazione enterprise)
  • runtime - le dipendenze sono richieste solo in esecuzione
  • test - le dipendenze sono richieste solo per la compilazione e l’esecuzione dei test
  • system - la dipendenza non viene recuperata tramite repository, ma ne viene esplicitamente dichiarata la posizione locale

Infine la generazione della documentazione e dei reports avviene nella fase site:

mvn site

L’output è disponibile nella directory ${basedir}/target/site.

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

2 comments Thursday 26 February, 2009 at 21:48

Ridimensionare un disco VMWare

VMWare vCenter Converter è lo strumento che, tra le altre cose, permette di ridimensionare un disco virtuale. Purtroppo non è disponibile una versione per linux e, inoltre, il processo di ridimensionamento sembra essere abbastanza lento oltre a richiedere nuovamente l’installazione dei VMWare Tools.

Questa è la procedura manuale:

  • spegnere la virtual machine
  • rimuovere eventuali snapshots
  • lanciare

    vmware-vdiskmanager -x {size} {disk}

    dove
    {size} è la nuova dimensione del disco (per es. 8GB) e
    {disk} è il path completo del file .vmdk.


Questa procedura aumenta il disco ma non la partizione. Se il disco è partizionato, è necessario un ulteriore utility per ridimensionare la partizione (per es. Partition Magic, GParted Live CD, Paragon Partition Manager, etc). L’utilizzo dell’utility DiskPart di Windows è possibile solo con partizione di dati e non partizioni di boot o di sistema.

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

Add comment Wednesday 21 January, 2009 at 17:47

Tracciare le chiamate concorrenti sul log di JBoss

Quando non si ha a che fare con degli unit test, analizzare il log di un’applicazione enterprise diventa un’impresa ardua per via dei processi concorrenti.

Fortunatamente quando si utilizza Log4J è possibile personalizzare il pattern di layout del log ed aggiungere alcune utilissime stampe.

Su JBoss il file log4j.xml di configurazione sta sotto jboss-VERSION/server/SERVER_INSTANCE_NAME/conf e di default presenta il seguente pattern di layout:

%d %-5p [%c] %m%n

d - la data, che per default usa il formato ISO8601 (dd-MMM-yyyy HH:mm:ss,SSS)

p - il livello (DEBUG, WARN, ecc.)

c - la categoria (nome completo della classe)

m - il messaggio

n - il ritorno a capo nel formato del sistema operativo su cui viene eseguito

Nota: il %-5 davanti a p indica che la stampa deve essere giustificata a 5 caratteri (serve ad allineare il contenuto di m)

Il layout “full” è invece commentato ed è fatto così:

%d %-5r %-5p [%c] (%t:%x) %m%n

r - i msec trascorsi per la costruzione del layout di questa stampa

t - il nome del thread!

x - l’NDC (nested diagnostic context) associato al thread

E’ possibile passare alla visualizzazione estesa anche con JBoss in esecuzione (N.B. se “Append” e’ a false il file di log viene ricreato!).

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

Add comment Tuesday 2 December, 2008 at 11:53

JBoss scheduling con data settata dinamicamente

Se si desidera avviare dei jobs in istanti di tempo settati dinamicamente e’ necessario sfruttare quanto di buono e’ offerto dai componenti JMX di JBoss.

Si parte con la creazione di un TimerMBean e per farlo si hanno tre possibilità:

  • Si aggiunge la seguente riga al file jboss-service.xml nella directory conf dell’istanza del server:

    <mbean code="javax.management.timer.Timer" name="MarcocciaNet:service=myTimer"/>
    
  • Si crea come servizio di un’applicazione di deploy configurando un file di service (per es. mytimer-service.xml) con il seguente contenuto:

    <?xml version="1.0" encoding="UTF-8"?>
    <server>
       <mbean code="javax.management.timer.Timer" name="MarcocciaNet:service=myTimer"/>
    </server>
    

    lo si mette nella root dell’ear e lo si richiama nel jboss-app.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE jboss-app PUBLIC "-//JBoss//DTD J2EE Application 1.4//EN" "http://www.jboss.org/j2ee/dtd/jboss-app_4_0.dtd">
    <jboss-app>
        <module>
            <service>mytimer-service.xml</service>
        </module>
        [...]
    </jboss-app>
    
  • Si crea il servizio programmaticamente:

    MBeanServer server = MBeanServerFactory.createMBeanServer();
    ObjectInstance timer = server.createMBean(
          "javax.management.timer.Timer",
          new ObjectName( "MarcocciaNet", "service", "myTimer" ));
    server.invoke(
          timer.getObjectName(),
          "start",
          new Object[] {},
          new String[] {});
    

La prima soluzione la trovo un pochino invasiva: di norma non è saggio mettere le mani in un file in cui ci sono le configurazioni di tutti gli altri servizi standard di un application server. La terza soluzione richiede attenzione soprattutto nel momento in cui è necessario decidere dove piazzare la parte di codice, che va fatto prima della registrazione listener e delle eventuali notifiche.

La seconda soluzione è la migliore: il servizio è deploiato con l’intera applicazione e prima di ogni altro eventuale componente enterprise. Inoltre all’atto pratico questa soluzione si dimostrerà senza dubbio più versatile.

Una volta istanziato il TimerMBean è necessario agganciare uno o più listener e per farlo è necessario invocare il metodo addNotificationListener sull’istanza dell’MBeanServer. Di seguito un esempio con listener anonimo:

NotificationListener notificationListener = new NotificationListener() {
    public void handleNotification(Notification notification, Object handback) {
        logWriter.debug("Notification received!");
        logWriter.debug("UserObject: " + notification.getUserData());
    }
};
try {
    ObjectName objectName = new ObjectName("MarcocciaNet:service=myTimer");
    // find the local MBeanServer
    MBeanServer server = MBeanServerLocator.locateJBoss();
    server.addNotificationListener(objectName, notificationListener, null, null);
} catch (MalformedObjectNameException ex) {
    // manage
} catch (InstanceNotFoundException ex) {
    // manage
}

Il terzo parametro di addNotificationListener permette al listener di filtrare le notifiche tramite un oggetto NotificationFilter, mentre il quarto parametro è un oggetto di “handback”, ovvero quello ritornato al listener all’arrivo di una notifica.

Adesso che abbiamo anche il listener è possibile inviare le notifiche:

public void sendNotification(Object userData, Date date){
	try {
		InitialContext initialContext = new InitialContext();
		RMIAdaptor adaptor = (RMIAdaptor) initialContext.lookup("jmx/invoker/RMIAdaptor");
		ObjectName objectName = new ObjectName("MarcocciaNet:service=myTimer");
		if (adaptor != null && adaptor.isRegistered(objectName)) {
		    adaptor.invoke(
				objectName,
				"addNotification",
		        new Object[]{
		                "oneShotNotification",
		                "One shot notification sample",
		                userData,
		                date},
		        new String[]{
		                String.class.getName(),
		                String.class.getName(),
		                Object.class.getName(),
		                Date.class.getName()});
		} else {
		    logWriter.debug("---- JNDI Name error: " + adaptor + " ----");
			// manage
		}
	} catch (NamingException ex) {
		// manage
	} catch (MalformedObjectNameException ex) {
		// manage
	} catch (IOException ex) {
		// manage
	} catch (InstanceNotFoundException ex) {
		// manage
	} catch (MBeanException ex) {
		// manage
	} catch (ReflectionException ex) {
		// manage
	}
}

Una volta messo insieme i pezzi è possibile centralizzare il tutto (registrazione/rimozione listener ed invio notifiche) creando un servizio (sar) ad hoc ed esponendo i metodi di javax.management.timer.Timer che non sono esposti dal TimerMBean:

public interface MyTimerMBean extends TimerMBean {

    public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback);

    public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException;

}
public class MyTimer extends Timer implements MyTimerMBean {

    public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback){
		super.addNotificationListener(listener, filter, handback)
    }

    public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
		super.removeNotificationListener(listener);
    }

}

Quest’ultima configurazione apre un’ampia gamma di scenari: è possibile gestire di un pool di listener, potento fare override di start() e’ possibile registrare un listener a startup e così via…

Alla prossima

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

Add comment Wednesday 27 February, 2008 at 15:52

Loggare TRACE su JBoss

Per il logging JBoss estende Log4J e aggiunge un livello custom TRACE, che si pone al di sotto di DEBUG:

  • FATAL
  • ERROR
  • WARN
  • INFO
  • DEBUG
  • TRACE


Fin quando nella propria applicazione non si utilizza il livello TRACE tutto fila liscio. Il problema nasce nel momento in cui si vogliono visualizzare dei log di JBoss che sono a TRACE (i messaggi SOAP che arrivano al webservice sono uno dei tanti esempi). Infatti, essendo TRACE un livello custom, per loggare è necessaria una configurazione diversa nel file conf/log4j.xml:

<category name="{package}">
     <priority value="TRACE" class="org.jboss.logging.XLevel"/>
</category>

Il valore {package} va sostituito con il package della classe che logga a TRACE (org.jboss.ws.core.MessageTrace nell’esempio dei webservice).

E’ necessario inoltre che l’appender non abbia definito il tag Threshold, commentando l’eventuale riga presente:

<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
      <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/>
      <param name="Target" value="System.out"/>
      <!--<param name="Threshold" value="INFO"/>-->

      <layout class="org.apache.log4j.PatternLayout">
         <!-- The default pattern: Date Priority [Category] Message\n -->
         <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
      </layout>
</appender>

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

Add comment Thursday 17 January, 2008 at 11:12

La carriera nell’IT

E’ la prima volta che annoto qualcosa di non tecnico e tuttora sono abbastanza combattuto se farlo o meno. Non è comunque mia intenzione abbandonare la linea guida che mi ero promesso di dare a questo blog, però oggi ho letto un articolo davvero interessante su Punto-Informatico in riguardo alla carriera IT e così mi ritrovo a scrivere di cose che condivido e altre no.

“… l’attenzione smodata alla propria carriera si manifesta in persone dotate, assolutamente in grado di affermarsi a partire dai propri talenti. È vero, ma questa verità non sconferma la tesi che qui sosteniamo. Il fatto è che con i propri talenti è difficile convivere. Impostare su questi la propria affermazione è oneroso. I talenti esigono lavoro, vanno coltivati, fatti fruttare. E poi vanno imposti in un contesto presumibilmente dominato da persone prive di talenti, che appunto costruiscono la propria affermazione sull’autoconvinzione che i talenti non sono necessari per affermarsi. Chi è dotato di talenti troverà così comodo o rintanarsi nel suo cantuccio, rinunciando ad entrare in competizione, alimentando il proprio equilibrio attraverso la lamentela: “sono qui nell’angolo perché nessuno capisce il mio valore”. Oppure troverà comodo accettare le regole del facile gioco dell’atteggiamento arrogante: la moneta cattiva scaccia quella buona, i talenti resteranno nel cassetto, e la gara si giocherà attorno alla capacità di sgomitare e di mettere in cattiva luce l’altro.”

“Chi è dotato di talenti troverà così comodo o rintanarsi nel suo cantuccio, rinunciando ad entrare in competizione”: il dotato di talenti che si rintana in un cantuccio sarà anche dotato di talenti ma la sua personalità non gli permette di farli valere. Chi non accetta la competizione o comunque non ha voglia di mettersi in gioco non ha obiettivi e chi non ha obiettivi non può aspirare a fare carriera. “Oppure troverà comodo accettare le regole del facile gioco dell’atteggiamento arrogante”: è questa l’arma che utilizza chi non vive di sana competizione e si sente già in una posizione di svantaggio.


“Un buon ambiente di lavoro nel quale, oltre ad uno stipendio adeguato, ci sia la possibilità di imparare continuamente cose nuove e di essere valutati in modo chiaro. Troppo spesso si predica che le persone, nell’economia della conoscenza, sono la risorsa più importante e poi a loro si dedica un decimo del tempo riservato alla valutazione di una nuova tecnologia o di un nuovo segmento di mercato”.

Lauro Venturi
amministratore delegato di Siaer


“In ogni grande azienda, dare visibilità al proprio lavoro è fondamentale. Non importa se siete incompetenti, se siete dei super professionisti, se siete gli esperti mondiali di una materia, se siete manager, se siete gli ultimi della catena gerarchica. Se non date visibilità al vostro lavoro, siete FOTTUTI!”

Sante parole…

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

Add comment Friday 11 January, 2008 at 12:20

Modificare il SOAP Address di un WebService su JBoss

In JBoss 4.0.5.GA è presente jbossws 1.0.3.SP1 e per poter modificare indirizzo ip e porta del webservice è necessario andare in {istanza.del.server}/deploy/jbossws14.sar e recuperare il file jbossws.beans.

Si tratta di un archivio che presenta la struttura minima di un deployment:

META-INF/jboss-beans.xml
META-INF/MANIFEST.MF

Esplodiamo il deployment ed eliminiamo l’archivio jbossws.beans; con lo stesso nome ora avremo una directory e i seguenti file all’interno:

{istanza.del.server}/deploy/jbossws14.sar/jbossws.beans/META-INF/jboss-beans.xml
{istanza.del.server}/deploy/jbossws14.sar/jbossws.beans/META-INF/MANIFEST.MF

Per poter modificare indirizzo ip e porta del webservice basta editare il file jboss-beans.xml:

[...]
<property name="webServiceHost">${jboss.bind.address}</property>
<property name="webServiceSecurePort">8443</property>
<property name="webServicePort">8080</property>
<property name="alwaysModifySOAPAddress">true</property>
[...]

Se invece avete un JBoss 4.0.5.GA con jbossws aggiornato, dalla versione 2.x, allora la directory di riferimento è {istanza.del.server}/deploy/jbossws.sar; jbossws.beans è già esploso, ma le definizioni delle porte (sia in chiaro che su ssl) risultano commentate:

[...]
<!--
      Set these properties to explicitly define the ports that will be used for rewriting the SOAP address.
      Otherwise the ports will be identified by querying the list of installed connectors.
      If multiple connectors are found the port of the first connector is used.
      <property name="webServiceSecurePort">8443</property>
      <property name="webServicePort">8080</property>
    -->
[...]

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

Add comment Thursday 10 January, 2008 at 12:23

grep, sed e awk

Anno nuovo, ma cose vecchie :)

Può capitare che a volte le cose s’inceppino e si è costretti a dover recuperare delle situazioni manualmente passando per giga e giga di log. Fortunatamente ci sono dei potenti strumenti di scripting per poter tirare fuori velocemente le informazioni desiderate. Unico requisito: scrivere dei log comprensibili.

Con questo articolo non intendo certo effettuare una trattazione completa su grep, sed e awk (in rete vi sono fiumi e fiumi di byte pronti ad assetare chiunque abbia sete di sapere…); mi limiterò a descrivere gli utilizzi che ne ho fatto.

Supponiamo di avere un oggetto “MyObject” da persistere sul database con le seguenti informazioni: un id di transazione (in numerazione esadecimale), un codice primario (numero di 4 cifre) e un codice esteso (numero di 16 cifre).

Con un log di livello FATAL annoto eventuali inserimenti andati a male e che andrebbero recuperati. Potrei quindi avere un log del tipo:

2007-12-22 07:22:18,214 FATAL [net.marcoccia.myapp.DaoManagerBean] --- Error saving MyObject MyObjectVO{transactionId=393264E, primaryCode=2345, extendedCode=2345678901234567}
2008-01-03 11:19:08,691 FATAL [net.marcoccia.myapp.DaoManagerBean] --- Error saving MyObject MyObjectVO{transactionId=383163C, primaryCode=1234, extendedCode=1234567890123456}
[...]

Eseguo lo script seguente:

grep 'Error saving MyObject' fatal.log | sed 's/.*transactionId=\\(.*\\), primaryCode=\\(.*\\), extendedCode=\\(................\\).*/\\1 \\2 \\3/g' | awk '{print "INSERT INTO MY_OBJECT_TB(ID_TRANSACTION, PRIMARY_CODE, EXTENDED_CODE) VALUES (\\47" $1 "\\47, \\47" $2 "\\47, \\47" $3 "\\47);"}' > recover.sql

Nel file recover.sql ho tutte le query SQL di insert per recuperare la situazione di errore:

more recover.sql
INSERT INTO MY_OBJECT_TB(ID_TRANSACTION, PRIMARY_CODE, EXTENDED_CODE) VALUES ('393264E', '2345', '2345678901234567');
INSERT INTO MY_OBJECT_TB(ID_TRANSACTION, PRIMARY_CODE, EXTENDED_CODE) VALUES ('383163C', '1234', '1234567890123456');

Con la grep effettuata tiro fuori solo le righe di log che riguardano errori di persistenza dell’oggetto MyObject. Le righe trovate vengono quindi passate (tramite pipe) al sed, che permette di selezionare e mettere in comode variabili le parti interessanti della riga (nel nostro caso interessa il transactionId, il primaryCode e l’extendedCode). La selezione viene effettuata utilizzando le regex e raggruppando tra parentesi tonde le parti di interesse.

Es.

sed 's/.*transactionId=\\(.*\\), primaryCode=\\(.*\\), extendedCode=\\(................\\).*/\\1 \\2 \\3/g'

è equivalente a:

sed 's/.*transactionId=\\(.*\\), primaryCode=\\(.*\\), extendedCode=\\(.*\\)\\}.*/\\1 \\2 \\3/g'

Le variabili selezionate vengono quindi passate ad awk, con cui costruisco la query di insert:

awk '{print "INSERT INTO MY_OBJECT_TB(ID_TRANSACTION, PRIMARY_CODE, EXTENDED_CODE) VALUES (\\47" $1 "\\47, \\47" $2 "\\47, \\47" $3 "\\47);"}'

Il codice \47 è il single quote. Avrei anche potuto definire una variabile (sq), valorizzarla con “” e utilizzarla al momento opportuno:

awk -v sq="'" '{print "INSERT INTO MY_OBJECT_TB(ID_TRANSACTION, PRIMARY_CODE, EXTENDED_CODE) VALUES ("sq $1 sq", "sq $2 sq", "sq $3 sq");"}'

Con awk oltre print è possibile utilizzare printf per la formattazione delle variabili. Ad esempio per avere il valore esadecimale si potrebbe scrivere:

awk '{printf "INSERT INTO MY_OBJECT_TB(ID_TRANSACTION, PRIMARY_CODE, EXTENDED_CODE) VALUES (\\47 %x \\47, \\47" $2 "\\47, \\47" $3 "\\47);\n", $1}'

Sotto awk sono disponibili tante altre funzioni come substr e strftime. La prima permette di estrarre solo una parte della variabile, la seconda permette di formattare una data.

Inutile dire che non c’è limite alle manipolazioni che si possono effettuare.

Alla prossima.

[Post to Twitter]  [Post to Yahoo Buzz]  [Post to Delicious]  [Post to Digg]  [Post to Ping.fm] 

Add comment Thursday 3 January, 2008 at 16:56

Previous Posts


Languages

Categories

Feeds

Links