giovedì 8 marzo 2012

Utilizzo utility TCP-MON errore "Failed to load Main-Class manifest..."

Ho scaricato dal sito http://code.google.com/p/tcpmon/downloads/detail?name=tcpmon-1.1.jar&can=2&q= l'utility TCP-MONITOR per fare dei test.

Sulla documentazione è scritto che per farla funzionare in ambiente windows basta fare doppio click sul jar.
Ho provato (su Windows XP con jvm 6) ma non mi ha funzionato, allora ho tentato da linea di comando digitando il comando

java -jar tcpmon-1.1.jar

e in questo caso l'errore apparso a console è stato:

Failed to load Main-Class manifest attribute from
tcpmon-1.1.jar


A questo punto ho decompilato il jar, ho visto che in effetti nel manifest non è presente l'attributo Main-Class.
Ho quindi cercato la classe che contiene il main e che fa partire l'interfaccia swing , ossia la com.codegoogle.tcpmon.MainWindow.

Quindi ho digitato, sempre da console il comando:

java -cp  tcpmon-1.1.jar com.codegoogle.tcpmon.MainWindow


Ora il TCP monitor funziona correttamente e appare a video la schermata



domenica 4 marzo 2012

EntityManager merge vs flush

La differenza fondamentale tra l'usare il metodo merge e il persist dell' EntityManager è che il metodo persist lascia l'entità in uno stato "managed" anche dopo la sua invocazione, mentre il metodo merge no.
Si può verificare il comportamento con un semplice esempio, supponiamo che il nostro Ejb abbia i 2 seguenti metodi:

.......
public void inserisci(Persona p) {
        System.out.println("Inserisco la persona");
        em.persist(p);
        p.setEta(99);

    }

public void inserisciM(Persona p) {
        System.out.println("Inserisco la persona");
        em.merge(p);
        p.setEta(99);
    }
.....


Il codice client che richiama i metodi è il seguente:

public static void main(String[] args) throws Exception {
        Properties p=new Properties();
        p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
        p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
        p.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");
        Context ctx=new InitialContext(p);
        GestoreDao gd=(GestoreDao)ctx.lookup("Anagrafe/remote");
        System.out.println("Ok alla lookup");
        Persona p1=new Persona();
        p1.setCognome("Rossi");
        p1.setNome("marco");
        p1.setEta(32);
        gd.inserisci(p1);
        Persona p2=new Persona();
        p2.setCognome("Verdi");
        p2.setNome("Gino");
        p2.setEta(32);
        gd.inserisciM(p2);
    }


Nel primo caso, chiamando il metodo persist, il valore dell'età della persona Marco Rossi sarà settata a 99, nel secondo caso, chiamando il metodo merge invece resterà al valore base di 32.

mercoledì 29 febbraio 2012

Configurare log4j per una Web Application

Una pratica comune quando si configura una Web Application per utilizzare log4j è la seguente.
Definiamo innanzitutto un appender per il log della nostra applicazione e poi tramite una servlet che si carica all'attivazione dell'applicazione configuriamo il log4j.
Nel nostro caso  la servlet cerca un parametro definito a livello di Web-xml (un context parameter) e, se lo trova valorizzato allora cerca il log4j.properties nella directory specificata, altrimenti utilizza l'impostazione di default.
Ricordiamo che i livelli di log previsti dal frqamework log4j sono i seguenti:

  •  TRACE;
  •  DEBUG;
  •  INFO;
  •  WARN;
  •  ERROR;
  •  FATAL.

log4j.properties

##############################################################
################### DEFINIZIONE DEI LOGGER ###################
##############################################################


log4j.rootLogger=INFO,ApplicationConsoleLog

#Gestione log4j generale per l'Applicazione

log4j.logger.WebTest.MyLogger=DEBUG, ApplicationLog



###############################################################
########## DEFINIZIONE DEGLI APPENDER DEFINITI SOPRA ##########
                        ###### CONSOLE ########
                        #######################
log4j.appender.ApplicationConsoleLog=org.apache.log4j.ConsoleAppender
log4j.appender.ApplicationConsoleLog.layout=org.apache.log4j.PatternLayout

                        ###### EXCEPTION ######
                        #######################
log4j.appender.ApplicationLog=org.apache.log4j.RollingFileAppender
log4j.appender.ApplicationLog.File=
log4j.appender.ApplicationLog.MaxFileSize=2000KB
log4j.appender.ApplicationLog.MaxBackupIndex=10
log4j.appender.ApplicationLog.layout=org.apache.log4j.PatternLayout
log4j.appender.ApplicationLog.layout.ConversionPattern=%d{dd-MM-yyyy HH\:mm\:ss} -%p- %m%n

Sono definiti 2 logger, un ApplicationConsoleLog   che opera solo a livello di console dell'application server e poi un ApplicationLog che si occupa di loggare su File.
Il codice della Servlet è il seguente:

