martedì 13 aprile 2021

MongoDb testare uguaglianza di 2 campi sulla stessa collection dati

Per eseguire una comparazione tra 2 campi stessa collection occorre usare una aggregation con una project.


Esempio


 


/* 1 */
{
    "_id" : ObjectId("5fae54de75f12730010c3d0c"),
    "first_name" : "MArio",
    "address" : {
        "state" : "ITALIA",
        "city" : "ROMA"
    },
    "nome" : "MArio",
    "ssn" : 12.0
}

/* 2 */
{
    "_id" : ObjectId("5fae550c75f12730010c3d0d"),
    "first_name" : "GINO",
    "address" : {
        "state" : "ITALIA",
        "city" : "MILANO"
    },
    "nome" : "PINO",
    "ssn" : 13.0
}


Se vogliamo tirar fuori solo i record con campo first_name uguale a nome dobbiamo fare in questo modo:

db.people.aggregate([{
    $project : {
        "CONDITION" : {
            $eq : ["$first_name", "$nome"] 
        },
        "document" : "$$ROOT"   
    }
}, {
    $match : {
        "CONDITION":true
    }
}]);

domenica 23 aprile 2017

Maven project export jar con libererie esterne al jar

Tramite le seguenti impostazioni nel file pom.xml è possibile ottenere l'export del jar inserendo nel classpath del manifest la referenza alle librerie esterne.

Vediamo un esempio semplice :

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>logbackSample.TestLog</mainClass>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib</classpathPrefix>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

In questo modo nel jar prodotto vediamo che sarà creata una cartella lib con tutte le librerie del progetto, mentre il manifest interno al jar avrà i seguenti valori:

Manifest-Version: 1.0

Archiver-Version: Plexus Archiver

Built-By: userdev

Class-Path: lib/logback-classic-1.0.13.jar lib/logback-core-1.0.13.jar

  lib/slf4j-api-1.7.5.jar

Created-By: Apache Maven 3.3.9

Build-Jdk: 1.8.0_72

Main-Class: logbackSample.TestLog



sabato 5 marzo 2016

Replica set chaining

Con il termine replica set chaining in mongodb si intende la possibilità di fare in modo che i nodi secondari possano sincronizzarsi da altri secondari e non sempre e solo dal primario, come accadeva nelle versioni precedenti alla 2.2.
Il motivo dell'abilitazione di default del replica set chaining è quello di evitare un sovraccarico di richieste al nodo primario, infatti al momento (versione 3.2) di default il comportamento prevede che ogni nodo si sincronizzi automaticamente con quello più vicino a livello di ping.
Vediamo ora come è possibile modificare la sincronia da un membro all'altro in una replica set.
Possiamo accedere alla console con il comando

mongo --nodb


Quindi istanziare l'oggetto ReplSetTest

 var test=new ReplSetTest({name:"prova",nodes:3});


Quindi facciamo partire la replica con il comando

 test.startSet();


Ora inizializziamo l'ambiente con il comando

 test.initiate();  


Di default i 3 server sono stati fatti partire sulle porte 30001 30002 e 30003.
Se ci connettiamo alla 30001 nel mio caso vedo che si tratta del primary, l'output del comando rs.status() è il seguente:



prova:PRIMARY> rs.status();
{
    "set" : "prova",
    "date" : ISODate("2016-03-05T16:21:07.306Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 0,
            "name" : "csciandrone-HP-Pavilion-Notebook:31000",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 355,
            "optime" : Timestamp(1457194861, 2),
            "optimeDate" : ISODate("2016-03-05T16:21:01Z"),
            "electionTime" : Timestamp(1457194638, 1),
            "electionDate" : ISODate("2016-03-05T16:17:18Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "csciandrone-HP-Pavilion-Notebook:31001",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 232,
            "optime" : Timestamp(1457194861, 2),
            "optimeDate" : ISODate("2016-03-05T16:21:01Z"),
            "lastHeartbeat" : ISODate("2016-03-05T16:21:06.963Z"),
            "lastHeartbeatRecv" : ISODate("2016-03-05T16:21:06.970Z"),
            "pingMs" : 0,
            "syncingTo" : "csciandrone-HP-Pavilion-Notebook:31000",
            "configVersion" : 1
        },
        {
            "_id" : 2,
            "name" : "csciandrone-HP-Pavilion-Notebook:31002",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 232,
            "optime" : Timestamp(1457194861, 2),
            "optimeDate" : ISODate("2016-03-05T16:21:01Z"),
            "lastHeartbeat" : ISODate("2016-03-05T16:21:06.963Z"),
            "lastHeartbeatRecv" : ISODate("2016-03-05T16:21:06.966Z"),
            "pingMs" : 0,
            "syncingTo" : "csciandrone-HP-Pavilion-Notebook:31000",
            "configVersion" : 1
        }
    ],
    "ok" : 1
}




Ho visto che questa situazione si verifica solo dopo aver inserito almeno un record sul primario, altrimenti la proprietà syncingTo indicava che non era ancora possibile sincronizzarsi ad alcun nodo.

Possiamo vedere quindi che entrambi i secondari si sincronizzano dal primario.
Per cambiare questo comportamento dobbiamo connetterci ad un secondario
Verifichiamo la configurazione della replica set, in particolare che il chaining sia correttamente abilitato come previsto da default:

prova:SECONDARY> var status=rs.conf();
prova:SECONDARY> status
{
    "_id" : "prova",
    "version" : 1,
    "members" : [
        {
            "_id" : 0,
            "host" : "csciandrone-HP-Pavilion-Notebook:31000",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
               
            },
            "slaveDelay" : 0,
            "votes" : 1
        },
        {
            "_id" : 1,
            "host" : "csciandrone-HP-Pavilion-Notebook:31001",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
               
            },
            "slaveDelay" : 0,
            "votes" : 1
        },
        {
            "_id" : 2,
            "host" : "csciandrone-HP-Pavilion-Notebook:31002",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
               
            },
            "slaveDelay" : 0,
            "votes" : 1
        }
    ],
    "settings" : {
        "chainingAllowed" : true,
        "heartbeatTimeoutSecs" : 10,
        "getLastErrorModes" : {
           
        },
        "getLastErrorDefaults" : {
            "w" : 1,
            "wtimeout" : 0
        }
    }
}





