Ho condotto un esperimento di scrittura di software per analizzare i risultati di una esportazione delle mie conversazioni con ChatGPT. Ma ho deciso di aiutarmi nell’analisi con un Large Language Model che sicuramente sa fare molto bene questo lavoro e in più con un LLM in locale, per vedere come funziona.
Negli ultimi giorni ho avuto problemi intermittenti con un mouse Bluetooth Trust (modello 24059-02) connesso al mio HP con Ubuntu. Il mouse ha funzionato regolarmente per un paio di giorni, poi improvvisamente ha smesso di …
Ho installato la versione 24.3.1.347 di Oracle SQL Developer però ho una sorpresa: sembra non sia possibile raggruppare le connessioni in cartelle. Risposta: Eh sì — è una sorpresa che ha colpito parecchi utenti anche …
Quando si esegue uno script Bash ci sono due modalità principali da impiegare, che hanno conseguenze diverse sull’ambiente della shell. 1. Esecuzione diretta (./script.sh) Quindi può solo leggere le variabili di ambiente della shell madre …
Preso dalla curiosità di ripassare l’assembly (tanti anni fa studiai il Motorola 68000) ho scritto una guida ordinata che illustra passo per passo come scrivere un semplice programma Assembly in real-mode (16 bit), confezionarlo come …
Il framework MVC Groovy on Grails si ispira anche onomatopeicamente a Ruby on Rails ed è la composizione di un linguaggio di programmazione per JVM (Groovy) su un framework MVC opzionalmente corredato da Spring (per il controllo della sicurezza) e Hibernate (lo strato ORM verso il database) che è Grails.
Groovy on Grails
Il linguaggio di programmazione è Groovy che però è molto simile a Java, avendo poi rispetto questo una serie di “ammorbidimenti” (vedi l’articolo Programmare in Groovy).
Groovy on Grails
Grails è un framework MVC che può essere associato a Spring per il controllo degli accessi e Hibernate come ORM. È un framework Open Source full-stack per applicazioni web basate su JVM. Adotta Apache Groovy come linguaggio di programmazione e il paradigma “meglio convenzione che configurazione” per consentire un’esperienza di programmazione estremamente produttiva e “aerodinamica”.
Ho scritto alcune guide come Creare un progettoGrails da linea di comando oppure con l’utilizzo della IDE Intellj IDEA di JetBrains. Uso la versione Ultimate che, tra le altre belle cose, ha che è possibile avere contemporaneamente anche un ambiente di sviluppo PHP attraverso l’attivazione di un plugin. In effetti JetBrains produce anche PHPStorm e quindi PHPStorm= IDEA + PHP Plugin.
Devo proteggere la mia applicazione con accesso di login e mi aggancio per questo ad un server CAS. La configurazione che ho fatto (minimale) è di inserire nel file BuildConfig.groovy, nella sezione plugins la riga
compile ":spring-security-cas:2.0.1"
Eseguendo il build:
$ grails s2-quickstart test1 Utente Ruolo Requestmap
SI verifica un errore (e Tomcat non parte)
Exception starting filter CAS Single Sign Out Filter Message: casServerUrlPrefix cannot be null.
Ora la configurazione dei parametri CAS era stata effetuata regolarmente nel file Config.groovy (tutti questi file si trovano nella cartella grails-app/conf del progetto Grails):
In questa situazione CAS viene invocato richiama l’host di ritorno passando il token di sessione, ma il client lato applicazione lo ignora e fa riferimento ad un suo db locale, un database H2.
L’url del db è scritto in DataSource.groovy ed è jdbc:h2:prodDb
ERROR pool.ConnectionPool
Mancano i driver che vanno aggiunti al file conf/BuildConfig.groovy:
dependencies {
// specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes e.g.
runtime 'mysql:mysql-connector-java:5.1.27'
// runtime 'org.postgresql:postgresql:9.3-1100-jdbc41'
test "org.grails:grails-datastore-test-support:1.0-grails-2.3"
}
La riga relativa a mysql di default è commentata: decommentarla. Eventualmente aggiungere il parametro di Oracle se invece si usa Oracle: si devono aggiungere i jar di odbc e ora sotto lib
marcob@jsbach[11:35:58]:betablog$ ll lib/
totale 3140
drwxrwxr-x 2 marcob marcob 4096 ago 26 18:07 ./
drwxrwxr-x 15 marcob marcob 4096 ago 26 18:05 ../
-rw-rw-r-- 1 marcob marcob 1555682 ago 5 16:47 ojdbc14.jar
-rw-rw-r-- 1 marcob marcob 1646178 ago 5 16:47 orai18n.jar
Groovy è un linguaggio debolmente tipizzato (consente la definizione implicita delle variabili e l’aritmetica implicita – in cui il tipo del risultato è dato da una serie di regole quando i tipi degli addendi non sono congruenti, come illustrato tra poco) derivato da Java con in più le performance e la flessibilità di un linguaggio di scripting come Python ad esempio. È un derivato del Java con una nuova sintassi tale da renderlo molto familiare ai programmatori Java. Vi posso assicurare che dopo un po’ che lo si usa si ha la sensazione che non sia poi così tanto diverso da PHP.
Le caratteristiche di Groovy risolvono una serie di problematiche di produttività ed eleganza con un buon compromesso sulla tipizzazione.
Essenzialmente è un linguaggio “Java light” che rispetto a Java ha alcuni rilassamenti:
Si possono definire i tipi delle variabili “in corsa” senza specificarne il tipo che viene dedotto al momento della compilazione (come avviene anche per PHP 7) def pi = 3.14 e pi sarà un Float
La tipizzazione non è rigida come con Java, praticamente scompare la differenza tra classe e tipo primitivo: int a=1 equivale a Integer a = 1
non sono necessari i ; alla fine dell’istruzione
overload degli operatori per gli oggetti. Ad esempio si potrà scrivere indifferentemente l’operatore == tra due int o due Integer, o addirittura tra un int e un Integer:
int a=2 // in Java sarebbe un tipo primitivo
print(a)
Integer b=2 // in Java sarebbe una classe
print(a.equals(b)) // stampa true
print(a==b) // stampa true
Per testare costrutti Groovy si può utilizzare la consolle che si può far partire da IDE selezionando Tools > Groovy console:
Uso della console per scivere programmi Groovy…
e cliccando sulla freccetta bianca in alto a sinistra si compila ed esegue il programma:
… ed esecuzione su console di programmi Groovy.
Tips on Groovy programming
Ci sono alcune pratiche utili che consentono di sviluppare in modo coerente.
Io sono partito da questa esigenza: voglio che ogni modello (o dominio come viene chiamato in Grails) debba possedere uno standard, ad esempio voglio che tutti i modelli siano corredati dai campi ch riportano lo username del creatore dell’oggetto, e i timestamp di creazione e modifica.d
Grails ha il notevole supporto ai template per ogni oggetto del paradigma MVC: dai model, ai controller alle alle view.
Per installare nella propria applicazione i template si ricorre al comando
Ho sperimentato per prima la prima modalità IDE, creando l’applicazione Test1:C
Creare un nuovo progetto con Intelij IDEA
Il wizard richiede di impostare la versione di Grails da utilizzare, la Java Virtual Machine che eseguirà la compilazione e il tipo dell’applicazione (se app o se plugin)
Per questa applicazione uso Java 1.8 come JVM e Grails 2.3.11 per allinearmi con le versioni utilizzate dal Cliente. Alla fine mi viene chiesto il nome dell’applicazione:
Parte il build dell’applicazione utilizzando la versione di Grails 2.3.1 selezionata:
/usr/lib/jvm/jdk1.8.0_111/bin/java -Dgrails.home=/home/marcob/.sdkman/candidates/grails/2.3.11 -Dtools.jar=/usr/lib/jvm/jdk1.8.0_111/lib/tools.jar -Dgroovy.starter.conf=/home/marcob/.sdkman/candidates/grails/2.3.11/conf/groovy-starter.conf -Dbase.dir=/home/marcob/IdeaProjects/Test1 -Dfile.encoding=UTF-8 -classpath /home/marcob/.sdkman/candidates/grails/2.3.11/lib/org.codehaus.groovy/groovy-all/jars/groovy-all-2.1.9.jar:/home/marcob/.sdkman/candidates/grails/2.3.11/dist/grails-bootstrap-2.3.11.jar org.codehaus.groovy.grails.cli.support.GrailsStarter --main org.codehaus.groovy.grails.cli.GrailsScriptRunner --conf /home/marcob/.sdkman/candidates/grails/2.3.11/conf/groovy-starter.conf "idea-print-project-settings -plain-output"
|Loading Grails 2.3.11
|Configuring classpath
.
|Environment set to development
....
|Installing zip scaffolding-2.0.3.zip…
...
|Installed plugin scaffolding-2.0.3
..........
---=== IDEA Grails build settings ===---
#
Tue Aug 13 12:12:01 CEST 2019
grails.work.dir=/home/marcob/.grails/2.3.11
grails.project.work.dir=target/work
....
Compile.0=/home/marcob/.sdkman/candidates/grails/2.3.11/lib/org.codehaus.groovy/groovy-all/jars/groovy-all-2.1.9.jar
Compile.1=/home/marcob/.sdkman/candidates/grails/2.3.11/dist/grails-plugin-rest-2.3.11.jar
....
Runtime.0=/home/marcob/.sdkman/candidates/grails/2.3.11/lib/org.codehaus.groovy/groovy-all/jars/groovy-all-2.1.9.jar
Runtime.1=/home/marcob/.sdkman/candidates/grails/2.3.11/dist/grails-plugin-rest-2.3.11.jar
....
Test.0=/home/marcob/.sdkman/candidates/grails/2.3.11/lib/javax.servlet/javax.servlet-api/jars/javax.servlet-api-3.0.1.jar
Test.1=/home/marcob/.sdkman/candidates/grails/2.3.11/lib/org.codehaus.groovy/groovy-all/jars/groovy-all-2.1.9.jar
....
Provided.0=/home/marcob/.sdkman/candidates/grails/2.3.11/lib/javax.servlet/javax.servlet-api/jars/javax.servlet-api-3.0.1.jar
Build.0=/home/marcob/.sdkman/candidates/grails/2.3.11/lib/xalan/serializer/jars/serializer-2.7.1.jar
Build.1=/home/marcob/.sdkman/candidates/grails/2.3.11/dist/grails-bootstrap-2.3.11.jar
....
---=== End IDEA Grails build settings ===---
Ometto per comodità la maggior parte del log che non presenta messaggi d’errore.
Alla fine l’applicazione è creata e viene popolato l’albero dei file all’interno della directory
Si seleziona innanzitutto il tipo di progetto: Applicazione o plugin: la stessa possibilità c’è nella IDE ma per iniziare partiamo dalle Applicazioni.
Si sceglie il nome dell’applicazione e la versione di Grails: purtropppo le versioni selezionabili non sono vecchie come quelle che usa il mio Cliente quindi scelgo la 3.2.13 (che ho anche a bordo della mia macchina):
Come si vede, ho installato le versioni 2.1.5, 2.2.3, 2.3.11, 3.2.13 e quest’ultima è quella preselezionata nella sessione di shell in cui ho lanciato il comando
Tra le features, lascio le scelte standard: Hibernate5 in particolare è lo strato software che permette di astrarre dal livello database (ORM, Object Relationship Mapping).
Premendo il pulsante Generate viene scaricato il package myapp.zip:
Lo scarico e unzippo dentro a ~/IdeaProjects/Test1. Dopo di che lo apro con la IDE e questa si deve riscaricare tutti i package dipendenti, ad esempio Gradle (che è il tool per fare il build della applicazione che manca nel file zip)
Download https://services.gradle.org/distributions/gradle-3.4.1-bin.zip (70,31 MB)
Download https://services.gradle.org/distributions/gradle-3.4.1-bin.zip finished succeeded, took 57 s 539 ms
Gradle Daemon started in 840 ms
Download https://repo.grails.org/grails/core/org/grails/grails-gradle-plugin/3.2.13/grails-gradle-plugin-3.2.13.pom
...
Creazione da linea di comando
L’ultimo modo per creare l’applicazione è da linea di comando. Essa assomiglia molto alla modalità php artisan per creare le applicazioni in PHP/Laravel. Innanzitutto si scarica SDKMan:
Dopo di che si può sviluppare con l’IDE che si vuole.
Build and Go!
Una volta terminata la fase di startup dell’applicazione è il momento di programmare e costruire l’applicazione vera e propria. Ma a questo punto siamo già davanti ad una applicazione di base funzionante. Usiamo gradle (come artisan) e facciamo il build aprendo un terminale da dentro la directory del progetto
marcob@jsbach[16:12:15]:myapp$ ./gradlew bootRun
Starting a Gradle Daemon (subsequent builds will be faster)
:compileJava NO-SOURCE
:compileGroovy
:buildProperties
:processResources
:classes
:findMainClass
:bootRun
Grails application running at http://localhost:8080 in environment: development
Building 85% > :bootRun
Quando lo script ha raggiunto la fase “running”, puntare il browser all’indirizzo http://localhost:8080
Come si presenta l’applicazione Groovy on Grails una volta avviata
Attenzione! c’è un comando in più da dare: prima di avviare il build, rendere eseguibile lo script gradlew:
marcob@jsbach[16:11:48]:myapp$ chmod a+x gradlew
Importazione in Intellij IDEA
Gradle Daemon started in 1 s 722 ms
CONFIGURE SUCCESSFUL
Total time: 10.864 secs
Manifest.writeTo(Writer) has been deprecated and is scheduled to be removed in Gradle 4.0. Please use Manifest.writeTo(Object) instead.
wget è un programma Linux che effettua il download di una risorsa web agendo allo stesso modo dei crawlers dei motori di ricerca e la deposita in un file in locale nel computer in cui si è lanciato il comando.
Digitando
$ wget https://www.mysite.org
scarico in locale la pagina indice:
marcob@jsbach:mysite$ ll
totale 20
drwxr-xr-x 2 marcob marcob 4096 ago 7 15:01 ./
drwxr-xr-x 4 marcob marcob 4096 ago 7 15:00 ../
-rw-r--r-- 1 marcob marcob 11395 ago 7 15:01 index.html
Se c’è una gerarchia di directory posso scaricare anche quella, cioè scaricare tutti i file le directory e le sottodirectory, con l’opzione -r (recursive) e specificando il numero di livelli di gerarchia a cui voglio scendere al massimo; per esempio se voglio fermarmi al terzo:
$ wget -r -l3 https://www.mysite.org
Spesso però i siti vengono impostati in modo da bloccare questo tipo di download massivo, ma wget è molto potente: possiamo inviare nella richiesta anche degli header HTTP personalizzati con l’opzione ‐‐header, ad esempio fingendoci un browser vero:
$ wget -r -l3 --no-parent --header="Accept: text/html" --user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0" https://www.mysite.org
Tuttavia il web server può sempre bloccare lo user agent quando il numero di pagine richieste al secondo è troppo elevata. Con queste opzioni ci presentiamo al server come se fossimo un browser Firefox per sistema operativo Mac OS X. Inoltre specificando ‐‐no-parent evitiamo di fare il download anche delle eventuali cartelle soprastanti quella di cui vogliamo fare il download.
Come ultimo esempio, potendo inviare header HTTP a piacere, possiamo inviare l’header di autenticazione per siti protetti con autenticazione. Ovviamente dobbiamo avere un account in quel sito e possiamo evitare di mandare in chiaro la password in http utilizzando l’opzione ‐‐ask-password:
$ wget -r -l10 --no-parent --user=myuser --ask-password --header="Accept: text/html" --user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0" http://www.mysite.org
Un utilizzo un po’ meno banale di quello illustrato nel primo post sulla clausola WITH è quello che alla fine ho utilizzato per riuscire in un task del genere:
Come faccio a produrre un recordset contente le date di una settimana da domenica a sabato dato che sia un giorno qualsiasi della settimana che si vuole estrarre?
Utilizzando la clausola WITH possiamo costruirci una tabella temporanea arr_dates con un’unica colonna arr_date:
Oracle: Esempio di uso della clausola WITH
Ora questo costrutto ha una sua complessità. Partiamo individuando i blocchi:
la clausola WITH definisce un recordset temporaneo arr_dates
contente il campo arr_date
il recordset è definito come segue: seleziona (riga 2) il sabato successivo (+5) al parametro data (:1)
union all
seleziono (riga 4) il giorno precedente (-1) dal recordset definito sopra con la condizione che la data deve essere maggiore della data di inizio della settimana della data parametro (:1); questa condizione è soddisfatta da tutti i giorni dall’inizio della settimana di partenza alla domenica successiva.
alla fine (riga 9) c’è una semplice selezione sull’oggetto appena costruito
Quindi l’effetto è quello di selezionare 7 “record” centrati attorno al giorno :1. Ad esempio se prendiamo come parametro il 2 luglio 2019 ho
Stesso recordset se come parametro scegliamo il 5 luglio 2019. Se invece scegliamo come “centro” il 10 luglio 2019:
Alla fine possiamo calcolare questi recordset inglobando il tutto in una function ma lo vediamo nel prossimo post.
Oracle with: questa clausola, introdotta a partire dalla versione Oracle 9.2, consente di eseguire una query in una tabella utilizzando come parametro il risultato di un’altra query.
Come esempio immaginiamo di avere una tabella di rilievi di temperature e voler selezionare i record in cui i valori sono superiori alla media.
create table temperature(
id number(10),
valore number(10,2),
data_rilievo timestamp
);
Aggiungiamo un po’ di dati:
ID
VALORE
TIMESTAMP
1
24,3
01-LUG-19 10:26:15,820000000
2
28,2
02-LUG-19 10:26:43,310000000
3
34,8
03-LUG-19 10:26:57,810000000
4
28,7
04-LUG-19 10:27:15,819000000
5
32,5
05-LUG-19 10:27:30,714000000
Una possibile soluzione per questo problema è la query seguente:
select valore from temperature
where valore > (
select avg(valore) from temperature
)
che da’ il seguente recordset
VALORE
34,8
32,5
Con la clausola WITH costruiamo una tabella temporanea con una sola colonna e una sola riga che contiene il valore medio. Potremo successivamente riferirci al valore contenuto in questo recordset per ritornare il recordset ottenuto con la query standard di prima:
WITH tmpTemperature (tmpMedia) as
(SELECT avg(valore) FROM Temperature)
SELECT valore
FROM Temperature, tmpTemperature
WHERE Temperature.valore > tmpTemperature.tmpMedia;
In sostanza si tratta di definire l’oggetto temporaneo che è un recordset con una riga e una colonna; poi si esegue la query sulla tabella delle temperature utilizzando la tabella temporanea definita in virtù della clausola WITH.
Il recordset ritornato è identico a quello di prima:
VALORE
34,8
32,5
Quindi fondamentalmente la sintassi della clausola WITH è
WITH tabellaTemporanea (parametro) AS
(select di calcolo del parametro)
SELECT *
FROM tabella1, tabella2, ..., tabellaTemporanea
WHERE
... condizione su tabellaTemporanea.parametro...
;
Questo è quello che si osserva una volta che si preme il pulsante Invia del modulino, dopo aver selezionato l’immagine. In particolare, di tutto il messaggio HTTP che viene compilato dal browser ho qui riportato l’inizio della parte contenente l’immagine, che è l’informazione binaria che vogliamo capire come viaggia in rete.
In particolare possiamo notare, leggendo la colonna centrale del dump riportato da Wireshark, che in HTTP non passa solo testo ma anche informazioni puramente binarie. I singoli bytes sono a volte superiori a 7F (=011111112) – e i caratteri codificati in ASCII standard sono a 7 bit – per cui si tratta di bytes a 8 bit (con il bit più significativo a 1) e non 7 (cioè con il bit più significativo a 0). Non c’è inoltre alcuna applicazione di una codifica base64 ai bytes che compongono il messaggio.
4.3 Encoding
While the HTTP protocol can transport arbitrary binary data, the
default for mail transport is the 7BIT encoding. The value supplied
for a part may need to be encoded and the "content-transfer-encoding"
header supplied if the value does not conform to the default
encoding.
Quindi, a imitazione del protocollo SMTP, si potrebbe codificare il file (che nella mail è l’allegato) utilizzando la codifica base64, ma in questo caso la codifica adottata è 8 bit. La scelta della codifica è ampia, come si desume dalla grammatica di encoding riportata nella rfc-1521:
In sintesi: il browser costruisce un file che contiene tutte le parti (multipart) contenute nei campi della form (multipart/form-data in questo caso solo due, l’immagine ed il pulsante di invio); in questo file tutto ciò che è testo rimane testo e ciò che è binario rimane binario; ogni elemento del form è separato dagli altri da un delimitatore (lo potete notare all’inizio della colonna di destra nell’immagine della cattura Wireshark):
——WebKitFormBoundaryXxWGBqDWd6OsDPE2
(per questo il tipo di encoding, o enctype, utilizzato dal browser è il multipart/form-data)
Le informazioni di intestazione che precedono il messaggio HTTP sono
Un fastidioso errore 404 mi risultava dalla visita di uno dei miei siti (che utilizzano come http server nginx su sistema operativo CentOS), e mi sembrava che l’attribuzione dei permessi fosse corretta.
L’errore HTTP 404 viene emesso dal server web quando non trova nel file system la risorsa (file o cartella) corrispondente all’URI che gli viene passato dal client (ad esempio Google Chrome o Firefox): il famoso errore di Pagina non trovata.
Questo errore si verifica anche se il file c’è ma si verifica una di queste condizioni:
l’utente sotto il quale sta girando il server non ha permessi di lettura sul file desiderato né come utente né come gruppo
non ha la possibilità di raggiungere quella risorsa percorrendo il grafo della directory da / al file desiderato
In realtà basta infatti che una directory della catena di “cd command” (change directory) che il servizio web deve fare non sia leggibile e si verifica l’errore. Il modo più efficiente di vedere lo stato di tutta la gerarchia di directory è utilizzare il comando namei:
namei - follow a pathname until a terminal point is found
Qui è evidente il problema: tutta la catena di directory ha l’opzione x (eXecute) tranne la directory contenente la DocRoot del web server (la directory html) ragion per cui è sufficiente aggiungere questo permesso:
Utilizziamo tecnologie come i cookie per memorizzare e/o accedere alle informazioni del dispositivo. Lo facciamo per migliorare l'esperienza di navigazione e per mostrare annunci personalizzati. Il consenso a queste tecnologie ci consentirà di elaborare dati quali il comportamento di navigazione o gli ID univoci su questo sito. Il mancato consenso o la revoca del consenso possono influire negativamente su alcune caratteristiche e funzioni.
Funzionale
Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici.L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.
Commenti recenti