InitServlet

public class InitServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private static final String LOG4J_FILE_PROP="log4j.appender.ApplicationLog.File";
      
    /**
     * @see HttpServlet#HttpServlet()
     */
    public InitServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see Servlet#init(ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        ServletContext ctx=config.getServletContext();
        StringBuffer log4jConf=new StringBuffer();
        log4jConf.append(getConfigPath(ctx));
        log4jConf.append(File.separator);
        log4jConf.append("log4j.properties");
        try
        {
        File log4JFile=new File(log4jConf.toString());
        if(log4JFile.exists()){
            InputStream stream=new FileInputStream(log4JFile);
            Properties log4jProp = new Properties();
            log4jProp.load(stream);
            
            if(log4jProp.getProperty(LOG4J_FILE_PROP).trim().equals("")){
                String logDir=getLogFileDefault(ctx);
                log4jProp.setProperty(LOG4J_FILE_PROP, logDir);
                
                
            }
            PropertyConfigurator.configure(log4jProp);
            Logger.getLogger("WebTest.MyLogger").warn("###########################\nLog4j CARICATO CON SUCCESSO");
        }
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
        
    }
    private String getLogFileDefault(ServletContext ctx){
        StringBuffer sb=new StringBuffer();
        sb.append(ctx.getRealPath(""));
        sb.append(File.separator);
        sb.append("logs");
        sb.append(File.separator);
        sb.append("log.log");
        return sb.toString();
    }
    /**
     * Cerco la directory contenente il file  di configurazione di log4j <br>
     * nella directory specificata nel context parameter"locationConfig"<br>
     * se il percorso non è specificato allora lo cerco dentro la directory config della web-app
     * @param ctx
     * @return
     */
    private String getConfigPath(ServletContext ctx) {
        String path="";
        String nome="locationConfig";    
        path = ctx.getInitParameter(nome);
        if(path==null || path.trim().equals("")){
            // non � stato configurato via Web Xml
            path=ctx.getRealPath("")+File.separator+"config";
        }
        return path;
    }

}
 
web.xml

....

 <servlet>
    <description></description>
    <display-name>InitServlet</display-name>
    <servlet-name>InitServlet</servlet-name>
    <servlet-class>it.servlet.InitServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>InitServlet</servlet-name>
    <url-pattern>/InitServlet</url-pattern>
  </servlet-mapping>
<context-param>
<param-name>locationConfig</param-name>
<param-value></param-value>
</context-param>

.....


Per richiamare il nostro log all'interno del codice Java sarà sufficiente scrivere:

.......

private static Logger log=Logger.getLogger("WebTest.MyLogger");

.......

domenica 26 febbraio 2012

Query JPQL e query native, comportamento apparentemente strano

Ho notato un comportamento "strano" utilizzando in JPA le query JPQL rispetto a quelle Native.

Abbiamo la seguente situazione di partenza su un db:




In un Ejb stateless abbiamo i due seguenti metodi:

@Override
    public void updateForTestJPQL() {
        Persona cust = em.find(Persona.class, 1);
        cust.setCognome("bianchi");
        Query q = em.createQuery( "UPDATE Persona p SET p.cognome = 'gialli'");
       
        q.executeUpdate();
        System.out.println(cust.getCognome());
       
    }
    @Override
    public void updateForTestNative(){
        Persona cust = em.find(Persona.class, 1);
        cust.setCognome("bianchi");
        Query q=em.createNativeQuery("UPDATE persona p SET p.cognome = 'gialli'");
        q.executeUpdate();
        System.out.println(cust.getCognome());
       
    }

Il primo metodo utilizza le query JPQL, il secondo invece la query native (sql standard).

Il risultato delle due esecuzioni però differisce.
In entrambi i casi in output trovo la scritta "bianchi".
Ma sul db nel caso di query JPQL ho la seguente situazione






quindi tutti i record della tabella sono stati aggiornati con il valore "gialli".

Invece nel secondo caso (query nativa):





Il primo record, quello che era stato recuperato tramite il metodo find dell'Entity Manager risulta con cognome "bianchi" e solo gli altri due record sono stati aggiornati.
Il motivo di questo apparente comportamento è che la query nativa viene eseguita in un suo contesto transazionale, quindi :
  1. e' effettuata prima l'update su tutto il db;
  2. alla fine del metodo avviene la sincronizzazione dell'entity manager con il db e quindi modificato il cognome del primo record.
Viveversa usando jpql tutto è eseguito alla fine del metodo, quindi "prevale" l'ultima operazione, quella dell'aggiornamento

venerdì 24 febbraio 2012

Trovare tutte le query in esecuzione sul DB

Eseguendo la seguente query sullo schema MASTER si possono tirare fuori tutte le query in esecuzione :