Come possiamo vedere la proprietà chainingAllowed è settata a true.
Ora vediamo come cambiare la sincronizzazione:

prova:SECONDARY> rs.syncFrom("csciandrone-HP-Pavilion-Notebook:31001")
{
    "syncFromRequested" : "csciandrone-HP-Pavilion-Notebook:31001",
    "prevSyncTarget" : "csciandrone-HP-Pavilion-Notebook:31000",
    "ok" : 1
}


Quindi facendo rs.status() possiamo verificare che adesso il secondario sulla porta 31002 si sincronizza con quello presente sulla porta 31001.

Per disabilitare il chaining possiamo connetterci ad esempio sul primario e salvare (come visto prima) in una variabile il risultato di rs.conf() .

 var conf=rs.conf();


Quindi digitare il comando


conf.settings.chainingAllowed=false;
rs.reconfig(conf);



Ovviamente la modifica sarà operativa solo per il prosieguo delle configurazioni, le modifiche già effettuate come la nostra resteranno attive.






domenica 28 febbraio 2016

Mongodb shard test (versione 3.0.7)

In questo esempio creiamo un ambiente con:
  • 3 config server;
  • 2 shard;
  • 2 router(mongos)
Per prima cosa mi sono creato una directory shardTest con all'interno le seguenti directory:
  • configServ;
  • configServ2;
  • configServ3;
  • shard1;
  • shard2

Quindiho fatto partire da shell prima i config server poi i router e poi gli shard


mongod --configsvr --port 30000 --dbpath /home/csciandrone//eserciziMongo/shardTest/configServ --logpath /home/csciandrone/eserciziMongo/shardTest/configServ/log.txt --fork



mongod --configsvr --port 30001 --dbpath /home/csciandrone//eserciziMongo/shardTest/configServ2 --logpath /home/csciandrone/eserciziMongo/shardTest/configServ2/log.txt --fork



mongod --configsvr --port 30002 --dbpath /home/csciandrone//eserciziMongo/shardTest/configServ3 --logpath /home/csciandrone/eserciziMongo/shardTest/configServ3/log.txt --fork


Di seguito i comandi per attivare i mongos:

mongos --port 27018 --configdb 127.0.0.1:30000,127.0.0.1:30001,127.0.0.1:30002
mongos --port 27019 --configdb 127.0.0.1:30000,127.0.0.1:30001,127.0.0.1:30002



Ora facciamo partire i 2 mongod (i 2 shard):

mongod --port 27020 --dbpath /home/csciandrone/eserciziMongo/shardTest/shard1 --logpath /home/csciandrone/eserciziMongo/shardTest/shard1/log.txt --fork
mongod --port 27021 --dbpath /home/csciandrone/eserciziMongo/shardTest/shard2 --logpath /home/csciandrone/eserciziMongo/shardTest/shard2/log.txt --fork


Ora possiamo connetterci ad uno dei due mongos e aggiungere i 2 shard appena creati:

mongo --port 27018
MongoDB shell version: 3.0.9
connecting to: 127.0.0.1:27018/test
mongos> sh.addShard("127.0.0.1:27020")
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> sh.addShard("127.0.0.1:27021")
{ "shardAdded" : "shard0001", "ok" : 1 }



A questo punto il nostro sharding cluster è configurato correttamente.
Occorre quindi operare nel seguente modo:
1) scegliere un db e creare la collection (es db test collection randomNumbers);
2) abilitare lo sharding per il db;
3) scegliere chiave di sharding per la collection;
4) creare la shard key

Quindi supponiamo di inserire un record in una collection:

db.randomNumbers.insert({a:1,b:2,c:3}) 

Creaiamo un indice sulla chiave a:
db.randomNumbers.createIndex({a:1})

Quindi abilitiamo lo shard:

sh.enableSharding("test")

Ora effettuiamo lo shard della collection sulla chiave a:

sh.shardCollection("test.randomNumbers",{a:1})
Con il comando sh.status() possiamo vedere lo stato del cluster:

sh.status()
--- Sharding Status --- 
  sharding version: {
 "_id" : 1,
 "minCompatibleVersion" : 5,
 "currentVersion" : 6,
 "clusterId" : ObjectId("56d305ccad2dda9a7c735322")
}
  shards:
 {  "_id" : "shard0000",  "host" : "127.0.0.1:27020" }
 {  "_id" : "shard0001",  "host" : "127.0.0.1:27021" }
  balancer:
 Currently enabled:  yes
 Currently running:  no
 Failed balancer rounds in last 5 attempts:  0
 Migration Results for the last 24 hours: 
  No recent migrations
  databases:
 {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
 {  "_id" : "test",  "partitioned" : true,  "primary" : "shard0000" }
  test.randomNumbers
   shard key: { "a" : 1 }
   chunks:
    shard0000 1
   { "a" : { "$minKey" : 1 } } -->> { "a" : { "$maxKey" : 1 } } on : shard0000 Timestamp(1, 0)

Ora proviamo ad inserire un pò di record nella collection:

for(var i=0;i<100000;i++){db.randomNumbers.insert({a:Math.floor(Math.random()*100),b:Math.floor(Math.random()*100),c:Math.floor(Math.random()*100)})}

Ora possiamo rieseguire il comando sh.status() e vediamo che il balancer è entrato in azione:

sh.status(true)
--- Sharding Status --- 
  sharding version: {
 "_id" : 1,
 "minCompatibleVersion" : 5,
 "currentVersion" : 6,
 "clusterId" : ObjectId("56d305ccad2dda9a7c735322")
}
  shards:
 {  "_id" : "shard0000",  "host" : "127.0.0.1:27020" }
 {  "_id" : "shard0001",  "host" : "127.0.0.1:27021" }
  balancer:
 Currently enabled:  yes
 Currently running:  no
 Failed balancer rounds in last 5 attempts:  0
 Migration Results for the last 24 hours: 
  1 : Success
  1 : Failed with error 'could not acquire collection lock for test.randomNumbers to migrate chunk [{ : MinKey },{ : MaxKey }) :: caused by :: Lock for migrating chunk [{ : MinKey }, { : MaxKey }) in test.randomNumbers is taken.', from shard0000 to shard0001
  databases:
 {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
 {  "_id" : "test",  "partitioned" : true,  "primary" : "shard0000" }
  test.randomNumbers
   shard key: { "a" : 1 }
   chunks:
    shard0000 2
    shard0001 1
   { "a" : { "$minKey" : 1 } } -->> { "a" : 9 } on : shard0000 Timestamp(2, 1) 
   { "a" : 9 } -->> { "a" : 81 } on : shard0000 Timestamp(1, 2) 
   { "a" : 81 } -->> { "a" : { "$maxKey" : 1 } } on : shard0001 Timestamp(2, 0)

lunedì 22 febbraio 2016

MongoDb 3.0 Security

Su MongoDb di default le impostazioni di sicurezza sono disabilitate, per abilitarle occorre far partire il server con l'opzione --auth.
Tutti i dati degli utenti sono salvati nella collection db.system.users nel db admin.
Obiettivo del post è creare un utente root in grado di creare utenti e che abbia un ruolo da amministratore generale su ogni db.
Quindi creare un db di prova e abilitare due utenti uno in sola lettura e uno in lettura e scrittura al db.

RUOLI BUILT-IN

Mongodb fornisce la possibilità di inserire i ruoli scegliendo in un ventaglio di ruoli predefiniti.

A livello di singolo database abbiamo:
  • read, può leggere su tutte le collection non di tipo system , eccezion fatta per system.indexes,system.js e system.namespaces
  • readWrite legge e modifica tutte le collection non system più la system.js

A livello di amministrazione di singolo database abbiamo:
  • dbAdmin consente di effettuare operazione amministrative, indicizzazione e raccolta di statistiche, non consente di gestire le utenze;
  • dbOwner consente di effettuare tutto sul singolo db, unisce i privilegi di readWrite a quelli di userAdmin e di dbOwner
  • userAdmin crea e modifica ruoli sul db
 A livello di amministrazione globale dei database abbiamo invece:
  • readAnyDatabase
  • readWriteAnyDatabase
  • userAdminAnyDatabase
  • dbAdminAnyDatabase
A livello di amministrazione di cluster abbiamo invece:

  • clusterAdmin
  • clusterManager
  • clusterMonitor
  • hostManager 
A livello di superuser abbiamo root che combina le abilitazioni di:
  • readWriteAnyDatabase
  • dbAdminAnyDatabase
  • userAdminAnyDatabase
  • clusterAdmin

ESEMPIO CREAZIONE SUPER USER


Facciamo partire il server senza impostazioni di sicurezza

sudo mongod --dbpath /data/dbTest --port 27018 --logpath /data/dbTest/dbInfo.log  --fork

Ora colleghiamoci al db:

mongo --port 27018

Quindi scegliamo il db admin e creaiamo l'utente con role root

> use admin
switched to db admin
> db.createUser({user:"superAdmin",pwd:"root",roles:["root"]})
Successfully added user: { "user" : "superAdmin", "roles" : [ "root" ] }


Quindi possiamo vedere interrogando la collection db.system.users l'utente attualmente inserito:


> db.system.users.find().pretty()
{
 "_id" : "admin.superAdmin",
 "user" : "superAdmin",
 "db" : "admin",
 "credentials" : {
  "SCRAM-SHA-1" : {
   "iterationCount" : 10000,
   "salt" : "HL4Hdem/yHeYZdPzC/1rFA==",
   "storedKey" : "3XGxaW4FtZEc+O+Xk+D/Zq7w3K0=",
   "serverKey" : "cOFlFork/KU//07ZZeNTp/vQ6AM="
  }
 },
 "roles" : [
  {
   "role" : "root",
   "db" : "admin"
  }
 ]
}


Quindi buttiamo giù il servizio di mongod e facciamo ripartire il server con lo stesso script di prima + l'opzione --auth. Con l'utenza appena creata possiamo riloggarci al db admin:

mongo 127.0.0.1:27018/admin -u superAdmin -p

Verrà chiesta al prompt la password.

Iniziamo a creare 4 utenti su un db denominato provaUser, uno con profilo read, uno write e uno dbOwner. Infine creiamo un utente denominato utentepowerdb con ruolo dbAdminAnyDatabase.

Per fare questa operazione dobbiamo specificare prima il db su cui opereremo , quindi dobbiamo digitare il comando use provaUser.

Prima di creare invece l'utente con ruolo dbAdminAnyDatabase


> db.createUser({user:"utentebase",pwd:"utentebase",roles:[{role:"read",db:"provaUser"}]})
Successfully added user: {
 "user" : "utentebase",
 "roles" : [
  {
   "role" : "read",
   "db" : "provaUser"
  }
 ]
}
> db.createUser({user:"utentewrite",pwd:"utentewrite",roles:[{role:"readWrite",db:"provaUser"}]})
Successfully added user: {
 "user" : "utentewrite",
 "roles" : [
  {
   "role" : "readWrite",
   "db" : "provaUser"
  }
 ]
}
> db.createUser({user:"utenteowner",pwd:"utenteowner",roles:[{role:"dbOwner",db:"provaUser"}]})
Successfully added user: {
 "user" : "utenteowner",
 "roles" : [
  {
   "role" : "dbOwner",
   "db" : "provaUser"
  }
 ]
}
db.createUser({user:"utentepowerdb",pwd:"utentepowerdb",roles:["dbAdminAnyDatabase"]}) 

Quindi proviamo a riloggarci con utentebase e verifichiamo che possiamo leggere dalla collection presente sul db provaUser ma non scrivere:


mongo 127.0.0.1:27018/provaUser -u utentebase -p 
MongoDB shell version: 3.0.9
Enter password: 
connecting to: 127.0.0.1:27018/provaUser
> show collections
randomData
system.indexes
> db.randomData.find()
{ "_id" : ObjectId("56cb814e7c51ef2d6cf2fdd1"), "a" : 60 }
> db.randomData.insert({a:Math.floor(Math.random()*100)})
WriteResult({
 "writeError" : {
  "code" : 13,
  "errmsg" : "not authorized on provaUser to execute command { insert: \"randomData\", documents: [ { _id: ObjectId('56cb8c0928a311bd5a49faa5'), a: 18.0 } ], ordered: true }"
 }
})




Con l'utentewrite invece l'inserimento sarà possibile ma non si potranno ad esempio creare nuovi utenti; tale operazione sarà invece consentita al nostro utente con ruolo dbOwner.


mongo 127.0.0.1:27018/provaUser -u utentewrite -p 
MongoDB shell version: 3.0.9
Enter password: 
connecting to: 127.0.0.1:27018/provaUser
> db.randomData.insert({a:Math.floor(Math.random()*100)})
WriteResult({ "nInserted" : 1 })
> db.randomData.createIndex({a:1})
{
 "createdCollectionAutomatically" : false,
 "numIndexesBefore" : 1,
 "numIndexesAfter" : 2,
 "ok" : 1
}
> db.createUser({user:"test",pwd:"test",roles:[{role:"read",db:"provaUser"}]})
2016-02-22T23:36:39.962+0100 E QUERY    Error: couldn't add user: not authorized on provaUser to execute command { createUser: "test", pwd: "xxx", roles: [ { role: "read", db: "provaUser" } ], digestPassword: false, writeConcern: { w: "majority", wtimeout: 30000.0 } }
    at Error ()
    at DB.createUser (src/mongo/shell/db.js:1101:11)
    at (shell):1:4 at src/mongo/shell/db.js:1101
> ^C
bye
xxxxxx@xxxxxx-HP-Pavilion-Notebook:/data$ mongo 127.0.0.1:27018/provaUser -u utenteowner -p 
MongoDB shell version: 3.0.9
Enter password: 
connecting to: 127.0.0.1:27018/provaUser
> db.createUser({user:"test",pwd:"test",roles:[{role:"read",db:"provaUser"}]})
Successfully added user: {
 "user" : "test",
 "roles" : [
  {
   "role" : "read",
   "db" : "provaUser"
  }
 ]
}




sabato 20 febbraio 2016

Mongostat e Mongotop

Tra i tool nativi di monitoraggio performance di MongoDb quelli  principalmente usati sono due:

1) mongostat -> fornisce dati real time sul processo mongod o mongos in esame, in particolare ci indica il numero di insert /update/delete etc

2) mongotop -> fornisce dati sul processo mongod o mongos separati per namespace (quindi per database e nome collection)indicando tempo totale di esecuzione e poi tempo impiegato per lettura e per scrittura