SELECT st.text, r.session_id, r.status, r.command, r.cpu_time, r.total_elapsed_time
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS st
order by total_elapsed_time desc

sys.dm_exec_sql_text(sql_handle | plan_handle)
"Restituisce il testo del batch SQL identificato dall'argomento sql_handle specificato" (MSDN)

Può essere utilizzato con le seguenti tabelle:

  • sys.dm_exec_query_stats
  • sys.dm_exec_requests
  • sys.dm_exec_cursors
  • sys.dm_exec_xml_handles
  • sys.dm_exec_query_memory_grants
  • sys.dm_exec_connections

giovedì 23 febbraio 2012

Strategie mappature PK con gli EJB 3.0

Ogni entity bean deve essere identificato univicamente.
Sono possibili 3 strategie:

  • Usando l'annotation @Id;
  • Usando l'annotation @IdClass;
  • Usando l'annotation @EmbeddedId.
@Id

Questa annotation è applicata al campo che mappa la primary key nel database, quando nel database la chiave primaria è composta da un singolo campo.
I campi supportati sono campi primitivi, e oggetti che implementano Serializable come java.lang.String java.util.Date e java.sql.Date.
Non è raccomandato utilizzare campi di tipo float o double, per via di possibili arrotondamenti e imprecisioni

@IdClass

Nel caso di composite key sul db si crea una classe con le chiavi e in questa classe si effettua l'override dei due metodi di object equals e hashCode.

Quindi nell'entity bean si utilizza a livello di classe l'annotation @IdClass facendo riferimento alla classe appena creata e quindi sui singoli campi che fanno parte dell'id si utilizza l'annotation @Id.

Esempio:

Supponiamo di avere una classe Persona con chiave composta formata dall'accoppiata cognome e nome + altri campi.

Definiamo quindi l'IdClass

public class ChiavePersona implements Serializable
{
    String nome;
    String cognome;
   public ChiavePersona(){}
   public boolean equals(Object other) {
   if(other instanceof ChiavePersona){
   final ChiavePersona otherPK=(ChiavePersona)other;
   return (other.name.equals(this.nome) && other.cognome.equals(this.cognome));

   }
   }
   public int hashCode(){
    return super.hashCode();
    }
 
}

L' entity è definita così:

@Entity
@IdClass(ChiavePersona.class)
public class Persona{
 @Id
  protected String nome;
 @Id
  protected String cognome;

   ......
}



@EmbeddedId

In questo caso si crea come nel caso precedente la classe con la primary key ma annotandola con @Embeddable e quindi referenziando poi la classe stessa con l'annotation @EmbeddedId
Nell'Entity quindi

@Entity
public class Persona{
 public Persona(){}
 @EmbeddedId
 protected ChiavePersona personaId;
}
.....

}


venerdì 17 febbraio 2012

Installare SVN sotto Windows

SVN nasce in ambiente Linux e quindi di solito sulle guide si trovano spesso le modalità di installazione per le distribuzioni del pinguino.
Per installare su Windows il tutto comunque non ci sono particolari difficoltà.
I passi che ho seguito (ovviamente non essendo un sistemista ho effettuato una installazione basilare e non so come effettivamente poi personalizzare il tutto) sono:

SCARICARE IL SW

Dal sito http://svn1clicksetup.tigris.org/   si può scaricare l'ultima versione disponibile. L'eseguibile si chiama Svn1ClickSetup-1.3.3.exe e l'installazione è guidata.
Al termine dell'installazione sarà startato un processo sul S.O.denominato SVNService.
Di default è utilizzata la porta 3690.
Inoltre saranno create 2 cartelle sul S.O.:

  • C:\Programmi\Subversion con le librerie di installazione e le guide di utilizzo;
  • C:\svnrepos dove è presente il "DB" del sistema (nella directory db).

NOTE INSTALLAZIONE

E' richiesta durante l'installazione la userId e pwd da amministratore. Le utenze saranno salvate nel file c:\svnrepos\conf\passwd . Sono salvate in chiaro nella forma username=password (anche in questo caso presumo sia possibile effettuare installazioni più serie con password cifrate ecc. ecc.). Per censire nuovi utenti bisogna editare questo file.

Una volta installato il pacchetto automaticamente la distribuzione ha scaricato anche Tortoise, il comodo client che può essere utilizzato nel menu contestuale che appare alla pressione del tasto destro del mouse.

UTILIZZO

Con Tortoise è già possibile utilizzare il repository, per linkarlo bisogna digitare l'indirizzo svn://[NOME_SERVER].

Per utilizzarlo su eclipse invece bisogna scaricare il plugin.
Da Eclipse  Indigo Help/Install New Software l'url del repository da inserire è  http://subclipse.tigris.org/update_1.6.x/

L'utilizzo del Plug In è semplice ed intuitivo come CVS.
Esistono poi numerose guide on line che spiegano in dettaglio l'utilizzo del sistema di versioning.