Entrambi i comandi agiscono ogni secondo e si bloccano premendo CTRL+C.

Esempio output mongostat

insert query update delete getmore command flushes mapped   vsize    res faults qr|qw ar|aw netIn netOut conn     time
  4413    *0     *0     *0       0     1|0       0 400.0M 1000.0M 131.0M      0   0|0   0|0  600k   257k    2 16:40:29
  4176    *0     *0     *0       0     1|0       0 400.0M 1000.0M 132.0M      0   0|0   0|0  568k   244k    2 16:40:30
  4376    *0     *0     *0       0     1|0       0 400.0M 1000.0M 132.0M      0   0|0   0|0  595k   255k    2 16:40:31
  4122    *0     *0     *0       0     1|0       0 400.0M 1000.0M 133.0M      0   0|0   0|0  561k   241k    2 16:40:32

Da questo output si deduce che sono in  corso delle insert sul db.

Esempio output mongotop sullo stesso db a batch in corso

2016-02-20T16:44:26.936+0100    connected to: 127.0.0.1

                                 ns    total    read    write    2016-02-20T16:44:27+01:00
                        test.awards     63ms     0ms     63ms                            
                 admin.system.roles      0ms     0ms      0ms                            
               admin.system.version      0ms     0ms      0ms                            
             certificationTest.crud      0ms     0ms      0ms                            
       certificationTest.log_events      0ms     0ms      0ms                            
            certificationTest.ninni      0ms     0ms      0ms                            
        certificationTest.sliceTest      0ms     0ms      0ms                            
   certificationTest.system.indexes      0ms     0ms      0ms                            
certificationTest.system.namespaces      0ms     0ms      0ms                            
                     prova.persona      0ms     0ms      0ms                            

                                 ns    total    read    write    2016-02-20T16:44:28+01:00
                        test.awards     51ms     0ms     51ms                            
                 admin.system.roles      0ms     0ms      0ms                            
               admin.system.version      0ms     0ms      0ms                            
             certificationTest.crud      0ms     0ms      0ms                            
       certificationTest.log_events      0ms     0ms      0ms                            
            certificationTest.ninni      0ms     0ms      0ms                            
        certificationTest.sliceTest      0ms     0ms      0ms                            
   certificationTest.system.indexes      0ms     0ms      0ms                            
certificationTest.system.namespaces      0ms     0ms      0ms                            
                   prova.persona      0ms     0ms      0ms                            


 Dal mongotop vediamo sempre che sono in corso operazioni di write e in più abbiamo l'evidenza del fatto che il namespace interessato è test.awards (quindi db test e collection awards)



domenica 11 ottobre 2015

MongoDb configurazione e utilizzo ReplicaSet

In MongoDb è facilmente possibile configurare n istanze di mongod in modo da farle coordinare tra loro come repliche.
Nella replica esiste un nodo di tipo primario (PRIMARY) dove di default avvengono letture e scritture e poi ci sono dei processi asincroni che si occupano di scrivere i dati sui nodi secondari (SECONDARY).
Le applicazioni che interrogano la replica di default leggono e scrivono dal primary, per evitare di incorrere nel problema degli stale data.
Questa impostazione si può cambiare consentendo la lettura (mai la scrittura) dai secondary, ovviamente accettando il rischio di poter leggere dati non attuali.
Per configurare una replica set sono necessari 2 passi.
Per prima cosa occorre far partire le istanze dei server che faranno parte della replica. Di seguito un esempio di bat su windows:

start mongod --replSet provaReplica --logpath 1.log --dbpath c:/data/rs1 --port 27017 --smallfiles --oplogSize 64
start mongod --replSet provaReplica --logpath 2.log --dbpath c:/data/rs2 --port 27018 --smallfiles --oplogSize 64
start mongod --replSet provaReplica --logpath 3.log --dbpath c:/data/rs3 --port 27019 --smallfiles --oplogSize 64

Questa bat fa partire sul proprio server 3 istanze di mongod legate alla replica chiamata provaReplica.
Bisogna prima assicurarsi che le 3 cartelle dove le repliche scriveranno i file siano fisicamente presenti sull'hard disk.
A questo punto bisogna configurare la replica, per farlo occorre connettersi ad uno dei 3 mongod e lanciare il seguente script:

> var config={_id:"provaReplica",members:[{_id:0,host:"localhost:27017"}, {_id:1,host:"localhost:27018"}, {_id:2,host:"localhost:27019"}]};
> rs.initiate(config);

A questo punto dopo una breve attesa comparirà subito un risultato (sperabilmente un ok) e possiamo vedere come la replica sia funzionante . Digitando rs.status() abbiamo lo stato della replica.
Il server su cui ci siamo connessi facendo parte della replica può essere o un primary oppure un secondary e abbiamo comunque questa evidenza connettendoci al db vederemo la scritta provaReplica:PRIMARY oppure provaReplica:SECONDARY.
Facendo delle insert sul primary è possibile quindi testare il corretto funzionamento disconnettendosi dal primary e quindi connetendosi (mongo --port 27018 o mongo --port 27019) ai secondary per verificare che l'insert sia stato correttamente replicato.
Quando si interroga un secondario per poter effettuare le query bisogna prima dare il comando:
rs.slaveOk().
Il meccanismo di replica è reso possibile dall' oplog, una collection presente nel db local dove sono loggate le operazioni effettuate sul primary.
Tale file viene sincronizzato dal primary ai secondary consentendo la riproduzione di quanto effettuato.
Per visionare la collection uplog occorre posizionarsi sul db local e quindi digitare il comando:

db.oplog.rs.find().pretty()

sabato 10 ottobre 2015

Indici geospaziali con MongoDb 3.0

Con MongoDb è possibile censire in una collection una serie di luoghi identificati tramite longitudine e latitudine, secondo uno specifico formato definito GeoJson.
Quindi esistono delle funzioni di ricerca molto comode che ci consentono di farci tornare i posti più vicini oppure situati entro un certo range da una locazione.
Vediamo un esempio di script di inserimento di un luogo:

db.locations.insert({citta:"roma",nome:"viale san giovanni bosco 72", location:{type:"Point",coordinates:[12.5588865,41.8604389]},type:"Street"}
Si noti che la sintassi type:"Point" e il valore coordinates sono nomi obbligatori che corrispondono alla nomenclatura prevista dai file di tipo GeoJson.
Le coordinate vanno inserite mettendo prima la longitudine e poi la latitudine (il contrario della visualizzazione da google maps, dove si possono prendere i valori nell'url dopo la @ una volta specificato un indirizzo.

Per creare un indice di tipo geospaziale si procede in questo modo:
 db.locations.createIndex({"location":"2dsphere"})
Quindi per ricercare i punti vicini ad una coordinata si può utilizzare l'operatore $near in questo modo:
db.locations.find({location:
{$near:{$geometry:{type:"Point",coordinates:[13.4181409,41.6929645],$maxDistance:2000}}}})
.pretty()

lunedì 24 agosto 2015

Tomcat LDAP

Per configurare Tomcat in LDAP (es. accesso active directory) è sufficiente:

1) definire il Realm JNDI dentro il file conf/server.xml

2) mettere la web application in sicurezza via web.xml

Per recuperare lo username loggato si può utilizzare il metodo getUserPrincipal() dell'oggetto HttpServletRequest.

Per prima cosa è necessario conoscere l'indirizzo IP del nostro server Ldap. Se siamo in una intranet possiamo da dos lanciare il comando nslookup.

Per configurare il realm inserire il seguente xml (la porta di default è la 389 verificare comunque presso il proprio ambiente):

 <Realm className="org.apache.catalina.realm.JNDIRealm" 
      connectionURL="ldap://[IP]:389/" debug="99" userPattern="{0}"/>


Per il valore debug si noti che un valore alto genera un maggiore dettaglio di log. Al valore 0 corrisponde nessun log.
userPattern definisce invece il DN (Distinguished Name) .

Nel web.xml dell'applicazione web invece inseriamo il security constraint :

 <security-role>
        <role-name>*</role-name>
    </security-role>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>
                Applicazione
            </web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>INSERIRE CREDENZIALI ACCESSO PC</realm-name>
    </login-config>


All'utente sarà richiesto quindi di autenticarsi solo la prima volta in modalità BASIC auth, e bisognerà immettere le credenziali di accesso al pc in LAN.

Javascript usare Active-X per recuperare utente connesso

Questo script javascript (funziona soltanto su IE, non è il massimo a livello di sicurezza etc.) può essere utilizzato per recuperare lo username dell'utenza Windows connessa.

function getUserName() {
  try
  {
   var wshNetwork = new ActiveXObject("WScript.Network");
   var userName = wshNetwork.userName; 
   document.getElementById("myVal").value=userName;
   return userName;
  }
  catch(err){
   alert(err.message);
  }
 }



lunedì 6 luglio 2015

Sql Server 2008 fetch first n rows

Sulla versione 2008 per ottenere la limitazione dell'output si usa una scomoda sintassi di questo tipo:


SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY nomeCampo desc) as row FROM miaTabella 
 ) a WHERE a.row >0 and a.row <= 100;


Questa sintassi è analoga alla LIMIT 0,100 di MySql

mercoledì 17 giugno 2015

Errore schemaLocation: ... must have even number of URI's

Nei file xml di spring la voce xsi:schemaLocation specifica dove trovare gli xsd per i namespace definiti prima nel file.
Esempio:


<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:jdbc="http://www.springframework.org/schema/jdbc"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:batch="http://www.springframework.org/schema/batch"
 xmlns:context="http://www.springframework.org/schema/context"
 
 xsi:schemaLocation="http://www.springframework.org/schema/jdbc 
     http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
  http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-4.0.xsd">


In questo caso su alcuni namespace è definita la locazione dell'xsd (non è obbligatorio che ci siano tutti).
E' però importante che le dichiarazioni seguano questo schema [NAMESPACE] [XSD_NAMESPACE], quindi il numero totale di dichiarazioni dovrà essere pari.

Se proviamo a cancellare una di queste voci allora apparirà l'eccezione

Error schemaLocation:....... must have even number of URI's

venerdì 5 giugno 2015

WebSphere (su windows) controllare versione

Per controllare esattamente la versione di WAS che si sta utilizzando posizionarsi via cmd sotto

C:\Program Files\IBM\WebSphere\AppServer\bin

e lanciare la bat versionInfo.bat.

Compariranno in console tutte le info relative all'installazione di WAS

domenica 3 maggio 2015

Spring esternalizzare configurazioni datasource e cifrare password

Scenario: abbiamo una applicazione Java che si connettead un db utilizzando Spring, vogliamo fare in modo che i dati della connettività siano spostati dal file di contesto di Spring ad un normale file di properties esterno al jar, in modo che l'utente possa personalizzarlo a seconda dell'ambiente di esecuzione senza dover toccare il file specifico del context di Spring.
Nel file di configurazione di Spring è possibile definire la seguente proprietà:

<context:property-override location="file:conf/db.properties"/>

Il file è letto in un percorso esterno rispetto al jar di esecuzione, come specificato appunto dalla direttiva file.

Il file esterno avrà una struttura di questo tipo (quella tipica di un file di properties):


dataSource.url=jdbc:sqlserver://localhost:1433;dataBaseName=hibernatetest
dataSource.username=pippo
dataSource.password=m4dr+75TW7u0nWFUU52IaQ==


Quindi nell'applicationContext.xml avremo la seguente definizione del dataSource:


 <bean id="dataSource"
  class="it.dao.BasicDataSourceCifrato">
  <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
  <property name="url" value="${dataSource.url}" />
  <property name="username" value="${dataSource.username}" />
  <property name="password" value="${dataSource.password}" />
  <property name="initialSize" value="5"></property>
  <property name="maxActive" value="10"></property>
 </bean>


Il datasource è stato ridefinito estendendo  org.apache.commons.dbcp.BasicDataSource .
In questo modo possiamo effettuare la ridefinizione del metodo setPassword inserendo la decifratura della password:


package it.dao;
import it.oasi.cipher.Cifratura;
import org.apache.commons.dbcp.BasicDataSource;
public class BasicDataSourceCifrato extends BasicDataSource {
 public BasicDataSourceCifrato() {
 }
 public synchronized void setPassword(String password) {     
        super.setPassword(decryptPassword(password));
    }
 private String decryptPassword(String password) { 
          // qui si inserisce la logica di decifratura pwd
          ...... 
 }

}


Spring Jdbc Template

In alcuni casi particolari risulta molto utile utilizzare per l'accesso al DBMS JDBC invece degli ORM (Hibernate,EBatis etc.), soprattutto in ambienti legacy dove il database è già esitente e non ottimizzato per l'utilizzo degli ORM.
Il problema di JDBC è comunque l'eccessiva verbosità del codice che ci costringe a gestire l'apertura delle connessioni, la chiusura, i PreparedStatement etc.
Una soluzione ideale in questo ambito è Spring Jdbc, che con il modello dei template ci esenta dal dover gestire manualmente il "plumbing code" JDBC.

Vediamo i passi da seguire per realizzare una applicazione client che si connette ad un db.

LIBRERIE

Ho utilizzato in questo caso spring 3.0
Le dipendenze maven sono le seguenti:


<properties>
 <org.springframework.version>3.0.0.RELEASE</org.springframework.version>
</properties>
 <dependencies>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${org.springframework.version}</version>
  </dependency>
  <dependency>

   <groupId>org.springframework</groupId>

   <artifactId>spring-jdbc</artifactId>

   <version>${org.springframework.version}</version>

  </dependency>
  <dependency>
   <groupId>commons-dbcp</groupId>
   <artifactId>commons-dbcp</artifactId>
   <version>1.2.2</version>
  </dependency>
  <dependency>
   <groupId>commons-pool</groupId>
   <artifactId>commons-pool</artifactId>
   <version>1.4</version>
  </dependency>

 </dependencies>








FILE DI CONFIGURAZIONE

Il file di configurazione del context di Spring:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:task="http://www.springframework.org/schema/task"
 xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">


 <bean id="adUserDao" class="it.dao.AdUserDaoImpl">
  <property name="dataSource" ref="dataSource" />
 </bean>

 <bean id="dataSource"
  class="org.apache.commons.dbcp.BasicDataSource">

  <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
  <property name="url" value="jdbc:sqlserver://localhost:1433;dataBaseName=hibernatetest" />
  <property name="username" value="pippo" />
  <property name="password" value="pippo" />
  <property name="initialSize" value="5"></property>
  <property name="maxActive" value="10"></property>
 </bean>
 
</beans>


TABELLA

La tabella su cui si scrive e si legge ha il seguente script di creazione (DBMS Sql server versione 2008):


CREATE TABLE [dbo].[aduser](
 [id] [bigint] IDENTITY(1,1) NOT NULL,
 [name] [varchar](255) NOT NULL,
 [password] [varchar](255) NOT NULL,
 CONSTRAINT [PK_aduser] PRIMARY KEY CLUSTERED 
(
 [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]



INTERFACCIA ADUSERDAO


package it.dao;
import java.util.List;
import it.objects.AdUser;
public interface AdUserDao {
 
 public boolean insert(AdUser obj) throws DaoException ;
 
 public AdUser getUserById(int id) throws DaoException;
 
 public List<AdUser> getListaUtenti() throws DaoException;

}


CLASSE ADUSER 

package it.objects;
public class AdUser {
 private static final String A_CAPO = "\r\n";
 private int id;
 private String nome;
 private String password;
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public String getNome() {
  return nome;
 }
 public void setNome(String nome) {
  this.nome = nome;
 }
 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
 public AdUser(){
  
 }
 public AdUser(String nome,String password){
  this.nome=nome;
  this.password=password;
  
 }
 public String toString(){
  StringBuffer sb=new StringBuffer();
  sb.append("ID: ");
  sb.append(this.id);
  sb.append(A_CAPO);
  sb.append("NOME: ");
  sb.append(this.nome);
  sb.append(A_CAPO);
  sb.append("PWD: ");
  sb.append(this.password);
  sb.append(A_CAPO);
  return sb.toString();
 }
}



IMPLEMENTAZIONE DAO 
package it.dao;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import it.objects.AdUser;
public class AdUserDaoImpl implements AdUserDao {
 private DataSource dataSource;
 private JdbcTemplate jdbcTemplate;
 public DataSource getDataSource() {
  return dataSource;
 }

 public void setDataSource(DataSource dataSource) {
  this.dataSource = dataSource;
 }

 public AdUserDaoImpl() {
  // TODO Auto-generated constructor stub
 }

 @Override
 public boolean insert(AdUser obj) throws DaoException {
  try
  {
   boolean retVal=false;
   jdbcTemplate=new JdbcTemplate(dataSource);
   String insert="insert into aduser(name,password) values (?,?)";
   int esito=jdbcTemplate.update(insert,new Object[]{obj.getNome(),obj.getPassword()});
   if(esito>0) retVal= true;
   return retVal;
  }
  catch(Exception ex){
   throw new DaoException("Errore nell'insert dettaglio "+ex.getMessage());
  }
 }

 @Override
 public AdUser getUserById(int id) throws DaoException {
  try
  {
   jdbcTemplate=new JdbcTemplate(getDataSource());
   String get="select id,name,password from aduser where id=?";
   AdUser retVal=jdbcTemplate.queryForObject(get,new Object[]{id}, new AdUserRowMapper());
   return retVal;
  }
  catch(Exception ex){
   throw new DaoException("Errore nel recupero dettaglio "+ex.getMessage());
  }
 }

 @Override
 public List<AdUser> getListaUtenti() throws DaoException {
  try
  {
   jdbcTemplate=new JdbcTemplate(getDataSource());
   String get="select id,name,password from aduser";
   List<AdUser> listaUtenti=new ArrayList<AdUser>();
   List<Map<String, Object>> rows =jdbcTemplate.queryForList(get);
    for (Map row : rows) {
     AdUser u=new AdUser();
     u.setId(Integer.parseInt(String.valueOf(row.get("ID"))));
     u.setNome((String)row.get("name"));
     u.setPassword((String)row.get("password"));
     listaUtenti.add(u);
    }
   return listaUtenti;
  }
  catch(Exception ex){
   throw new DaoException("Errore nel recupero dettaglio "+ex.getMessage());
  }
 }

}



ROWMAPPER 

Il RowMapper è una classe che implementa l'interfaccia di Spring org.springframework.jdbc.core.RowMapper e che serve a mappare il ResultSet con l'oggetto.

package it.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import it.objects.AdUser;
import org.springframework.jdbc.core.RowMapper;
public class AdUserRowMapper implements RowMapper<AdUser> {
 public AdUserRowMapper() {
  // TODO Auto-generated constructor stub
 }
 @Override
 public AdUser mapRow(ResultSet rs, int arg1) throws SQLException {
  // TODO Auto-generated method stub
  AdUser a=new AdUser();
  a.setId(rs.getInt("ID"));
  a.setNome(rs.getString("name"));
  a.setPassword(rs.getString("password"));
  return a;
 }

}



ESECUZIONE PROGRAMMA

 ConfigurableApplicationContext context=new ClassPathXmlApplicationContext("conf/applicationContext.xml");
   AdUserDao ad=(AdUserDao)context.getBean("adUserDao");
        AdUser user= ad.getUserById(1);
        System.out.println(user);
        List<AdUser> utenti=ad.getListaUtenti();
        for(AdUser a : utenti){
         System.out.println(a);
        }
        ad.insert(new AdUser("qweqw", "rrrrrrr"));
        context.close(); 

NOTE 

In questo caso abbiamo utilizzato la connessione da un pool gestito dalle librerie :

commons-dbcp-1.4.jar
commons-pool-1.4.jar

Si può anche decidere di utilizzare il DriverManagerDataSource di Spring che ci ritorna una connessione ogni volta che viene richiesta. Oppure il SingleConnectionDataSource che torna ogni volta la stessa connessione già utilizzata, come se si trattase di un pool di connessioni con size pari a 1.
Di seguito la configurazione per l'implementazione del DriverManagerDataSource:


<bean id="dataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource">

  <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
  <property name="url" value="jdbc:sqlserver://localhost:1433;dataBaseName=hibernatetest" />
  <property name="username" value="pippo" />
  <property name="password" value="pippo" />
 </bean>




mercoledì 29 aprile 2015

BAT per stop Tomcat clean directory e restart servizio

Con questo script è possibile stoppare tomcat ripulire le directory e fare il restart.
E' meglio utilizzare il comando NET START o NET STOP piuttosto che sc in quanto sc è asincrono per cui si rischia vada in errore la cancellazione dei file.
Lo script è il seguente:


    @echo OFF

    set CATALINA_HOME=D:\lavoro\apache-tomcat-7.0.53
    net stop TomcatService
   
    
    

    echo removing work
    rmdir /S /Q "%CATALINA_HOME%\work"
    echo making new work dir
    mkdir "%CATALINA_HOME%\work"
 
    echo removing temp
    rmdir /S /Q "%CATALINA_HOME%\temp"
    echo making new temp dir
    mkdir "%CATALINA_HOME%\temp"
 
    echo removing logs
    rmdir /S /Q "%CATALINA_HOME%\logs"
    echo making new logs dir
    mkdir "%CATALINA_HOME%\logs"
 
    echo starting tomcat service
  
    net start TomcatService


venerdì 17 aprile 2015

Verificare le porte di WebSPhere

WAS  utilizza porte diverse a seconda dell'accesso da console oppure da http o https.
Per verificare quali sono si può aprire il file

AboutThisProfile.txt che si trova tipicamente in

C:\Program Files\IBM\WebSphere\AppServer\profiles\[PROFILO]\logs

Il file presenta queste informazioni:


Ambiente del server delle applicazioni da creare: Server delle applicazioni
Ubicazione: C:\Program Files\IBM\WebSphere\AppServer\profiles\AppSrv01
Spazio su disco richiesto: 200 Mb
Nome profilo: AppSrv01
Imposta questo profilo come valore predefinito: True
Nome nodo: xxxxxxxx
Nome host: xxxxxxxxxxxx
Abilita sicurezza di gestione (consigliato): True
Porta della console di gestione: 9060
Porta sicura della console di gestione: 9043
Porta di trasporto HTTP: 9080
Porta di trasporto HTTPS: 9443
Porta di avvio: 2809
Porta connettore SOAP: 8880
Esegui il server delle applicazioni come servizio: True
Crea una definizione server Web: False
Impostazioni di ottimizzazione delle prestazioni: Standard



mercoledì 15 aprile 2015

PrimeFaces autoscroll dopo update eventi ajax

Spesso in form molto lunghi, quando si scrolla per la compilazione, l'utilizzo di Ajax può essere fastidioso perchè ad ogni esecuzione del codice poi sul client ci si riposiziona in alto e si perde lo scroll.
In javascript questo problema si risolve utilizzando la seguente sintassi:


document.getElementById("idComponente").scrollIntoView();


Dalla versione 3.2 di Primefaces è stato introdotto il metodo  
  RequestContext.getCurrentInstance().scrollTo

Purtroppo però non mi funzionava (stavo utilizzando la versione 3.5).
Comunque un workaround funzionante è quello di utilizzare sempre l'oggetto org.primefaces.context.RequestContext, inserendo il javascript dentro in questo modo:

 RequestContext.getCurrentInstance().execute("document.getElementById( 'componentId' ).scrollIntoView();");


Si noti che l'id deve essere quello generato dal componente a video, per capirci quello che vediamo col viewsource della pagina.

domenica 12 aprile 2015

Java8 iterazione esterna

Esempio iterazione esterna con Java 8.
Supponiamo di avere una lista di oggetti di tipo Persona identificati da alcune proprietà (nome, cognome, età ) ed una proprietà isMaggiorenne che ci filtra le persone con età > 18 anni.
Per avere un conteggio di queste occorrenze prima di Java 8 si operava in questo modo:



int count=0;
  for(Persona p: l){
   if(p.isMaggiorenne()){
    count++;
   }
  }


Questa è la classica iterazione esterna.
Con Java 8 si introduce il concetto di iterazione interna , dove invece di procedere noi all'esecuzione del ciclo for si opera sugli stream inserendo i filtri esplicitamente.
Il caso di esempio precedente si risolve in Java 8 nel seguente modo:

long l= i.getListePersone().stream().filter(person -> person.isMaggiorenne()).count();


Oltre alla maggiore compattezza del codice il vero vantaggio è che l'utilizzo degli stream consente  al compilatore di eseguire lui al meglio le operazioni richieste, magari anche parallelizzando le operazioni se necessario.

giovedì 12 febbraio 2015

Settare JDK su Ireport

Ireport attualmente non funziona se abbiamo installato la JDK 1.8.
La versione che ho provato io è la 4.7.0, non proprio l'ultimissima (siamo attualmente alla 5.5.0), comunque anche su quest'ultima leggo sui forum che ci sono problemi.
Io avevo installato la JDK 1.8 ma comunque ho nel path come prima entry quella della JDK 1.6 per cui ero convinto di girare comunque con la JDK 1.6 (anche la JAVA_HOME punta alla JDK 1.6).
Attenzione quindi, iReport prende la JDK da suoi ragionamenti interni ignorando le opzioni specificate a livello di sistema....

Avevo il seguente messaggio di errore (il file di log di Ireport si trova in Windows  sotto c:\Utenti\nomeutente\.ireport\4.7.0\var\log\messages.log )

java.lang.IllegalStateException: java.lang.IllegalAccessException: Class org.openide.util.WeakListenerImpl$ProxyListener can not access a member of class org.openide.filesystems.$Proxy0 with modifiers "public"

Per risolvere occorre modificare il seguente file ireport.conf che si trova sotto C:\Program Files\Jaspersoft\iReport-4.7.0\etc.

In particolare ho modificato la parte in grassetto

# ${HOME} will be replaced by user home directory according to platform
default_userdir="${HOME}/.${APPNAME}/4.7.0"
default_mac_userdir="${HOME}/Library/Application Support/${APPNAME}/4.7.0"

# options used by the launcher by default, can be overridden by explicit
# command line switches
default_options="--branding ireport -J-Xms256m -J-Xmx512m -J-Dorg.netbeans.ProxyClassLoader.level=1000 -J-XX:MaxPermSize=512m "
# for development purposes you may wish to append: -J-Dnetbeans.logger.console=true -J-ea

# default location of JDK/JRE, can be overridden by using --jdkhome <dir> switch
#
jdkhome="C:/Program Files/Java/jdk1.6.0_45"

# clusters' paths separated by path.separator (semicolon on Windows, colon on Unices)
#extra_clusters=