ACG Vision4 Service Bus
Transcript
ACG Vision4 Service Bus
ACG Vision4 Service Bus Guida Tecnica Componenti Architetturali © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 1 di 167 Quarta Edizione (16 Gennaio 2014) Questa edizione si riferisce alla Versione 1 Rilascio 4 livello di modifica 0 di ACG Service Bus (5733-F13) e a tutti i successivi rilasci e modifiche, se non altrimenti indicati in nuove edizioni o lettere di accompagnamento. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 2 di 167 Note e Trademarks © Copyright ACG S.r.l. 2014. Tutti i diritti riservati. ACG e ACG Vision4 sono marchi di ACG S.r.l., con socio unico e soggetta all’attività di direzione e coordinamento di TeamSystem S.p.A., sede legale in in via Yuri Gagarin, 205 – 61122 Pesaro (PU), Cap. Soc. € 100.000 i.v., codice fiscale e iscrizione al Registro delle Imprese di Milano n. 08419500965 (di seguito “ACG”) - Tutti i diritti riservati. I marchi e loghi riportati nel presente documento diversi da ACG e ACG Vision4 (ivi inclusi, a mero titolo esemplificativo e non esaustivo, IBM, il logo IBM, Adobe, il logo Adobe, PostScript, il logo PostScript, Intel, il logo Intel, Intel Inside, il logo Intel Inside, Intel Centrino, il logo Intel Centrino, Celeron, Intel Xeon, Intel SpeedStep, Itanium e Pentium, Linux, Microsoft, Windows, Windows NT e il logo di Windows, UNIX, Java, Novell, il logo Novell, openSUSE e il logo openSUSE, AS/400, BladeCenter, Cognos, DB2, DB2 Universal Database, eServer, i5/OS, iSeries, OpenPower, OS/400, POWER, Power Systems, pSeries, Rational, System i, System i5, System p, System p5, System Storage, System x, WebSphere, etc.) sono di titolarità di soggetti terzi. ACG rispetta i diritti di proprietà intellettuale di terzi. Tutti i contenuti del presente documento e i diritti ad essi correlati sono riservati. Tali contenuti pertanto possono essere consultati esclusivamente per finalità d’informazione personale, essendo espressamente vietato ogni diverso utilizzo senza il preventivo consenso scritto di ACG. Sebbene sia stata usata ogni ragionevole cura nel raccogliere e presentare le informazioni contenute nel presente documento, nessuna garanzia è prestata in ordine alla loro esattezza, completezza, utilità, né ai loro possibili impieghi da parte degli utenti; è pertanto esclusa ogni responsabilità di ACG per errori, inesattezze od omissioni relative a dette informazioni. I contenuti del presente documento sono soggetti a continuo aggiornamento e sono da ritenersi puramente indicativi e suscettibili di eventuali errori e/o imprecisioni. ACG può introdurre miglioramenti e/o variazioni ai prodotti e/o programmi descritti nel presente documento in qualsiasi momento e senza preavviso. Il presente documento può contenere informazioni che riguardano programmi e propositi futuri, che vengono descritti di volta in volta mediante l’utilizzo di termini come "attendersi", "stimare", "prevedere", "prospettare" e "programmare". Tali dichiarazioni per loro natura non comportano alcun impegno a carico di ACG, che pertanto non assume in relazione ad essi alcuna responsabilità di realizzazione. Qualunque riferimento a siti web diversi da www.acginfo.it è fornito a solo titolo esemplificativo e non costituisce invito all’utilizzo e/o navigazione. I contenuti dei siti web referenziati non sono parte dei prodotti ACG e il loro eventuale utilizzo da parte dell’utente è effettuato a suo esclusivo rischio. Le informazioni relative a prodotti non ACG contenute nel presente documento sono fornite dai rispettivi fornitori, dagli annunci pubblicitari e da informazioni liberamente disponibili. ACG non ha collaudato tali prodotti e non può confermarne l’accuratezza delle prestazioni, la compatibilità con i prodotti ACG o qualunque altra caratteristica. Qualunque richiesta sulle caratteristiche operative dei prodotti non ACG deve essere rivolta direttamente ai rispettivi fornitori. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 3 di 167 Fatti salvi i danni causati da dolo o colpa grave, ACG non assume nessuna responsabilità circa i contenuti del presente documento. In particolare, tali contenuti non rappresentano una promessa o garanzia relativa all’idoneità a determinati scopi dei prodotti ACG oppure alla non violazione, da parte dei prodotti ACG stessi, di leggi di qualsivoglia natura. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 4 di 167 Indice NOTE E TRADEMARKS ....................................................................... 3 TABELLA DEGLI AGGIORNAMENTI .............................................. 8 1. INTRODUZIONE ............................................................................. 9 2. COMPONENTE VIEW ................................................................. 11 2.1 INTRODUZIONE ........................................................................................................................ 11 2.2 CARATTERISTICHE DI BASE...................................................................................................... 12 2.3 DIZIONARIO ............................................................................................................................. 17 2.4 COMPORTAMENTI GENERALIZZATI .......................................................................................... 19 2.4.1 Response dal server ...................................................................................................... 22 2.4.2 Funzioni di Base............................................................................................................ 23 2.4.3 Contesto e pre/postActions............................................................................................ 25 2.4.4 Accessibilità .................................................................................................................. 26 2.4.5 Messaggi ....................................................................................................................... 27 2.4.6 Menu contestuale .......................................................................................................... 27 2.5 COMPONENTI ........................................................................................................................... 29 2.5.1 Window.......................................................................................................................... 30 2.5.2 Panel ............................................................................................................................. 33 2.5.3 Field .............................................................................................................................. 38 2.5.4 XAction .......................................................................................................................... 44 2.5.5 Liste ............................................................................................................................... 48 2.5.5.1 Referenced Objects ............................................................................................... 53 2.5.5.2 Navigazione .......................................................................................................... 56 2.5.5.3 Liste con selezione multipla .................................................................................. 60 2.5.5.4 Rielaborazione Liste ............................................................................................. 62 2.5.6 Componente HeaderRows ............................................................................................. 68 2.5.6.1 Definizione di un oggetto HeaderRows ................................................................ 68 2.5.6.2 Gestione di una lista HeaderRows lato Controller ................................................ 68 2.5.6.3 Response xml ........................................................................................................ 68 2.5.6.4 Caricamento iniziale delle righe della lista HeaderRows ..................................... 69 2.5.6.5 Formattazione della stringa dati da inviare alla lista............................................. 69 2.5.6.6 Inserimento o modifica di una riga della lista ....................................................... 72 2.5.6.7 Cancellazione di una riga ...................................................................................... 73 2.5.6.8 Gestione del pannello piede .................................................................................. 74 2.5.6.9 Gestione di più liste nella stessa response ............................................................ 75 2.5.6.10 Label dinamiche .................................................................................................... 76 2.5.6.11 Modifica del menu di una riga .............................................................................. 76 2.5.6.12 Aggiunta di una riga in posizione successiva alla riga corrente ........................... 76 © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 5 di 167 2.5.6.13 Creazione di una finestra tramite menu contestuale ............................................. 76 2.5.7 Lista Editabile ............................................................................................................... 78 2.5.7.1 Dblclick ................................................................................................................. 78 2.5.7.2 Onedit .................................................................................................................... 78 2.5.7.3 Xsend .................................................................................................................... 79 2.5.7.4 Riga in errore ........................................................................................................ 79 2.5.7.5 Riga in editazione.................................................................................................. 79 2.5.7.6 Combo dinamica su lista editabile ........................................................................ 80 2.5.8 Drilldown ...................................................................................................................... 83 2.5.9 Componente Tree View ................................................................................................. 85 2.6 SKIN E FONT ............................................................................................................................ 89 2.7 FAQ ........................................................................................................................................ 95 2.8 TOOL DI NAVIGAZIONE .......................................................................................................... 105 3. COMPONENTE CONTROLLER .............................................. 111 3.1 INTRODUZIONE ...................................................................................................................... 111 3.2 INTEGRAZIONE VELOCITY – STRUTS IN ACG SERVICE BUS .................................................. 111 3.2.1 Benefici........................................................................................................................ 112 3.3 ACTION E FORM BEAN IN ACG SERVICE BUS ....................................................................... 115 3.3.1 ACGDynaActionForm ................................................................................................. 115 3.3.2 ACGDispatchAction .................................................................................................... 116 3.3.3 Global forwards .......................................................................................................... 118 3.3.4 Alcuni metodi di ACGDispatchAction ........................................................................ 118 3.4 CHAIN OF RESPONSIBILITY .................................................................................................... 119 3.5 CONFIGURAZIONE IN MODULI ................................................................................................ 119 3.6 ACGVALIDATOR .................................................................................................................. 120 3.7 MESSAGGI APPLICATIVI ......................................................................................................... 121 3.7.1 La classe BaseException ............................................................................................. 121 3.7.2 Catalogo messaggi ...................................................................................................... 121 3.7.3 Gestione dei messaggi in struts................................................................................... 122 4. COMPONENTE MODEL ........................................................... 123 4.1 INTRODUZIONE ...................................................................................................................... 123 4.2 UTILIZZO DELLA FRAMEWORK HIBERNATE IN ACG SERVICE BUS ........................................ 124 4.2.1 Session Factory e Configurazione .............................................................................. 125 4.3 LEGACY CONNECTOR ............................................................................................................ 127 4.3.1 Diagramma architetturale .......................................................................................... 128 4.4 STRUTTURA DELLE TABELLE TEMPORANEE E DI LOG DELL’ELABORAZIONE .......................... 132 4.5 AMMINISTRAZIONE DELLE TABELLE TEMPORANEE ................................................................ 133 4.6 GENERAZIONE CODICE JAVA PER LA LEGACY PROGRAM INTERFACE ..................................... 134 4.6.1 Lancio del code generator .......................................................................................... 136 4.7 ACCESSO TRAMITE SERVIZIO WEB ......................................................................................... 139 4.8 REENGINEERING DEI PROGRAMMI LEGACY ............................................................................ 140 4.9 TABLE MANAGER .................................................................................................................. 143 4.9.1 Utilizzo tramite file di properties................................................................................ 143 4.9.2 Utilizzo file di properties a runtime ............................................................................ 147 © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 6 di 167 5. LINEE GUIDA PER SVILUPPO FUNZIONALITÀ VISION4149 5.1 5.2 ESECUZIONE OPERAZIONI IN BATCH ..................................................................................... 149 MODALITÀ DI RICHIESTA ESECUZIONE REPORT DI IBM COGNOS TRAMITE ACG SERVICE BUS 161 5.3 PERSONALIZZAZIONI IN ARCHITETTURA ACG VISION4 ......................................................... 164 5.3.1 Personalizzazione dello startup della web-application .............................................. 164 5.3.2 Personalizzazione della User Interface – posizionamento dizionari .......................... 165 5.3.3 Personalizzazione del richiamo dei report Cognos .................................................... 166 5.3.4 Gestione tabelle utente nei file ANTA200F, ASTA300F, ANTB300F, ASTB300F ..... 167 © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 7 di 167 Tabella degli aggiornamenti Data PTF id 21/06/2011 SVB140031D 13/07/2011 SVB140034D 16/01/2014 SVB140108D Descrizione Aggiunto il paragrafo "2.6 Skin e Font" che descrive la modalità di personalizzazione dell’aspetto grafico dell’applicazione ACG Vision4 Aggiunti i paragrafi 4.9 “TableManager” e 5.3.4 “Gestione tabelle utente in ANTA200F, ASTA300F, ANTB300F e ASTB300F Modifiche al paragrafo 2.4.4 Accessibilità. Aggiunto 2.5.5 Liste - Formato visualizzazione liste © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 8 di 167 1. Introduzione ACG Service Bus implementa un modello di sviluppo basato sullo standard open dell’MVC, ovvero del Model, View e Controller, in base al quale le applicazioni vengono scritte secondo un’architettura multilivello in cui ogni livello è logicamente separato rispetto agli altri. Per Model si intende la rappresentazione dei dati di business dell’applicazione e le regole con cui tali dati vengono interrogati, modificati o gestiti. Per View si intende la componente responsabile della visualizzazione dei dati della componente Model: i dati del Model possono essere visualizzati utilizzando viste diverse. Per Controller si intende la componente responsabile dell’interazione tra View e Model: essa interpreta le richieste della View, che non sono altro che le richieste dell’utente dell’applicazione, inducendo azioni corrispondenti nella parte Model, selezionando la nuova View da visualizzare o se del caso aggiorna la View stessa da cui è partita la richiesta. In particolare, in ACG Service Bus il paradigma Model-View-Controller è stato così implementato: La parte di View, responsabile della presentazione dei dati verso l’utente, è realizzata interamente secondo le tecnologie Ajax e viene sviluppata sfruttando la framework open source Dojo. E' costituita dal motore di gestione delle form (ACG Form Handler) che si occupa di gestire gli aspetti di rendering utilizzando DHTML (codice HTML dinamico) e di interattività utilizzando Ajax per la comunicazione asincrona con il server web: la View è costruita sulla base delle informazioni contenute nei dizionari visuali (Data Dictionary). La parte di Controllo è stata realizzata estendendo la framework Struts integrata con l’OSS Velocity. La parte Model si può implementare sviluppando codice in java basato sulla framework Hibernate per gestire l’accesso ai dati oppure riutilizzando programmi legacy Rpg congiuntamente a codice di interfacciamento con Java (Java Object Wrappers). © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 9 di 167 La logica di interazione tra le componenti costituenti l’applicazione è la seguente: dal desktop ACG Service Bus, nel quale si integrano le applicazioni ACG Vision4, viene richiesta l’esecuzione di un’operazione (il cui risultato è la visualizzazione di una finestra con i dati relativi) il Form Handler ricava dai dizionari visuali la struttura della finestra video e richiama l’URL ad essa associato (richiamo del metodo di una Action di Struts) viene verificato dal componente di autorizzazione che l’utente sia abilitato all’esecuzione dell’operazione: in caso negativo viene restituito XML di errore al Form Handler viene eseguito il metodo della Action di Struts che richiama la Business Logic e ritorna XML di risposta contenente i dati del Model da visualizzare ed eventuali errori la componente UIEngine interpreta la risposta xml ed aggiorna di conseguenza la finestra video © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 10 di 167 2. Componente view 2.1 Introduzione La UI (UserInterface) dei prodotti ACG Vision4 si basa sull’uso di un dizionario come repository per le sue componenti (finestre, pannelli, campi,…). Le interfacce sono costruite dinamicamente sul browser da un runtime javascript (denominato uiengine) creato appositamente per l’architettura ACG Vision4, in base alle informazioni presenti nel dizionario. Le interazioni con la parte server dell’applicazione sono, di norma, richieste asincrone di tipo AJAX, raccolte ed elaborate da classi Java che restituiscono un response con un formato xml. Tale risposta viene costruita automaticamente per mezzo di un meccanismo trasparente (basato sui tools VelocityStruts) che accede agli attributi della classe Action della piattaforma STRUTS e all'eventuale lista dei messaggi generati, formattandoli secondo dei template (output.vm, check.vm,…). Essa viene rimandata al browser dove l’uiengine l’interpreta e aggiorna l’interfaccia. Alcuni comportamenti sono/saranno resi disponibili nell’uiengine e potranno attivarsi in base alle proprietà definite nel dizionario, in base al contesto operativo (inserimento,visualizzazione,…), al contesto applicativo. L’uiengine fa uso della piattaforma open-source DOJO per la comunicazione fra browser e per la creazione di alcuni componenti di base (FloatingPane, Accordion,…) o loro estensioni. La UI è inoltre personalizzabile da parte dell’utente finale nella sua impostazione grafica generale (sfondi, icone, colori) scegliendo fra i temi (skin) disponibili e in altri aspetti legati all’usabilità quali tipo e dimensione font, modalità di attivazione (mediante click o movimento mouse,…) © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 11 di 167 2.2 Caratteristiche di base Pannelli di logon Implementano il Single Sign-On (SSO), memorizzano le informazioni sull’ultimo utente collegato, la lista dei suoi sistemi informativi e quello usato per ultimo. Desktop L’ambiente di lavoro è composto da diverse sezioni in cui vengono lanciate le nuove funzioni ACG Vision4 caratterizzate da una nuova interfaccia grafica. Il desktop stesso è definito nel dizionario Menubar Toolbar Nel desktop sono presenti i seguenti componenti: Menubar (componente ad-hoc ACG Vision4) © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 12 di 167 Posizionato sulla sinistra contiene le voci di partenza di un menu a tendina. I sottomenu sono elementi ad albero creati dinamicamente a partire da dati provenienti dal database ACG e/o dai file di personalizzazione (vedi Personalizzazione) Il menu a tendina esploso viene “memorizzato” e riproposto al successivo posizionamento del mouse sull’area della menu bar . Possibilità nelle voci di menu di segni di spunta per attivare o meno un comportamento. E’ basato sulle voci contenute nel dizionario di tipo NAV_ e parte da un nodo radice del tipo diz['NAV_TOP']='ACG|USR|LAST|TOOLS|HELP'; che rappresenta i nodi di base del menu a tendina. La struttura ad albero si sviluppa definendo per ogni nodo (che rappresenta un menu) i sottonodi che possono essere ancora nodi o azioni. Per quest’ultime bisogna definire l’etichetta a video (label) e il comando da eseguire. L’albero delle azioni di ACG Vision4 è quindi la composizione delle voci del dizionario di tipo NAV_ , provenienti da dizmenu.js dall’esecuzione di appmenu.jsp (per le voci delle ACG WebEdition) infine, se presenti, nella cartella <root>/customized/ dai file (opzionali) menu_<utente>_<sistemainformativo>.js ext_<utente> che permettono di integrare/sovrascrivere le voci caricate dai file precedenti. Esempio Sotto il nodo TOOLS di NAV_TOP è presente il sottonodo SETTINGS con le sue label in lingua diz['NAV_TOOLS']='..|SETTINGS|…'; diz['LBL_IT_SETTINGS']="Impostazioni"; diz['LBL_EN_SETTINGS']="Settings"; diz['NAV_SETTINGS']=' VIEWINFO |…|…|'; Il nodo “VIEWINFO” è un’azione che se invocata esegue in comando javascript diz['LBL_IT_VIEWINFO']="Visualizza info"; diz['LBL_EN_VIEWINFO]="View info"; diz["CMD_VIEWCOOKIE"]="js:<comando visualizza info>;" © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 13 di 167 se l’azione lancia una gestione ACG Vision4: diz["CMD_4RdP"]="js:xcrtWIN('F0','WIN_RdP');" Richiamo di applicazioni/siti esterni in finestra ACG Vision4 Possibilità di aprire url di applicazioni o siti web in finestra separata o in finestra ACG Vision4; ciò è possibile indicando come azioni nel menu URL:<url> Se si desidera in finestra esterna, WIN:<url> Se si vuole aprire l’url in una finestra del desktop. In questo modo è possibile “integrare” a livello grafico le gestioni non ACG Vision4nel desktop (create con jsp, jsf,…) Un limite è nella compatibilità delle gestioni integrate, queste devono consentire il lancio in sottofinestra del browser e non eseguire operazioni sul documento al livello superiore. Esempio: diz['LBL_IT_4EXTLINK']="Collegamento esterno ACGINFO in finestra separata"; diz['LBL_EN_4EXTLINK']="External link (separate window)"; diz["CMD_4EXTLINK"]="URL:http://www.acginfo.it" diz['LBL_IT_4EXTLINKV4']="Collegamento esterno ACGINFO in finestra ACG VISION4"; diz['LBL_EN_4EXTLINKV4']="Link to ACGINFO in a ACG VISION4window"; diz["CMD_4EXTLINKV4"]="WIN:http://www.acginfo.it" Toolbar (componente ad-hoc ACG Service Bus) Contenente una serie di bottoni funzionali, completamente personalizzabile (vedi Personalizzazione) Esempio: desktop ACG Vision4con 2 topbar diz["TOPBAR0"] = '{"actions":[ "ACT_REFRESHDESKTOP","ACT_NEWDESKTOP" ] }'; diz["TOPBAR1"] = '{ "actions":["ACT_CLIENTSETTINGS","ACT_DUPFRM","ACT_EXCEL","ACT_WORD","ACT_MAI L" ] }'; diz["LBL_EN_DESKTOP"]="ACG Vision4"; diz["LBL_IT_DESKTOP"]="ACG Vision4"; diz["DESKTOP"]='{ "label":"DESKTOP" ' +','+ ' "toolbars":[ '+ $d("TOPBAR0") + ','+ $d("TOPBAR1") + ‘ ] } '; Toolbar © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 14 di 167 Window funzioni ACG Service Bus Window Taskbar Sono finestre che attivano funzioni applicative sviluppate usando l’architettura ACG Vision4. Sono indipendenti e possono essere lanciate in maniera multipla anche sulla stessa funzione; hanno le consuete caratteristiche di minimizzazione, massimizzazione, ridimensionamento; prevedono un controllo alla chiusura in presenza di modifiche non salvate. Sono composte da: a. Finestra (estensione del FloatingPane di DOJO) b. Pannelli (dictionary-based) c. Pannello Tabbed (dictionary-based) d. Pannello Lista (pf-based) e. Componente TestataRighe (componente ad-hoc ACG Service Bus) f. ReferencedObject, ovvero funzione ? di ACG (dictionary/pf-based) g. Calendario (estensione del componente DOJO con festivi configurabili) h. Componente note TaskBar Posizionata nella parte inferiore, realizzato come estensione del componente DOJO (ACGTaskBar) elenca le finestre attive, con titolo troncato (visibile nella forma completa posizionando il mouse) e completo di una icona che consente di lanciare la chiusura di tutte le finestre ACGv4 presenti nella TaskBar. Altre funzionalità disponibili nel desktop © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 15 di 167 Nel menù Strumenti sono a disposizione una serie di funzionalità: Funzionalità di log A partire dal menù ad albero Strumenti -> Debug -> Raccolta informazioni -> Stampa Log è attivabile la funzione di log sul client, avente le seguenti caratteristiche: Il Log viene visualizzato in una finestra separata Molto spazio a disposizione Possibilità di ricerca con diverse modalità Possibilità di abilitare o meno l’emissione dei messaggi Barra per filtrare i messaggi in base alla categoria o Error (messaggi tipo ERR…) o Warn (messaggi tipo WARN…) o Info (altri messaggi, esempio <response>…) o Debug (tipo non definito) A partire dal menù suddetto è possibile anche Cancellare il Log ed Eliminare il Limite del Log disabilitando così il limite massimo al log. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 16 di 167 Cambio di Sistema informativo E’ possibile a partire dal Menu Preferiti lanciare la funzione di Cambio di Sistema Informativo che permette all’utente di passare a un Sistema Informativo diverso da quello corrente (scelto fra quelli a lui disponibili). Viene richiesta conferma dell’operazione e tutte le finestre sono chiuse. Controllo Dizionari e Controllo Componenti I controlli sono attivabili da Strumenti -> Debug -> Controlli. 2.3 Dizionario Tutte le componenti della UserInterface delle nuove funzioni ACG Vision4 (finestre, pannelli, campi,…) sono definite come voci di un dizionario. Esempio: dizionario[campoCodiceCliente]=stringa delle caratteristiche del campo Il dizionario è destinato a contenere anche altre informazioni, pertanto è stata adottata la seguente convenzione: - ogni voce del dizionario è preceduta da un prefisso ( in genere di 4 caratteri ) che consente di identificare la categoria di appartenenza. Esempio: diz["LBL_DATE"]='Data'; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 17 di 167 I prefissi definiti sono: LBL_ Etichetta generica o risorsa stringa WIN_ Finestra PNL_ Pannello FLD_ Campo ACT_ Funzione (bottone e simili) CMD_ACT_ Comando della funzione CMD_ Comando URL_ Indirizzo da invocare URL_PARMS_ Parametri dell’indirizzo VAL_ Lista valori tipo Enum REF_ Riferimento ad altra voce diz Il concetto di dizionario permette di riutilizzare ove previsto le informazioni comuni a più definizioni; ad esempio una stringa come etichetta o uno stile predefinito o un campo comune a più pannelli. I dati del dizionario sono disponibili come tabella di database (generalmente DICT400F) e a partire da questo saranno generati uno o più file di dizionario in modo da gestire in modo efficiente il trasferimento sul client. I file di dizionario sono raggruppabili nelle seguenti categorie: di base (contengono le voci base della Vision4) diz.js contiene l’inizializzazione e le voci di applicazione dizmenu.js contiene le voci del menu di base diz<country>.js contiene le label e i messaggi in lingua di prodotto (contengono le voci relative ad un prodotto) diz_<acronimo_prodotto>.js contiene le voci relative a risorse riferite dal prodotto diz_<acronimo_prodotto>_<country>.js contiene le label e i messaggi in lingua delle voci relative a risorse riferite dal prodotto dipendenti dal sistema informativo (situati in una cartella /addon/diz/<sistema informativo>, permettono di integrare o sovrascrivere le voci degli altri dizionari e devono mantenere la notazione di quelli di prodotto. A runtime verranno caricati i dizionari nella lingua corrente, i dizionari di base e tutti quelli che rispettano la notazione dei dizionari di prodotto nella cartella: <root>/addon/diz/ ed eventualmente quelli presenti nella cartella: <root>/addon/diz/<sistema informativo>/ © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 18 di 167 Le etichette saranno definite nel seguente modo - nel file dizIT.js diz["LBL_IT_SAVE"]='Salva’; - nel file dizEN.js diz["LBL_EN_SAVE"]='Save’; Sono previsti alcuni meccanismi per minimizzare le informazioni richieste per definire gli oggetti, alcune sono dedotte o recuperate per mezzo delle convenzioni. Esempio: Id di un oggetto Tale informazione è generalmente opzionale; viene costruito a partire dalle informazioni disponibili. Di seguito le assunzioni per tipologia di oggetto: XWIN: l’id sarà creato partendo dalla chiave e con un prefisso che ne garantisca l’univocità. XFIELD: l’id è opzionale, se non definito verrà adottato come id l’informazione attrname che rimane obbligatoria. XPANEL: L’id può essere un id di fantasia o lo si omette se l’inclusione dei pannelli avviene attraverso le funzioni $d o $d2. Esempio diz["PNL_SampleList"]='{ "id":"myId", "label":"LIST", …} diz["PNL_Sample3List"]='{ "label":"LIST", "init":"true", '…} diz["PNL_Sample3ContainingPanel"]='{ "id":"Sample3ContainingPanel", "style":"position:relative;width:100%;height:100%;" ' +',' +' "formId":"Form1", ' +' "collapsed":"false", label:"Pannello in pannello", ' +' "panels":[ ' +$d("PNL_SampleList") +' , ' +$d2("PNL_Sample3List", " id:'Pannello2', … ") +' ] ' +' } '; Sono comunque ancora supportate le definizioni preesistenti del tipo: diz["PNL_RdPList"]='{ id:"RdPList", "label":"LIST", "init":"true", ' diz["WIN_RdPList"]= '{"window":{ "id":"RdPWindowList", "width":700, "height":300, ' + ' "panels":[ ' + diz["PNL_RdPList"] +' ] } }'; XLABEL: per l’attributo label, se è definito un valore questo viene considerato come chiave per la ricerca nel dizionario; se la voce non è presente viene restituita la chiave stessa. 2.4 Comportamenti generalizzati © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 19 di 167 Ecco la sintesi dei principali comportamenti attivabili per mezzo delle proprietà degli oggetti nel dizionario. Finestra - struttura (pannelli, tabbed) e posizionamento - gestione messaggi con reindirizzamento al campo in errore - init sulla finestra usata per impostare i dati passati all’atto della creazione Pannelli - tipologia (form, tabbed, lista,…) - struttura (normale o griglia) - barra azioni (opzionale) - gestione comunicazione ajax - url associato - reload - passaggio parametri - abilitazione/disabilitazione e occultamento dell’intero pannello mediante operazioni invocate nella response dal server come pre/postActions. - azione di init (inizializzazione) a livello di pannello normale, proprietà formId) o tabbed (senza specificare formId) multipanel (impostando la - disabilitazione dei campi e tasti all’invocazione di una azione sul server o “offuscamento” dell’intero pannello in attesa dell’inizializzazione dello stesso - validazione del form prima dell’invocazione di una operazione sul pannello (Es. controllo sul browser dei campi obbligatori sull’invocazione della save) - funzione standard associabile all’enter - shortcut (vedi Accessibilità) - controllo alla chiusura di eventuali modifiche in sospeso - menu contestuale (sui pannelli di tipo lista) Campi - label (sopra o a sinistra) - help di campo - icona di campo per attesa, errore,… - campo descrizione associato - funzione controllo all’exit focus con eventuali campi associati - controllo tastiera per campi numerici e data © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 20 di 167 - controllo tastiera in base a set caratteri ammessi - formattazione migliaia campi numerici - obbligatorietà, stile e controllo - readonly - disabilitazione in base al contesto (immissione, modifica,…) - uppercase - componente ReferencedObject che realizza la funzione ? di ACG sul campo - visualizzazione messaggio di errore (flying message) e/o visualizzazione dello stato di errore sui campi multipli mediante bordo rosso. - richiamo funzioni exitfocus solo se il valore è modificato o mediante forzatura, per evitare l’invocazione al server anche quando il valore non è cambiato. - menu contestuale - possibilità di associare un campo “Note” Help E’ possibile associare un testo di aiuto sul campo sull’azione (bottone) sul pannello sulla window Le modalità di visualizzazione previste sono: flying text (il testo appare quando il mouse si posiziona sull’elemento) window separata (F1 sull’elemento evidenziato) La prima modalità è adatta se il testo ha una lunghezza limitata (<100 char), può essere definito come voce di help direttamente nel dizionario. Esempio diz["FLD_Sample7"]='{ "id":"Sample7", "attrname":"CDCSO", … "help":"true"… sarà recuperata la voce diz["HLP_IT_Sample7"]='con help=true, si recupera una voce da dizionario per mezzo dell\' id (Sample7)'; oppure specificando l’attributo diz["FLD_Sample7"]=={… "help":"Sample7Bis" …} sarà recuperata la voce diz["HLP_IT_Sample7Bis"]='con help=Sample7Bis, si recupera la voce indicata dal dizionario'; Per contenuti più corposi ed articolati (immagini) è necessario usare la modalità finestra separata. Nel dizionario la voce di help conterrà l’url della risorsa che contiene l’help che sarà visualizzato in una sezione apposita del desktop o in finestra separata. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 21 di 167 La finestra separata è unica per tutti i pannelli di help. Nella window ACG Vision4è stato aggiunto un tasto che permette di evidenziare nel pannello gli elementi che hanno un file di help associato mediante l’icona , accessibile da mouse e da tab. Esempio diz["HLP_SampleCombo1onPanel7Help"]='/products/E01/help/IT/FunzioneHelp.html'; 2.4.1 Response dal server La risposta proveniente dal server è, per funzioni ACG Service Bus, in formato xml con il seguente schema: <response con le informazioni sulla richiesta, sulla finestra, pannello e capo origine della richiesta e contesto operazione> <preActions>…</preActions> <postActions>…</postActions> <data è il buffer dati> <item name="nomeparametro" value="valoreparametro" [display=”true”|”false”] [visibility=”true”|”false”] [disabled="true"|”false”] > </item> <action name=”nomeazione" [display=”true”|”false”] [visibility =”true”|”false”] [disabled="true"|”false”] > </action> </data> <msgs> <msg tgt="nomeparametro"> testo messaggio </msg> </msgs> </response> dove nomeparametro coincide con attrname definito per l’oggetto XFIELD. La response a seguito di un CHECK che ritorni una descrizione e nessun errore è del tipo: <?xml version="1.0" ?> <response xwin="3_F1_WIN_RdP" xform="3_F1_WIN_RdP_PanelInPanel_Form1" xfield="3_F1_WIN_RdP_PanelInPanel_Form1_CDCSO" xmethod="checkCustomer" xexec="" xcontext="2" > <preActions></preActions> © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 22 di 167 <postActions></postActions> <data> <item name="CDCSO_rascl" value="BENDER PLASTICA </data> srl"></item> <msgs> </msgs> </response> La response a seguito di un SAVE che ritorni dei dati e dei messaggi di errore è del tipo: <?xml version="1.0" ?> <response xwin="1_F3_WIN_DynaAddressCHECK" xform="1_F3_WIN_DynaAddressCHECK_DynaAddressPanel_Form1" xmethod="SAVE" xcontext="0"> <data> <item name="name_descr" value="BENDER Plastica"></item> <item name="exec" value=""></item> <item name="address" value="23qer"></item> <item name="email" value="erwrewq"></item> <item name="name" value="000100"></item> </data> <msgs> <msg>Dati non validi</msg> <msg tgt="address">Indicare Via, Corso, Piazza, Vico..., Lungo..., Strada..., in questo campo</msg> <msg tgt="email">Formato email errato</msg> <msg tgt="name">Codice cliente gia' presente</msg> </msgs> </response> E’ possibile produrre una response contenente item non strettamente collegati al form/pannello di partenza della richiesta. L’engine avvalorerà gli item trovati nell’ambito del form indicato, in caso di insuccesso li ricercherà all’interno di tutti i pannelli della XWIN. Analogo comportamento è previsto per le actions o operazioni sottostanti ai BUTTON. Le azioni nella response hanno id del tipo ACT_xxx_yyy ovvero sono referenziabili per mezzo della loro chiave nel dizionario. Esempio: <action name="ACT_Banca_saveCont" disabled="true" ></action> 2.4.2 Funzioni di Base Sono disponibili le seguenti funzioni di base sui pannelli: xreload xclose © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 23 di 167 xcollapse/xexpand/xtoggle xcheck(url, [force]) xvisibility,xdisplay,xreadonly Esse possono essere invocate: direttamente sul browser Esempio: ad un bottone, la XACTION relativa invocherà l’aggiornamento di un pannello. come risultato di una richiesta al server Esempio: nella response proveniente dal server, nel parametro xexec è presente l’invocazione dell’aggiornamento di un pannello Vediamo con maggior dettaglio alcune di queste funzioni: xcheck(url [,force]) Permette di eseguire una richiesta ajax su un campo, specificando url. Nella richiesta sarà incluso il valore del campo e gli eventuali parametri aggiuntivi (altri campi dei pannelli). La richiesta non viene rilanciata (ad esempio se attivata sull’exit focus) se il valore rimane lo stesso, altrimenti impostare il flag opzionale [force] diz["URL_CustomerServlet"]='../DynaAddress.do'; diz["FLD_CustomerIdKey"]='{ "attrname":"name", …, "onblur":"xcheck('+"'"+'URL_CustomerServlet'+"'"+')" diz["FLD_CustomerIdKey"]='{ "attrname":"name", …, "onblur":"xcheck('+"'"+'URL_CustomerServlet'+"'", true+')" xreadonly, xvisibility, xdisplay, xreload E’ possibile invocare le suddette API passando come identificativo il nome del pannello (attributo id) l’identificativo assoluto del pannello (id composto da <id finestra corrente>…<id pannello>) In questo modo è possibile pilotare il ricaricamento, la visualizzazione o la disabilitazione di un pannello di una qualsiasi window ACGv4 presente nel desktop conoscendone l’id assoluto. Esempio: Supponiamo di avere una window A che apra una window B; il comando di apertura della window B giunge al server corredato dei parametri xwin (identificativo assoluto della window A) e xform (identificativo assoluto del pannello da cui è partita la richiesta). Queste informazioni possono successivamente essere utilizzate dalla parte controller della finestra B per invocare un’azione sulla finestra o su un pannello della finestra A (ad esempio la disabilitazione). © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 24 di 167 Riassumendo: xreadonly([“panelId”|”absolutePanelId”],[”true”|”false”]) xvisibility([“panelId”|”absolutePanelId”],[”true”|”false”]) xdisplay([“panelId”|”absolutePanelId”],[”true”|”false”]) xreload([“panelId”|”absolutePanelId”]) Esempio: <postActions> <action name='xreadonly("1_F0_WIN_Sample_SamplePanel","false")' /> </postActions> 2.4.3 Contesto e pre/postActions Il contesto definisce la tipologia di operazione in corso e permette, impostato opportunamente, di attivare dei comportamenti in modo automatico. Ad esempio un campo che ha la proprietà disableonchange=”true” sarà automaticamente disabilitato se il server imposta il contesto al valore 2 (“modifica”). Il contesto può assumere i seguenti valori: 0 non definito 1 New I campi/azioni con disableonnew sono disabilitati 2 Change I campi/azioni con disableonchange sono disabilitati 5 View Tutti i campi e azioni sono disabilitate A volte si vuole far eseguire all’interfaccia delle operazioni dipendenti dal contesto applicativo ma non mappabili negli stati predefiniti. Ad esempio in una situazione specifica prima o dopo aver avvalorato il contenuto di un pannello con una response è necessario disabilitare o rendere non visibile un altro pannello, ricaricare un'altra sezione,... Una o più operazioni di questo tipo possono essere inserite nella response in due sezioni denominate <preActions> e <postActions> che saranno eseguite nella sequenza. Utili per gestire in modo dinamico il comportamento dei vari pannelli (ad esempio se si vuole rendere non visible o disabilitare l’intero contenuto di un pannello con dei comandi inseriti nella response). Esempio di response operante sulla visualizzazione dei campi <preActions> <action name='xdisplay("DynaAddressPanel","false")' /> © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 25 di 167 <action name='alert(" ho nascosto il pannello")' /> </preActions> <data>…</data> <msg>…<msg> <postActions> <action name='xdisplay("DynaAddressPanel","true")' /> <action name='alert(" ho ripristinato il pannello")' /> <action name='xreadonly("DynaAddressPanel","true")' /> <action name='alert(" ho disabilitato il pannello")' /> <action name='xreadonly("DynaAddressPanel","false")' /> <action name='alert(" ho abilitato il pannello")' /> </postActions> Esempio di comando complesso o metodo javascript nella postActions E’ possibile emettere nella postAction l’esecuzione di un command complesso che nell’esempio fornito Chiede la conferma all’utente In caso affermativo reinvoca un’azione sul server Nel dizionario: diz["CMD_SampleConfirm"] = "js:if(confirm('Sei sicuro?')){ xdirectsend('../DynaAddress.do?action=CONFIRMED_DELETE') }"; Nella response dovrà essere invocato cosi: <postActions> <action name="CMD_SampleConfirm"/> </postActions> 2.4.4 Accessibilità L’applicazione prevede l’accesso via mouse o tastiera. Per garantire l’usabilità via mouse, i tasti funzionali sono dimensionati opportunamente (min 20x20). Tutte gli elementi dell’interfaccia sono accessibili mediante tastiera (TAB). E’ possibile modificare l’ordine agendo sul parametro tabIndex dei campi e dei tasti funzione. Acceleratori Sono previsti alcuni tasti acceleratori per le funzioni usate più frequentemente: CTRL-S save (disponibile nei pannelli con azione SAVE standard*) CTRL-X close window CTRL-N o CTRL-n apertura note CTRL-F apertura componente ricerca stringa, su liste PF (PersistentFilter) CTRL-SPACE apertura componente lista RO (ReferencedObject) ENTER/CTRL submit F3 close window * esempio: gestione Operazioni di SVB © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 26 di 167 E’ possibile personalizzare l’associazione per alcuni tasti (ad esempio il submit può essere associato all’ENTER o al tasto CTRL). 2.4.5 Messaggi L’interfaccia grafica prevede l’emissione di messaggi secondo diverse modalità, dipendenti dall’importanza, dal contesto applicativo e grafico. I messaggi di attesa di una risposta dal server a livello di finestra o di campo (nel caso di richieste AJAX) evidenziate dall’icona animata . Le indicazioni di errore sui pannelli sono rappresentati Tutti i campi in errore vedono il loro bordo modificato in rosso. Sul primo dei campi in errore appare un flyingbox Il testo di tutti i messaggi viene aggiunto nel combo box situato nel bordo inferiore della finestra; il click sul singolo errore porterà il focus sul campo relativo 2.4.6 Menu contestuale La funzione di menu contestuale è disponibile attualmente sugli oggetti XFIELD e XPANEL di tipo lista. E’ opzionale e attivabile specificando la proprietà "menu" avente come valore la chiave del dizionario che elenca i comandi disponibili. Questo attributo può essere una chiave di tipo MNU_xxxx che elenca una serie di comandi (voci di tipo CMD_xxx) o un nodo del menu ad albero (vedi campo Sample2TYPE, Esempio 2). E’ previsto il passaggio di parametri: nel caso della lista la chiave della riga evidenziata, anche di tipo composta, le cui parti sostituiranno le occorrenze di !key nel caso di un campo il suo valore Esempio: menu contestuale sulla lista diz["MNU_SampleList"]=' [ "CMD_NEWRDP","CMD_MODRDP","URL_URLEsterno" ] '; diz["PNL_SampleList"]='{ "id":"RdPList", "label":"LIST", "init":"true", ' +' "style":"height:100%;width:100%;" ' +' , ' +' "url":"URL_RdPList" ' +' , ' +' "menu":"MNU_SampleList" ' +' , ' +' "dblclick":"ACT_DblClickRdPList" ' +' } '; Esempio: sul campo Customer è attivato un menu contestuale con 3 azioni diz["CMD_NEWRDP"]="js:xcrtWIN(\'F1\',\'WIN_RdP\')"; diz["CMD_MODRDP"]="js:xcrtWIN(\'F1\',\'WIN_RdP\', \'[!key]\' )"; diz["URL_URLEsterno"]='http://www.ibm.com?id=!key&id2=!key'; diz["MNU_SampleList"]=' [ "CMD_NEWRDP","CMD_MODRDP","URL_URLEsterno" ] '; diz["FLD_Customer"]='{"attrname":"cdcli","menu":"MNU_SampleList", …} '; oppure diz["FLD_CustomerIdKey"]='{"attrname":"name","menu":"MNU_SampleList", …} '; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 27 di 167 Esempio: invocare Actions sul server da menu contestuale diz["LBL_IT_CMD_SampleServlet"]='Invocazione diretta action'; diz["CMD_SampleServlet"]="xsend('../MyAction.do?tipoazione=CANCELLA')"; diz["MNU_SampleList"]=' ["CMD_SampleServlet",…] '; diz["PNL_SampleList"]='{…, menu":"MNU_SampleList" ' }’ Passaggio parametri al menu contestuale delle liste E’ possibile passare oltre alla chiave del record selezionato (notazione !key) anche valori provenienti da campi presenti in altri pannelli specificando una voce del dizionario del tipo URL_PARMS_xxx. Questa voce conterrà l’id dei campi da passare (pannello_[form]_attrname) sulla falsa riga delle actions definibili sul pannello. diz["URL_PARMS_SampleServlet2"]=' [ "Sample2ContainingPanel_Form1_S2TYP" ] '; diz["LBL_IT_CMD_SampleServlet2"]='Invocazione diretta + passaggio valore campo di altro pannello'; diz["CMD_SampleServlet2"]="xsend('../DynaAddress.do?selectedKey=!key')"; diz["MNU_SampleList"]='…,"CMD_SampleServlet2"] '; diz["PNL_SampleList"]='{ ... “menu":"MNU_SampleList" © Copyright ACG Srl 2014 Tutti i diritti riservati. ...}’ Pagina 28 di 167 2.5 Componenti Di seguito le schede tecniche per i componenti disponibili. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 29 di 167 2.5.1 Window XWIN Oggetto: window Sintassi: diz[“WIN_nomewindow”] Attributo Sintassi Opzionale/ Obbligatorio id Opzionale width Opzionale height Opzionale panels [ <definizione pannello> , …] Obbligatorio parms [ ‘fieldId’,…] Opzionale type Nome del FloatingPane personalizzato Opzionale url init permette di invocare un’azione sul server alla chiusura della finestra usato per invocare l’inizializzazione personalizzata [“init”:“true”|”false”] Opzionale Opzionale indica se l’inizializzazione di window è attiva resizable [“resizable”:“true”|”false”] Opzionale Se non specificato si assume che sia True. Se false si assume che Sia una finestra il cui contenuto sia di dimensioni limitate e non variabili Vengono rimosse le azioni di minimizzazione, massimizzazione,.. Viene centrata nello schermo Inizializzazione della window La funzione di creazione della window prevede la possibilità (opzionale) di inizializzazione. Questa viene attivata impostando la proprietà "init":"true" a livello di window. L’url usato per l’inizializzazione è l’url definito a livello di window. diz["WIN_Sample9"]= '{"window":{ "id":"Sample9", … , "init":"true",'… "url":"../DynaAddress.do"'… Questa inizializzazione è eseguita prima di quella dei pannelli (se presente "init":"true" sui singoli pannelli). In generale, se presente, l’inizializzazione sulla window dovrebbe essere in alternativa a quella dei singoli pannelli, a meno di particolari esigenze applicative. La nuova funzione di creazione della window è function xcrtWIN(aPref,aWinId[,aParmsValues,aInitParms]){ e consente di creare la window passando un metodo di init personalizzato (che sovrascriverà quello standard) diz["CMD_4Sample9"]="js:xcrtWIN('F0','WIN_Sample9','[]','xmethod=mioinit');" e la richiesta sarà del tipo: ../DynaAddress.do?xwin=1_F0_WIN_Sample9&xmethod=mioinit © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 30 di 167 E’ possibile anche passare altri parametri provenienti da una lista xcrtWIN('F0','WIN_Sample9','[!key]','xmethod=mioinit&mioparm=EDIT');" in questo caso è necessario impostare a livello di window la definizione del campo passato diz["WIN_Sample9"]= '{"window":{ "id":"Sample9", … +', "parms": [ "Sample9_Form1_CDCSO" ] ' in modo da ottenere una invocazione di questo tipo: ../DynaAddress.do?xwin=1_F0_WIN_Sample9&CDCSO=5&xmethod=mioinit E’ possibile anche passare altri parametri statici diz["CMD_4Sample9"]="js:xcrtWIN('F0','WIN_Sample9','[]','xmethod=mioinit&mioparm=EDIT');" in questo caso avrò ../DynaAddress.do?xwin=1_F0_WIN_Sample9&CDCSO=5&xmethod=mioinit&mioparm=EDIT Passaggio di parametri alla window In questo esempio la window conta 2 pannelli e riceve un parametro passato in fase di creazione che andrà ad avvalorare un campo (RdPPanel_Form1_num). diz["WIN_RdP"]= '{"window":{ id:"RdPWindow", "width":700, "height":500, ' + ' "panels":[ ' +diz["PNL_RdPPanel"] +',' +diz["PNL_RdPRowsList"] +' ] ' +' } ,'+ ' "parms": [ "RdPPanel_Form1_num" ] ' +' }'; Uso di window personalizzate La proprietà “type” per le window consente l’uso di FloatingPane personalizzati nel desktop ACG Vision4. Prerequisito la presenza degli oggetti necessari: MyFloatingPane.html MyFloatingPane.css MyFloatingPane.js nelle cartelle di dojo e l’inserimento del file MyFloatingPane.js fra i require in /addon/b_dsk.jsp Esempio diz["WIN_RO"]= '{ "type":"ACGROFloatingPane", "window":{ "id":"ROWindow", Chiusura forzata di una window In caso di errore è possibile chiudere una window emettendo un messaggio. Il metodo da impostare nelle postAction è del tipo xclose(winId, msg) con la winId che ha lo stesso valore della xwin e per testo un testo già localizzato; L’utente riceve l’errore nella popup, clicca su OK e la finestra si chiude invocando il metodo close sul server (se l’url della window è impostato) Parametro InitialState parametro initialState nella definizione della window. Viene ignorato se sono presenti i parametri width e height. Riassumendo: diz[WIN_<id> "]= '{"window":{ "id":"<id>", "initialState":"[maximized|minimized]",… Esempio: diz["WIN_Sample"]= '{"window":{ "id":"Sample", "initialState":" maximized ",… oppure diz["WIN_Sample"]= '{"window":{ "id":"Sample", "width":100, "height":200,… Proprietà Title E’ possibile passare il titolo della window nel comnado di creazione function xcrtWIN(aPref,aWinId,aParms,aInit,aType,aTitle) Il parametro aTitle può essere il valore definitivo o nel dizionario diz_<acron.prod.>_<locale>.js Es. diz['LBL_IT_XAAC0']="Lista Clienti"; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 31 di 167 diz['LBL_EN_XAAC0']="Customer List"; diz["CMD_4PFList"]="js:xcrtWIN('F0','WIN_PFList','[com/ibm/acg/xml/AAC0.xml]',undefined,undefined,'XAAC0');" oppure diz["CMD_4PFList"]="js:xcrtWIN('F0','WIN_PFList','[com/ibm/acg/xml/AAC0.xml]',undefined,undefined,'miotitolo');" Troncato al valore della variabile globale $$maxtit (impostato a 40) Meccanismo recupero del title: // first, check if passed as parameter, // then retrieve from property title in win definition // otherwise get it from labels using winId Azione per collassare/espandere tutte i pannelli E’ possibile collassare e riespandere tutte le sezioni definite collassabili (impostazione di un valore nella property collapse del pannello). Al primo click tutti pannelli sono collassati, al secondo sono ampliati. La modifica non è persistente, alla riapertura del pannello saranno riprese le impostazioni di default dei singoli pannelli. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 32 di 167 2.5.2 Panel XPANEL Oggetto: pannello Sintassi: diz[“PNL_nomepannello”] Attributo Sintassi/Note Opzionale/ Obbligatorio id panels label Obbligatorio [ <definizione pannello> , …] Se esiste, realizza il bordo con etichetta Opzionale Opzionale E’ possibile modificarla dinamicamente (vedi esemp.) fields [ <definizione campo> , …] Obbligatorio actions [ <definizione tasto azione> , …] Opzionale enterAction Permette di definire la action da lanciare sull’INVIO sul pannello. All’ENTER dell’utente, invocherà il click sul button con id del tipo <formId>_SAVE Opzionale L’INVIO può essere personalizzato (attivato su tasto ENTER o CTRL) Es. "enterAction":"SAVE" rows rows:# Opzionale insieme all’attributo cols attiva la definizione del pannello “tipo griglia” cols cols:# Opzionale style <definizione di stile> Obbligatorio bar toolbar grafica per azioni standard (SAVE,NEW,…), Opzionale relativa al pannello, posizionata in alto nel pannello, subito a destra della etichetta del bordo (se presente), sempre presente, contiene l’icona di attesa risposta dal server icone azioni standard <definizione di barra per azioni non standard> collapsed type se definito abilita la funzione di collapse/restore sul pannello "tabbed" "grid" "list" per gestire fonti dati di tipo lista diverse dall’applicazione PF © Copyright ACG Srl 2014 Tutti i diritti riservati. Opzionale Opzionale Pagina 33 di 167 "headerRows" per identificare il componente testatarighe linked contiene l’id o la serie di identificativi di altri componenti collegati. Utilizzato nel componente testataRighe Opzionale menu contiene la chiave del menu contestuale associato al pannello (di tipo lista) Opzionale oncollapse metodo eseguito all’occultamento del pannello Opzionale onexpand metodo eseguito all’espansione del pannello. Opzionale creation usato nei tabbed per rimandara la creazione de pannello al click dell’utente sulla linguetta del tabbed Opzionale valori ammessi:deferred Proprietà per “attivare” il pannello come form formId Obbligatorio url Obbligatorio init init=”true” Opzionale Se esiste, alla creazione della finestra viene invocato l’url del pannello con il method init Esempio di pannello di tipo grid, con inizializzazione, posizionamento campi nelle celle e 2 azioni nella action bar: diz["PNL_RdPPanel"]='{ id:"RdPPanel", "label":"RdPPanel", "init":"true", "url":"../DynaAddress.do", "formId":"Form1", "rows":5, "cols":5, "style":"position:relative;top:100;left:100;width:100%;height:100%;"' +','+ ' "fields":[ '+ $d2("FLD_RdPDATA", "row:0, col:0") … +','+ $d2("FLD_RdPStato", "row:1, col:3, tabIndex:-1") + ']' +','+ ' "bar":{row:2, col:1, span:2, "buttonstyle":"width:100;" } ' +','+ ' "actions":[ '+ $d("ACT_DynaAddress_CANCEL") +','+ $d("ACT_RdP_SAVE") + ']' +' } '; Esempio di pannello con sovrascrittura delle proprietà (tabIndex e labelStyle) di un campo: diz["PNL_RdPPanel"]='{ id:"RdPPanel", … ' "fields":[ '+ $d2("FLD_RdPStato", "row:1, col:3, tabIndex:-1, labelStyle:'top' ") ']'… +' } '; + Esempio d’uso della proprietà “oncollapse/onexpand” per i pannelli Se definite vengono eseguire all’espansione o all’occultamento del pannello. Esempio: Si desidera aggiornare la lista all’espansione della suo pannello originariamente chiuso. diz["ACT_Sample5onexpand"]='{ "url":"xreload(\'Sample5List\')" } '; diz["ACT_Sample5oncollapse"]='{ "url":"confirm(\'action on collapsing...\')" } '; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 34 di 167 diz["WIN_Sample5"]= '{"window":{ "id":"Sample", ' +' "panels":[ ' +$d2("PNL_SampleList", ' "id":"Sample5List", "init":"false", collapsed:"true", onexpand:"ACT_Sample5onexpand", oncollapse:"ACT_Sample5oncollapse" ') +' ] ' +' }' +' , "parms": [ "Sample5_Form1_combo1" ] ' +' }'; Esempio di label di pannello modificabile dinamicamente da programma E’ possibile modificare dinamicamente la label di un pannello da programma invocando come action della response la seguente API xlabel(<absolutePanelId>,<labelId>) Esempio: <action name="xlabel('1_F0_WIN_Sample8_Sample8Panel','LabelFromServer')"/> dove 'LabelFromServer' viene cercata nel dizionario come label e in assenza restituita com’e’ e usata per l’intestazione del pannello Esempio di gestione larghezza e altezza E’ possibile definire alcune proprietà di stile per il pannello (width e height) Esempio: Nel pannello HH:MM (figura A) è stata impostata la larghezza a 100 per “avvicinare” i due campi HH e MM, in caso contrario il pannello ocuuperà il 100% della larghezza e i campi, in celle separate si ritroverranno ben distanziati. diz["PNL_Sample4HHMM"]='{"id":"Sample4HHMM", "label":"Sample4HHMM", … "style":"{width:100}" ' Esempio di pannello con bordo trasparente per pannelli privi di etichetta E’ possibile avere un pannello con bordo trasparente per mantenere un eventuale allineamento con pannelli presenti nella stessa finestra e provvisti di bordo. E’ sufficiente specificare la parola chiave TRANSPARENT come valore della proprietà label. (figura A) diz["PNL_Sample4"]='{"id":"Sample4", "label":"TRANSPARENT", … Esempi di pannelli di tipo tabbed: Pannello tabbed con 2 sottopannelli diz["PNL_RdPTabbed"]='{ id:"RdPTabbed", "type":"tabbed", "label":"DynaAddressTabbed", "style":"position:relative;top:100;left:100;width:100%;height:100%;"' +','+ © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 35 di 167 ' "panels":[ ' +diz["PNL_RdPPanel"] +',' +diz["PNL_RdPRowsList"] +' ] ' +' } '; Pannello tabbed : abilitazione/disabilitazione linguetta tabbed E’ possibile abilitare o meno la linguetta del tabbed xenableTab(<#pagina da selezioanare>, true|false [,<id del tabbed>]) richiamabile anche come action nella response. Esempio: All’uscita campo disabilitare la pagina 2 del tabbed in cui il campo è posizionato $d2("FLD_Sample10SelectTabField", "row:1, col:0, onblur:'xenableTab(2,false)' ") Esempio: Emettere come postAction il seguente comando per disabilitare la pagina 1 del tabbed con id='mySampleTabbed' <postActions> <action name="xenableTab(1,false,'mySampleTabbed')"/> </postActions> Selezione di una pagina del tabbed E’ possibile selezionare la pagina di un tabbed mediante l’API dell’UIEngine xselectTab(<#pagina da selezioanare>[,<id del tabbed>]) richiamabile anche come action nella response. Esempio: All’uscita campo passare alla pagina 2 del tabbed in cui il campo è posizionato $d2("FLD_Sample10SelectTabField", "row:1, col:0, onblur:'xselectTab(2)' ") Esempio: Emettere come postAction il seguente comando per passare alla pagina 1 del tabbed con id='mySampleTabbed' <postActions> <action name="xselectTab(1,'mySampleTabbed')"/> </postActions> Creazione differita di una pagina del tabbed E’ possibile realizzare un tabbed in cui alcune pagine non siano create al momento dell’aperura della window; è sufficiente specificare la property creation:'deferred' nella definizione del panello usata per definire una pagina del tabbed. Al click sulla linguetta del tabbed il pannello viene creato ed eventualmente inizializzato. E’ opportuno non avere riferimenti dall’esterno ad informazioni presenti nel pannello che potrebbero non essere ancora state inizializzate. Esempio: diz["PNL_SampleTabbed"]='{"id":"SampleTabbed","type":"tabbed","label":"SampleTabbed", "style":"position:relative;top:100; left:100;width:100%;height:100%;"' +',' +' "panels":[' +$d2("PNL_Sample1String") +',' +$d2("PNL_SampleList", " creation:'deferred' " ) +',' +$d2("PNL_Sample1Num", " creation:'deferred' " ) +',' +$d2("PNL_Sample1Combo", " creation:'deferred' " ) +' ] ' +' } '; Esempio di pannello di tipo grid Esempio: diz["PNL_RdPGrd"]='{ id:"RdPGrd", "type":"grid", "label":"DynaAddressGrd", "style":"position:relative;top:100;left:100;width:100%;height:100%;"' © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 36 di 167 +','+' "panels":[ '+diz["PNL_RdPPanel"]+',' +diz["PNL_RdPRowsList"]+' ] ' +' } '; Esempio di pannello di tipo list Consultare il paragrafo relativo alle liste. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 37 di 167 2.5.3 Field XFIELD Oggetto:campo di editazione Sintassi: diz[“FLD_nomecampo”] Attributo Sintassi/Note Opzionale/ Obbligatorio id se non definito, assume lo stesso valore di attrname Opzionale attrname nome del parametro nella request, Obbligatorio type non definito combobox label textarea file (upload) value contiene l’eventuale valore di default del campo; Opzionale Opzionale se type=combobox è l’indice dell’elemento preselezionato values se type=combobox è la chiave per accedere ai valori consentiti Opzionale se non definito è un combobox con valori dinamici caricati a runtime dalla response size se type=combobox è il numero di elementi da visualizzare Opzionale datatype Indica il tipo di dato. Valori ammessi: Opzionale DATE, DATE10,DECIMAL style Opzionale label Stringa o riferimento a risorsa Opzionale labelStyle Indica la posizione della etichetta del campo, Opzionale se non definito essa non è visualizzata. Valori ammessi: top,left mandatory non definito o “true” © Copyright ACG Srl 2014 Tutti i diritti riservati. Opzionale Pagina 38 di 167 disableonnew non definito o “true” Opzionale disableonchange non definito o “true” Opzionale uppercase non definito o “true” Opzionale visibility non definito o “true” Opzionale campo e/o descr non visibile display non definito o “true” Opzionale campo e/o descr non costruito filled Se definita attiva il controllo di riempimento del campo all’exitfocus sul campo e in fase di validazione prima del lancio Opzionale help impostando questa proprietà si attiva l’help valori consentiti “true” oppure ”chiave_di_dizionario” Opzionale menu contiene la chiave del menu contestuale associato al campo Opzionale pf chiave della definizione di un ReferencedObject nel dizionario pfparms lista parametri da passare al ReferencedObject parzializzazione (additionalWhere) note nome del parametro nella request (attrname) del campo “note” associato al campo per la Opzionale Opzionale Proprietà agganciabili al campo a livello di definizione del pannello row Riga col Colonna span Numero colonne su cui debordare tabIndex TabIndex mask usato per controllare il valore di un campo Esempio: campo di tipo ‘Cliente’ dotato di campo descrizione (‘FLD_DESCR’), obbligatorio, con un metodo da eseguire all’exitfocus ('URL_RdP_CheckCustomer', già nel dizionario) con un suo stile. diz["URL_RdP_CheckCustomer"]='../CheckAction.do?xmethod=checkCustomer'; diz["FLD_RdPCliente"]='{"id":"RdPCliente","attrname":"CDCSO","pf":"RO_Clienti","mandatory":"true,"label":"CLIENTE","l abelStyle":"left", "onblur":"xcheck('+ "'" + 'URL_RdP_CheckCustomer'+ "'" + ')", "descr":"FLD_DESCR" , "style":{"size":6,"maxlength":6} } '; Esempio: Associazione generalizzata della descrizione ad un campo E’ stata semplificata la definizione della descrizione associato ad un campo e dell’eventuale referencedObject. E’ ora possibile definire delle tipologie generalizzate di campi descrizione (senza attrname) e referenziarli nella definizione del campo. diz["FLD_GenericDescription50"]='{"style":{"size":30,"maxlength":50}} '; diz["FLD_Sample2Customer"]='{"attrname":"CDCSO2", …, "descr":"FLD_GenericDescription50" } '; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 39 di 167 Il campo Sample2Customer sarà seguito da un campo descrizione di 30 char e con attrname CDCSO2_xdescr. Esempio: campo di tipo combobox e lista diz["LBL_IT_COMBOVALUES_1st"]="Uno"; diz["LBL_IT_COMBOVALUES_2nd"]="Due"; diz["LBL_IT_COMBOVALUES_3rd"]="Tre"; diz["LBL_IT_COMBOVALUES_4th"]="Quattro"; diz["FLD_COMBO1"]='{id:"COMBO1", values:"COMBOVALUES", type:"combobox", "attrname":"combo1" } '; diz["FLD_COMBON"]='{id:"COMBON", values:"COMBOVALUES", type:"combobox", size:3, "attrname":"combon"} '; Esempio di campi collegati ad un campo predefinito I valori di tali campi saranno inclusi nella richiesta all’esecuzione di operazioni sul campo predefinito. Ad esempio il CHECK all’exit focus del campo name vedrà aggiunte alla sua request la stringa &date=valoredelcampo_date&date10= valoredelcampo_date10 diz["URL_PARMS_DynaAddressPanel_id"]='["DynaAddressPanel_Form1_nome","DynaAddressPanel_Form1_codfisc"]’; Esempio di help Modificata la logica di attivazione che ora avviene solo impostando la proprietà help nella definizione del campo Modalità consentite: help:”true” oppure help:”chiave_di_dizionario” Esempio diz["HLP_IT_COMBON"]='combo con valori predefiniti…'; diz["HLP_EN_COMBON"]='combo with predefined values…'; diz["FLD_SampleComboN"]='{"id":"COMBON","attrname":"combon", type:"combobox", help:"true", …"} '; diz["HLP_IT_HelpIdNotRelatedToFldId"]='combo con valori dinamici…'; diz["HLP_EN_HelpIdNotRelatedToFldId"]='combo with dynamic values….'; diz["FLD_SampleComboV"]='{ "attrname":"combov", type:"combobox”, help:"HelpIdNotRelatedToFldId" …} '; Esempio di combobox dinamico E’ possibile generare dinamicamente su server valori e le label usate da un combobox e restituirli nella response secondo la seguente sintassi <item name="combov" value="A~APPROVATO;;!R~RESPINTO" ></item> dove APPROVATO e RESPINTO sono le label che appariranno, A e R i valori reali del campo ed il ! indicherà eventualmente l’opzione selezionata Il combo box dovrà essere definito nel dizionario in questo modo diz["FLD_SampleComboV"]='{ type:"combobox", "attrname":"combov" } '; o diz["FLD_SampleComboV"]='{ type:"combobox", "attrname":"combov", "size":5} '; I casi possibili sono: <item name="combon" value="N" ></item> un solo valore selezionato <item name="combon" value=" ;;S" ></item> più valori selezionati <item name=" combon " value="!A~APPROVATO;;!R~RESPINTO;;S~SOSPESO" ></item> reimposta il combo con nuove opzioni di cui 2 selezionati <item name="combon" value="" ></item> ignorato Esempio di textarea Con gestione dell’obbligatorietà, lunghezza massima e indicazione della lunghezza testo alla digitazione Esempio diz["FLD_SampleTextarea"]='{ "attrname":"textarea", "mandatory":"true", "type":"textarea" , "maxlength":"3", "rows":5, "cols":60 } '; Esempi di Label © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 40 di 167 Il componente XField permette di implementare anche il concetto di label in modo etremamente flessibile. Riassumendo, la label può essere Testo isolato Testo isolato Associato ad Associato ad statico dinamico un campo, opzionale, (informazione statica) un campo, opzionale, (informazione dinamica) caratterizzato da uno stile di default o personalizzabile. Nell’esempio 8 presenta alcuni di questi casi Esempio di testo isolato statico Usato per inserire un commento o una informazione predefinita. Si dichiara nel modo seguente diz["FLD_SampleSubject"]='{ "type":"label", "labelStyle":"left", labelStyle:"width:100%" } '; L’assenza dell’attributo “value” lo identifica come statico. Esempio di testo isolato dinamico Usato per visualizzare una label il cui valore dipende dall’operazione in atto. Si dichiara nel modo seguente: diz["FLD_SampleDynalabel"]='{ "attrname":"dynalabel" , value:"dynamic label", "type":"label" } '; Esempio di Associato ad un campo, opzionale, (informazione statica) E’ la label che precede un campo il cui valore è risolto in base alla locale dai dizionari. Opzionale perché attivabile solo se è presente l’attributo labelStyle. Tale attributo può assumere i seguenti valori: “top” ”left”, properties di stile, esempio: "width:70;position:top" La posizione top pone la label sopra il campo, la left a sinistra, inoltre con le properties è possibile variare le altre carattistiche (per esempio la larghezza che se non impostata è quella di default: 100px) Esempio di Associato ad un campo, opzionale, (informazione dinamica) Come il precedente con la possibilità dal server di variarne il contenuto, usando i metodi delle Action disponibili per produrre una response del tipo <data>... <item name="CDCSO" value="000100" label="Cliente di spedizione 2" ></item> ...</data> Esempio di label dinamica Realizzata usando un campo di input con attributo "type":"label". Può essere statica impostando l’attributo “value”=”testo statico” o dinamica; in questo caso il valore può essere impostato dalla parte server dell’applicazione con le stesse modalità degli altri campi di input. Esempio: diz["FLD_DYNALABEL"]='{ "attrname":"dynalabel" , "type":"label" , "value":"testo statico" } '; Esempio: diz["FLD_DYNALABEL"]='{ "attrname":"dynalabel" , value:"dynamic label", "type":"label" } '; e nella response dovrà essere presente <data> <item name=" dynalabel" value="etichetta dinamica" ></item> </data> Esempio di XFIELD di tipo file (per UPLOAD) Realizzato con , ovvero Input tipo file per l’upload, necessario inserirlo in un suo form dedicato. diz["FLD_SampleFileupload"]='{"attrname":"file2upload" , type:"file" } '; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 41 di 167 Esempio di associazione del componente “Note” ad un campo E’ possibile associare ad un campo un commento definendo la proprietà note nella definizione nel dizionario. diz["FLD_SampleFieldWithNote"]='{ ..."attrname":"FLDWITHNOTE", "note":"NOTE1", "... } '; Il valore di questa proprietà è l’attrname del commento; sarà quindi il nome del paramentro nella request. La response sarà di questo tipo: Esempio di un campo di tipo “nota” con attrname NOTE1 avente come valore un testo di 2 righe: <item name="NOTE1" value="rigo1 rigo2" ></item> Graficamente apparirà l’icona accanto al campo. Il componente “note” potrà essere invocato dal campo stesso con l’acceleratore CTRL-N o cliccando sull’icona. Per chiudere la finestra senza salvare, cliccare sull’icona apposita senza salvare o con CTRL-K Per memorizzare (non salva su server) cliccare sul dischetto o CTRL-S. Campo Note: proprietà valore E’ possibile associare nella definizione del campo commento nel dizionario un valore statico o locale-dipendente. diz["FLD_Sample4Image"]='{"id":"Sample4Image… "note":"sample4imageurl", notevalue:"Attivo", … } '; oppure diz["LBL_IT_DATE"]='Data'; diz["FLD_Sample4Image"]='{"id":"Sample4Image… "note":"sample4imageurl", notevalue:"DATE", … } '; Campi con proprietà mask E’ possibile definire una proprietà sul campo per controllare che il valore inserito sia compatibile con una maschera deifnita per mezzo di una espressione regolare. Es. All’exit focus si vuole controllare che il valore nel campo sia compatibile con l’ora nel formato xx:yy (xx=0..24 e yy=0..59) Bisogna impostare l’espressione regolare relativa ^(20|21|22|23|[01]\d|\d)(:)[0-5]\d$ nella proprietà mask del campo: diz["FLD_SampleString09"]='{"attrname":"Sample1Field09", "constraint":"^[0-9:]$", "mask":"^(20|21|22|23|[01]\d|\d)(:)[0-5]\d$", "labelStyle":"width:300px;position:left", "style":{"size":5,"maxlength":5} } '; Gestione proprietà style per i field E’ possibile impostare da dizionario la proprietà HTML style per un field. diz["FLD_SampleCustomerStatus"]='{"id":"SampleCustomerStatus", "type":"combo", "attrname":"customerstatus", "style":{"background":"#FF8000", "color":"#FFFFFF" }, "refcol":"ANCL200F.ATA12","labelStyle":"left" } '; Esempio di XFIELD di tipo time E’ disponibile la prima versione del componente per inserire l’ora in diversi formati. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 42 di 167 Derivato da un oggetto dojo, mantiene alcune caratteristiche come il controllo sulla digitazione e l’emissione del messaggio di errore sulla destra del campo. Offre un limitato supporto ad alcune funzionalità aggiuntive ACGv4 (onblur,onfocus,onkeyup). Non supporta altre funzioni tipo help,… Per definire nel dizionario questo tipo di campo aggiungere la proprietà type:"time" ed eventualmente il parametro format (opzionale) che può avere i seguenti valori format:"HH:mm” format:"HH:mm:ss" format:"hh:mm:ss:SSS" Se non specificato si assume il formato format:"HH:mm” (tipo 23:59) Esempio: diz["FLD_SampleTime1"]='{"attrname":"time1", type:"time", labelStyle:"left", format (default HH:mm)", "style":{"size":20,"maxlength":20} } '; label:"Time senza diz["FLD_SampleTime2"]='{"attrname":"time2", type:"time", labelStyle:"left", label:"Time HH:mm:ss", format:"HH:mm:ss" } '; diz["FLD_SampleTime3"]='{"attrname":"time3", type:"time", labelStyle:"left", label:"Time hh:mm:ss:SSS con onblur", format:"hh:mm:ss:SSS" } '; diz["PNL_SampleTime"]='{ "id":"SampleTime", "label":"SampleTime", "formId":"Form1", ' "cols":5, "style":"position:relative;top:100;left:100;width:100%;height:100%;"' ' +' "fields":[ ' + $d2("FLD_SampleTime1", "row:0, col:0 ") +','+ $d2("FLD_SampleTime2", "row:1, col:0 ") +','+ $d2("FLD_SampleTime3", "row:2, col:0, \"onblur\":\"alert('lancio onblur')\" ") +']' +',' +' "bar":{row:5, col:0, span:2, "buttonstyle":"width:100;" } ' +','+' "actions":[ '+ $d("ACT_Sample2_save") +']' +' , ' +' "url":"URL_Sample2Servlet" ' +' } '; +' "rows":5, + ' , diz["WIN_SampleTime"]= '{"window":{ "id":"SampleTime", "width":700, "height":500, "title":"Time", + ' "panels":[ '+ diz["PNL_SampleTime"]+' ] '+' }'+' }'; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 43 di 167 2.5.4 XAction XACTION Oggetto: Action Sintassi: diz[“ACT_nomeazione”] || diz[“ACT_nomepannello_nomeazione”] Operazioni predefinite: Attributo Sintassi/Note Opzionale/ Obbligatorio id Opzionale label Stringa o riferimento a risorsa Opzionale Onclick Comando o stringa comando Opzionale Se non definito, invocato xsend che invia una richiesta ajax all’url definito Esempio: "onclick":"mysend()" url url da invocare Opzionale se non definito viene preso quello del XPANEL se url del XPANEL non definito, preso quello della XWIN se url di XWIN non definito, errore a runtime se url inizia con: WIN: la pagina andrà in finestra ACG Vision4 URL: in finestra browser esterna disableonnew non definito o “true” Opzionale disableonchange non definito o “true” Opzionale non definito o “true” Opzionale visibility campo e/o descr non visibile display non definito o “true” Opzionale campo e/o descr non costruito Proprietà agganciabili definizione del pannello al tasto a livello di Row Riga (se non presente la Action Bar) Col Colonna (se non presente la Action Bar) Span Numero colonne supero (se non presente la Action Bar) tabIndex TabIndex © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 44 di 167 Il nomeazione è usato per attivare implicitamente alcuni comportamenti. Ad esempio nomeazione viene aggiunto alla request come parametro addizionale <url>&xmethod=<nomeazione> semprechè non sia già presente e cablato nell’url stesso. Esempio: diz["ACT_DynaAddress_SAVE"]='{' +' "id":"ACT_DynaAddress_SAVE"' +', "label":"ACT_DynaAddress_SAVE"' /*opzionale, se non definita viene usata la chiave */ /*opzionale,se non definita viene usata <nomeazione> */ +', "onclick":"mysend"' /*opzionale, standard xsend */ +', "url":"../DynaAddress.do?xmethod=SALVA"' /*opzionale */ +' } '; Tutte le proprietà sono opzionali perché alcune di esse vengono costruite a partire dalla chiave e possono far riferimento a informazioni eventualmente già disponibili a livello superiore. Pertanto sarebbe possibile avere un pannello che includa fra le actions un’azione, la cui definizione non è presente nel dizionario, purchè sia specificato l’url da invocare a livello di pannello o di window. Il nomeazione è usato anche per recuperare la definizione e i comportamenti delle azioni standard. Si dicono standard le azioni: new,edit,view,delete,print,copy,save,savenew,saveclose,close,undo,reload,export Per esse è precaricata nel dizionario la label la posizione prevista è nella toolbar dell’XPANEL mediante l’icona l’icona è fornita, del tipo /images/<nomeazione>.gif l’ordine è ridefinibile in base alla sequenza fra le actions del XPANEL Per ottenere azioni non standard è sufficiente usare un <nomeazione> standard. non incluso nella lista 1) Metodo invocato al click se non definito viene usato il metodo standard xsend che gestisce la composizione della request con gli eventuali parametri e la sottomissione della stessa come richiesta AJAX. 2) Url da invocare è definito come proprietà di XACTION altrimenti viene preso quello del XPANEL © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 45 di 167 se url del XPANEL non definito, preso quello della XWIN se url di XWIN non definito, errore a runtime Esempi di azione diz["ACT_DynaAddress_SAVE"]='{' +' id:"ACT_DynaAddress_SAVE"' +',' +' "label":"ACT_DynaAddress_SAVE"' +',' +' "url":"../DynaAddress.do"' +' } '; diz["ACT_SAVE"]='{id:"SAVE","label":"SAVE","onclick":" eseguire"} '; diz["PNL_DynaAddress"]='{ "tabIndex:0") } id:"DynaAddressPanel… + metodo javascript da $d2("ACT_DynaAddress_READ", Oggetto XACTION (bottone) apre finestre ACG Vision4o separate Come nel menu ad albero, anche il bottone nei pannelli ACG Vision4ha la possibilità di aprire nuove finestre ACG Vision 4 o URL in finestre separate., usando i noti prefissi (WIN: e URL:) diz["URL_PARMS_Sample8_4INTLINK"]='["Sample8_Form1_CDCSO"]'; diz["ACT_Sample8_4INTLINK"]='{"label":"4INTLINK", "url":"WIN:http://www.ibm.com", "style":"width:120;" } '; diz["URL_PARMS_Sample8_4EXTLINK"]='["Sample8_Form1_CDCSO"]'; diz["ACT_Sample8_4EXTLINK"]='{"label":"4EXTLINK", } '; © Copyright ACG Srl 2014 Tutti i diritti riservati. "url":"URL:http://www.ibm.com" Pagina 46 di 167 Possibilità di modificare lo stile del singolo bottone Sovrascrivendo quello impostato a livello di actionbar. diz["PNL_Sample8"]='{ "id":"Sample8",' .... +','+' "bar":{row:4, col:0, span:2, +','+' "actions":[ "buttonstyle":"width:80;" } ' '+ $d2("ACT_Sample_SAVE", " help:'SAVEonPanel7Help' ") +','+ $d2("ACT_Sample8_4INTLINK") +','+ $d2("ACT_Sample8_4EXTLINK", " \"style\":\"width:160;\" ") +']' +' } '; Validazione disattivabile sulle actions Le action possiedono una proprietà che permette di non eseguire la validazione all’invocazione. Nell’esempio la seconda azione di tipo save non richiama la validation. diz["PNL_Sample5"]='{ "id":"Sample5", "label":"SamplePanel", ' ' "actions":[ '+ … $d2("ACT_Sample_save") +','+ d2("ACT_Sample_save", " id:'exec,no validation', validation:'false' " ) +'] } '; Identificazione delle action di tipo "Nuovo" o "Lista" dalla Cronologia Come da standard ACGv4, più nodi del menu ad albero possono essere identificati da stringhe del tipo “Nuovo”/”Lista”. Nella “Cronologia” però queste azioni non sono identificabili perché tutte caratterizzate dalla stessa stringa “Lista”. E' possibile inserire nel dizionario una ulteriore etichetta, nella sintassi come da esempio, che verrà visualizzata solo nella Cronologia. Esempio: se diz["LBL_IT_4ANFO200FList"]="Lista"; basta aggiungere la seguente definizione diz["LBL_IT_SECONDARY_4ANFO200FList"]="Lista Fornitori"; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 47 di 167 2.5.5 Liste Liste (PersistentFilters) Le ACG Vision4sono dotate di un macrocomponente denominato PersistentFilters, che permette di impostare ed eseguire query parametriche complesse. Le query usano una definizione di base, creato a design-time mediante un wizard. Tale definizione di base è un file xml, posizionato nella cartella <root>\ WEB-INF\classes\com\ibm\acg\xml dell’applicazione web, definisce la lista dei campi, i nomi delle tabelle interessate, le condizioni di ordinamento, di selezione (anche dinamica), di join/union, numero max di record e per pagina,... Query parametriche utente L’utente finale ha la possibilità di creare a runtime delle interrogazioni proprie denominate “query parametriche utente” in cui applicare condizioni di visualizzazione, selezione, ordinamento che si applicano su quelle impostate nel file di definizione di base citato nel paragrafo precedente. Tale gestione è possibile usando la toolbar delle liste che offre le operazioni di: Ricerca nelle liste Esecuzione query default Riesecuzione query corrente Riesecuzione query corrente con nuova richiesta parametri (solo per le query dinamiche) Nuova query Modifica query Lista query Stampa risultato query Impostazioni di stampa Export risultato query © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 48 di 167 Dal pannello “Lista queries” è possibile creare una nuova query, modificare, copiare o cancellare la query selezionata. E’ possibile definire una query utente di default che sarà richiamata al lancio della lista al posto di quella descritta nella definizione di base. Inoltre è possibile selezionare quelle preferite mentre il superutente avrà la possibilità di definire una query di tipo public ovvero disponibile per tutti gli utenti. Dal pannello “Crea/Modifica query” è possibile creare la query impostando gli attributi da visualizzare, le condizioni di selezione (anche dinamiche) e di ordinamento, ... © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 49 di 167 Il componente per la gestione delle query utente è stato realizzato in una finestra dedicata che contiene tutte le funzionalità già note (lista delle query utente disponibili, creazione e modifica delle stesse, esecuzione di prova,…) In più consente, sulla lista delle query disponibili, il lancio di una query nel pannello della finestra che lo ha aperto. Per esempio il ReferencedObject presenta la combo con le query preferite, l’utente apre la “Gestione query utente” e crea una nuova o richiama una esistente ma non preferita dalla lista di tutte le query, la sua esecuzione avverrà nel ReferencedObject. Tale componente è disponibile su tutte le liste prodotte con i “PersistentFilters”. Richiamo rapido a query predefinite tramite combo Un combo contenente i nomi delle query utente definite come preferite, è posizionato nella lista dei risultati ed integrato nel messaggio relativo allo stato della query. Al cambio della selezione viene invocata la query selezionata. Se la query eseguita non è tra le preferite, questa viene aggiunta temporaneamente nel combo per coerenza con il messaggio. Ordinamento a richiesta sulle liste E’ disponibile l’ordinamento a richiesta da parte dell’utente sul risultato di una lista o query utente. Quando l’utente clicca sull’intestazione della colonna viene attivato l’ordinamento sulla stessa, visualizzato con un’icona tipo ; cliccando nuovamente si ottiene l’ordinamento inverso; per tornare alla situazione iniziale è necessario rieseguire la query con l’operazione di reload. Se la lista conta una sola pagina, tutti i risultati solo sul client e l’ordinamento viene eseguito sul browser, altrimenti viene rieseguita la query con l’aggiunta della condizione di ordinamento; tale condizione non si aggiunge ma sostituisce eventuali condizioni di ordinamento cablate o definite sulla query. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 50 di 167 Formato visualizzazione liste E’ possibile variare dinamicamente la visualizzazione della lista per una più agevole consultazione in caso di colonne numerose o intestazioni molto lunghe. La funzione è attivabile espandendo o cliccando su una intestazione e mediante un click nella prima cella in alto a sinistra è possibile scegliere (ciclicamente) fra diverse modalità di visualizzazione (visualizzazione standard, ridotta ai primi 3 caratteri, minima, su più righe e successivamente di nuovo la standard). La modalità scelta è valida per tutte le liste di tipo PersistentFilters ed è memorizzata nel cookie dell’applicazione. Cancellando la cache del browser o puntualmente il cookie viene ripristinata la visualizzazione standard. Ricerca nelle liste Permetta la la gestione della ricerca all’interno delle liste. Dopo aver eseguito una query, essa e’ attivabile con due modalità: 1) facendo click sul pulsante di ricerca presente nella toolbar delle liste. 2) facendo click su una colonna per selezionarla e usando la combinazione di tasti Ctrl ed F. La window di ricerca © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 51 di 167 contiene i seguenti campi: il testo da cercare; il check button Intera Stringa (Whole Word) che permette di specificare se cercare l’intera parola; il check button Maiuscolo/Minuscolo (Case Sensitive) che specifica se la ricerca deve essere o meno di tipo CASE Sensitive; il pulsante Cerca (Find): facendo click su di esso viene effettuata la ricerca e viene visualizzata la pagina che contiene il testo ricercato; il pulsante Evidenzia (Highlight): facendo click su di esso le celle della tabella contenenti il testo ricercato verranno evidenziate in giallo (la ricerca viene effettuata esclusivamente sulla vista corrente); la combo box “Seleziona colonne” contiene la lista di tutti i campi della tabella ottenuti dalla response. In particolare, se è utilizzata la prima modalità di attivazione della window di ricerca (click su pulsante ) il campo selezionato in lista è “All columns” e la ricerca viene effettuata sull’intera tabella; se è utilizzata la seconda modalità di attivazione (click su colonna e Ctrl-F) verrà automaticamente selezionata la colonna in questione e la ricerca verrà esclusivamente effettuata sulla stessa. Funzionalità: Export del risultato di una query La funzione di export permette di scegliere il tipo di file su cui esportare i dati (excel, xml, csv e csv123), il formato, il numero di record da considerare, etc Funzionalità: Settaggi di stampa © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 52 di 167 Questa funzionalità che permette di settare le caratteristiche della stampa per una determinata query. E' possibile settare: lo stile del file con una delle seguenti opzioni: master.xls, masterBlue.xls e masterRed.xls; il formato della pagina con formato A4, Letter, Legal, Folio, Quarto, Note, A3, A2. E' inoltre possibile inserire una nota e un messaggio che verranno inseriti nel frontespizio del documento stampato. 2.5.5.1 Referenced Objects Di seguito vi sono una serie di indicazioni per l'utilizzo ottimale del componente Referenced Object. Esempio di attivazione semplificata ReferencedObject Si può attivare il referencedObject specificando solo diz["RO_Sample2Customer"]='{"file":"AAC0","key":["cdcli","rascl"],"show":["stato", "cdcli","rascl"] }' omettendo gli attrname dei campi in cui scaricare i valori (refFields); gli attrname saranno desunti dall’attrname del campo su cui è stato attivato il referencedObject (CDCSO2 e CDCSO2_xdescr) Questa modalità d’uso richiede che la parte servente mappi l’attributo della descrizione CDCSO2_xdescr. con Esempio di passaggio parametri al ReferencedObject E’ possibile invocare la lista del RO passando uno o più valori di parzializzazione, definibili inline. As esempio per implementare una lista “a partire da”, il file xml sottostante al refobj contiene una additionalWhere del tipo " ANCL200F.CDCLI like '?%' " e il dato da passare è contenuto nel campo con attrname CDCSO2. diz["RO_Sample2GenericCustomer"]='{ "file":"AAC0a", "key":["cdcli","rascl"], "show":["stato","cdcli","rascl"] }' diz["FLD_Sample2Customer"]='{"attrname":"CDCSO2", "labelStyle":"left", "mandatory":"true" , "pf":"RO_Sample2GenericCustomer", "pfparms":"[\'CDCSO2\']", © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 53 di 167 "onblur":"xcheck('+"'"+'URL_SampleCustomerServlet'+"'"+')" "descr":"FLD_GenericDescription50" } '; , Altre modalità di utilizzo del ReferencedObject Mediante il doppio click sul titolo del ReferencedObject, è possibile massimizzarlo a tutto schermo. E' possibile definire: i campi che compongono la chiave del record selezionato i campi in cui scaricare i valori della chiave del record selezionato le colonne da visualizzare la where diz["RO_SampleCustomer"]='{ "file":"AAC0", ’+ ‘ "key":["cdcli","rascl","procl"], ’+ ‘ "refFields":["CDCSO", "CDCSO_rascl"], ’+ ‘ "show":["loccl","cdcli","rascl"], ’+ ‘ "where":" cdcli like \'!key%\' " , ’+ ‘ "sort":"loccl DESC" }'; Attivazione funzione di gestione query utente per il ReferenceObject E’ possibile richiamare la funzione di “Gestione query utente” dal referenced Object che apparirà in una finestra separata. Attraverso la “Gestione query utente” sarà possibile creare, modificare le query utente associate a quel ReferencedObject (più precisamente le query utente associate al file .xml associato al ReferencedObject). In questo modo sarà possibile creare delle query più vicine all’uso dell’utente finale, con eventuali parzializzazioni ed ordinamenti, ed averle a disposizione nel combobox delle query preferite. Richiamo rapido a query preferite mediante combobox per il ReferenceObject Si fa notare la presenza di un combobox contenente i nomi delle query utente definite come preferite. Il componente è posizionato nella lista ed è integrato nel messaggio relativo allo stato della query. Al cambio della selezione viene invocata la query selezionata. Se la query eseguita non è tra le preferite, questa viene aggiunta temporaneamente nel combo per coerenza con il messaggio. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 54 di 167 © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 55 di 167 2.5.5.2 Navigazione E’ la possibilità fornita all’utente di passare da un set di informazioni ad un altro secondo regole predefinite. Per informazione di partenza si intende ad esempio la chiave di un record in una lista, il contenuto di un campo, un’azione,…L’informazione di arrivo può essere una nuova lista, il risultato di un calcolo, il lancio di una funzione, ecc. I percorsi e le modalità di ogni navigazione sono definiti nel dizionario. Di seguito sono presentati due esempi di navigazione fra liste: il primo, semplificato, è realizzato con il passaggio dei campi definiti come chiave nella lista di partenza. Nel secondo esempio viene passato come parametro il contenuto di una o più celle del record selezionato nella lista di partenza. Esempio 1 Supponiamo di realizzare un percorso di navigazione che lista dei clienti lista bolle per il cliente selezionato lista delle righe per la bolla selezionata lista delle registrazioni contabili per il cliente selezionato e di rendere tale percorso accessibile dal menu dell’applicazione. Innanzitutto ricordiamo che nel dizionario la relazione fra nodo e sottonodi per strutture ad albero (quali il menu dell’applicazione, il popup menu o la navigazione)è realizzata secondo la sintassi diz["NAV_nomeLogicoOggetto"]='nomeLogicoAzione[|nomeLogicoAzione]'; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 56 di 167 Aggancio al menu ad albero Nel nostro esempio si deve aggiungere un nodo al 4SamplesNAVMenu con una sola azione: la lista dei clienti: menu ad albero dell’applicazione con identificativo // 4SamplesMenu diz["NAV_4SamplesMenu"]="4AppSamplesMenu|4SamplesWINMenu|4SamplesLinksMenu|4SamplesNAVMenu"; diz["LBL_IT_4SamplesMenu"]="Esempi"; Da questo nodo vogliamo invocare la lista dei clienti: diz["NAV_4SamplesNAVMenu"]='PFAAC0'; diz["LBL_IT_4SamplesNAVMenu"]="Navigazione"; La lista dei clienti si ottiene definendo PFAAC0 come richiamo di una action di tipo PF che fa uso di un file di definizione xml AAC0 presente nella cartella <acgv4>\WEB-INF\classes\com\ibm\acg\xml che sulla lista da produrre siano imposti dei campi chiave (anche diversi da quelli impostati nel file di definizione) che sia applicata una condizione di ordinamento che sulla lista prodotta associ un menu di navigazione (PFAAC0menu) // navigazione tipo definita su informazioni di tipo “Cliente” diz['ACT_PFAAC0']='{id:"PFAAC0", type:"PF", file:"AAC0", "key":["cdcli"], "show":["cdcli","rascl"], "sort":"cdcli", "menu":"PFAAC0menu" }'; diz['LBL_IT_PFAAC0']="Lista Clienti"; Sulla lista dei clienti abbiamo attivato la navigazione PFAAC0menu che ora andremo a definire diz["NAV_PFAAC0menu"]='PFBOTE|XCLGX05'; In maniera analoga ora devo definire l’azione PFBOTE e le sue caratteristiche. PFBOTE usa come file file BOTE.xml con 8 colonne da visualizzare (proprietà show) con 2 colonne che sono da considerarsi campi chiave su questa lista (proprietà key), con una condizione di ordinamento su 2 colonne e con una selezione fatta usando la chiave passata. diz["LBL_IT_PFBOTE"]="Bolle per cliente"; diz['ACT_PFBOTE']='{id:"PFBOTE", type:"PF", file:"BOTE", "key":["nrreb","anneb"], "show":["tpdcb","cdcsb","nrreb","anneb","dattb","rareb","gbatb","attnb"], "sort":"tpdcb DESC,cdcsb " , "where":" cdcsb = \'!key\' " , "menu":"PFBOTEmenu" }'; Per il menu PFBOTEmenu indicato sono definiti i percorsi di navigazione possibili: diz["NAV_PFBOTEmenu"]='PFBORI'; L’azione PFBORI viene definita in questo modo: file BORI.xml con condizione di where sui 2 campi chiave passati diz["LBL_IT_PFBORI"]="Righe bolla"; diz['ACT_PFBORI']='{id:"PFBORI", type:"PF", file:"BORI" , "where":" nrres = !key AND annes = \'!key\' " }'; Applicando le regole suddette si potrà definire l’altra navigazione (lista delle registrazioni contabili per il cliente selezionato). © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 57 di 167 Navigazione fra liste passando il contenuto di una o più celle E’ possibile definire una navigazione più flessibile rispetto a quella standard che, ricordiamo, è basata sul passaggio di informazioni che sono definite come chiave del record selezionato. In questa nuova modalità non viene passata la chiave ma il contenuto di alcune celle del record selezionato, in base ad una definizione presente nel dizionario. Prima di tutto si definisce sulla lista di partenza il menu contenente tutte le navigazioni possibili (standard e non). La sintassi di tale definizione rimane la stessa diz["NAV_nomeLogicoOggetto"]='nomeLogicoAzione[|nomeLogicoAzione]'; come pure la definizione della lista di partenza e quella di arrivo. La novità consiste nella presenza nel dizionario di una voce che nel suo nome lega la lista di partenza e quella di arrivo e il cui valore è una lista di nomi di colonne nel seguente formato diz[“URL_PARMS_<listaPartenza>-<listaArrivo>]= [ “<nometabella>.<nomecampo1>” [,“<nometabella>.<nomecampoN>”] ] Esempio: Dalla lista delle righe dei documenti bolla si vuole risalire ai movimenti a magazzino relativi all’articolo e al magazzino del record corrente alla lista dei clienti dell’agente presente nella riga del documento bolla. La lista delle righe dei documenti bolla non ha chiave predefinita. Si realizza un menu contestuale ("menu":"PFBORI") sulla lista delle righe bolle PFBORI. Questo menu ha 2 navigazioni (diz["NAV_PFBORI"]='PFMOMAARTMAG|PFCLAG';) Le navigazioni sono azioni di tipo PF con le condizioni di where opportune. I parametri da passare sono definiti nel dizionario (es. diz["URL_PARMS_PFBORI-PFCLAG"]) // --- PFCLAG diz["LBL_EN_PFCLAG"]="Agent's customers"; diz["LBL_IT_PFCLAG"]="Clienti dell'agente"; diz['ACT_PFCLAG']='{id:"PFCLAG", type:"PF", file:"AAC0" "where":" cdage = \'!key\' " , "menu":"PFBORI" }'; , "show":["cdcli","rascl","loccl","procl"], "sort":"procl", // lista campi chiave da passare a questo filtro, provenienti da altra lista // diz["URL_PARMS_<lista di partenza>-<lista di arrivo>"]=' [ "<campo lista di partenza>" [,"<campo lista di partenza>"] ] '; diz["URL_PARMS_PFBORI-PFCLAG"]=' [ "BORI200F.CDAGS" ] '; // --- PFMOMAARTMAG diz["LBL_EN_PFMOMAARTMAG"]="Warehouse mov for item and warehouse"; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 58 di 167 diz["LBL_IT_PFMOMAARTMAG"]="Movimenti magazzino per articolo e magazzino"; //esempio navigazione da 2 celle non chiave diz['ACT_PFMOMAARTMAG']='{id:"PFMOMAARTMAG", type:"PF", file:"MOMA" , "show":["cdpar","cdmag","nrmov","cddet","cdcom"], "sort":"cdpar, cdmag", "where":" cdpar = \'!key\' AND cdmag = \'!key\' " }'; // lista campi chiave da passare a questo filtro, provenienti da altra lista // diz["URL_PARMS_<lista di partenza>-<lista di arrivo>"]=' [ "<campo lista di partenza>" [,"<campo lista di partenza>"] ] '; diz["URL_PARMS_PFBORI-PFMOMAARTMAG"]=' [ "BORI200F.CDARS","BORI200F.CDMGS" ] '; // --- PFBORI diz["LBL_EN_PFBORI"]="Shipping doc rows"; diz["LBL_IT_PFBORI"]="Righe bolla"; diz['ACT_PFBORI']='{id:"PFBORI", type:"PF", file:"BORI" , "where":" nrres = "menu":"PFBORI" }'; !key AND annes = \'!key\' " , // menu sulla lista PFBORI diz["NAV_PFBORI"]='PFMOMAARTMAG|PFCLAG'; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 59 di 167 2.5.5.3 Liste con selezione multipla Questo comportamento è attivabile impostando la proprietà "selection":"multiple" nella definizione di un pannello che deve ospitare una lista. La stessa modalità può essere usata nelle liste usate nella navigazione; in questo caso è la definizione della ACTion di tipo lista che conterrà la proprietà selection. La richiesta al server conterrà il parametro di nome sel con la serie delle chiavi dei record selezionati. Esempio di log della richiesta per le selezioni effettuate sull’immagine precedente param:xform 1_F0_WIN_Sample3List_Sample5SelectableList param:xwin 1_F0_WIN_Sample3List param:sel 222 param:sel 3 altriparametri … Esempio di attivazione per un pannello che contiene una lista In un pannello contenitore sono definite due liste, la seconda di queste ha la proprietà per la selezione multipla attivata. Definizione del pannello SampleList: diz["URL_SampleList"]=''+xPFContext+'/pf/XGenericTable.jsp?queryDefinitionFileName=com/ibm/acg/xml/XRRCT.xml& '; diz["PNL_SampleList"]='{ "id":"myId", "label":"LIST", "init":"true", "style":"height:100%;width:100%;" ' +' , "url":"URL_SampleList", "menu":"MNU_SampleList" } '; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 60 di 167 Utilizzo del pannello SampleList nel pannello contenitore attivando la selezione multipla: diz["PNL_Sample3ContainingPanel"]='{ "style":"position:relative;width:100%;height:100%;", ' "id":"Sample3ContainingPanel", +' "formId":"Form1", ' +' "collapsed":"false", label:"Pannello in pannello", ' +' "panels":[ ' +$d2("PNL_Sample3List",' "id":"Pannello2", "label":"Sample3", … ) +',' +$d2("PNL_SampleList", ' "id":"Sample5SelectableList", "selection":"multiple", collapsed:"true" ') …} Esempio di attivazione su una lista usata nelle navigazioni Supponiamo di avere una navigazione fra liste e su una delle liste interessate si desideri attivare la selezione multipla per poter eseguire una certa operazione (presente nel flying menu) su più record. La lista delle bolle ottenuta mediante una ACTion di tipo PF avrà la selezione multipla per supportare per esempio la ristampa delle stesse (azione presente nel menu PFBOTE) e che sul server potrà eseguire l’operazione usando le chiavi dei record selezionati passati come valori multipli del parametro sel. diz["LBL_EN_PFBOTE1"]="Shipping doc for customer"; diz["LBL_IT_PFBOTE1"]="Bolle per cliente "; diz['ACT_PFBOTE1']='{id:"PFBOTE", type:"PF", file:"BOTE", "selection":"multiple", "key":["nrreb","anneb"], "show":["tpdcb","cdcsb","nrreb","anneb","dattb","rareb","gbatb","attnb"], "sort":"tpdcb DESC,cdcsb DESC" , "menu":"PFBOTE" }'; Selezione multipla impostabile via programma E’ possibile selezionare/deselezionare le scelte su una lista a selezione multipla dinamicamente a programma. Vi sono 2 metodi; il primo seleziona/deseleziona tutti i checkbox, il secondo opera sul singolo record identificato attraverso la sua chiave. xselectAll(<nome form>, true|false) xselect(<nome form>,<chiave>,true|false) Esempio: <action name="xselectAll('1_F0_WIN_RdPList_RdPList',true)"/> <action name="xselect('1_F0_WIN_RdPList_RdPList',100,true)"/> Selezione multipla sul ReferencedObject © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 61 di 167 E’ possibile associare ad un campo di input standard o di tipo TEXTAREA un referencedObject con la possibilità di scelta multipla. Cliccando sui checkbox e alla conferma la lista delle chiavi dei record selezionati vengono riversati nel campo di riferimento. La lista è una stringa unica in cui le chiavi sono separate dalla , Esempio di risultato chiave1[,chiaveN] dove la chiave è una stringa. Si ricorda che nel caso di chiavi composte da più informazioni esse sono del tipo chiaveParte1[|chiaveParteN] Il componente ReferencedObject a selezione multipla contiene nella prima cella delle intestazioni un checkbox che Seleziona/deseleziona tutto Inverte la selezione effettuata 2.5.5.4 Rielaborazione Liste Questo comportamento consente di rielaborare il risultato di una query e di evidenziare il contenuto informativo di una colonna o della singola cella, di attivare funzioni specifiche. E’ attivabile inserendo una definizione nel dizionario che faccia riferimento alla colonna. Essa può essere identificata da <queryId>_<xmlfile>_<nomeTabella>.<nomeColonna> © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 62 di 167 La definizione sarà valida solo per la query specificata sul file xml indicato <xmlfile>_<nomeTabella>.<nomeColonna> La definizione sarà valida solo tutte le query sul file xml indicato <nomeTabella>.<nomeColonna> La definizione sarà valida sempre, per la colonna indicata In questo modo si possono attivare comportamenti diversi in base alle diverse esigenze applicative. Ad esempio è possibile: modificare lo stile di una intera colonna (sfondo, colore testo,…) modificare lo stile a livello di singola cella di una colonna aggiungere una icona (il logo della società) sulla destra o sulla sinistra del testo di una cella sostituire completamente il valore della cella (lo stato il cui valore è I di IMMESSO) con una icona, mantenendo l’accessibilità al valore attraverso il flying-text sostituire/aggiungere una icona che è un bottone per lanciare funzioni specifiche con il passaggio di parametri (valore della cella corrente e chiave del record selezionato) Nell’esempio in figura vi è una sostituzione sulla “colonna codice cliente di spedizione” con una aggiunta sulla destra di una icona (il logo dell’azienda). Sulla colonna “stato” è invece impostata una sostituzione mediante funzione che restituisce una sostituzione con uno stile con sfondo verde chiaro per lo stato blank e una sostituzione del valore I con l’icona relativa. eseguire le operazioni di sostituzione sopra descritte come risultato di una funzione (scritta in javascript) che modifichi le caratteristiche delle singole celle di una colonna. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 63 di 167 Nell’esempio che segue sulla colonna “fido” è impostata una sostituzione che fa uso di una funzione che restituisce una definizione di sostituzione diversa a seconda che il valore sia <200 (sfondo grigio), <1000 (sfondo giallo), >1000 (sfondo rosso, testo bianco). Inoltre è stata attivata l’opzione action che permette di rendere l’icona come un bottone e di associare l’invocazione di un metodo javascript. Nell’esempio cliccando sul bottone si ottiene un messaggio di conferma (con il passaggio del valore della cella e della chiave del record selezionato) e successivamente il lancio di una gestione ACGv4 con il passaggio di tali parametri. Esempi: Sintassi: diz["REF_[<queryid>_][<xml>_]<table>.<column>"] = '{ ["type":"icon", "src":"<iconsrcReference>",] ["position":"right|left",] ["style":"...",] ["func":"<functionReference>"] }'; // OVR CDCSO // replace text with icons mapped through in a ACGV4 relative src path diz["REF_RRCT3V4F.CDCSO"]='{ "type":"icon", "position":"right", "src":"RRCT3V4FCDCSO" }'; diz["REF_RRCT3V4FCDCSO"]="/products/E01/images/logo_!value.gif"; // replace text with icons mapped through in an external src path © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 64 di 167 diz["REF_RRCT3V4F.CDCSO"]='{ "type":"icon", "position":"right", "src":"RRCT3V4FCDCSO" }'; diz["REF_RRCT3V4FCDCSO"]="http://127.0.0.1:9080/acgv4/products/E01/images/logo_!v alue.gif"; // OVR campo FLIMO di RRCT3V4F // change the text style, for the column, for all xml, for all queries diz["REF_RRCT3V4F.FLIMO"]='{ "style":"background:#BBFFBB;color:#FF0000;" }'; // change the text style, for the column, for an xml, for all queries diz["REF_XRRCT.xml_RRCT3V4F.FLIMO"]='{ "style":"background:#BBFFBB;color:#FF0000;" }'; // change the text style, for the column, for an xml, for DEFAULT query diz["REF_DEFAULT_XRRCT.xml_RRCT3V4F.FLIMO"]='{ "style":"background:#BBFFBB;color:#FF0000;" }'; change the text style, for the column, for an xml, for a query named ovr diz["REF_ovr_XRRCT.xml_RRCT3V4F.FLIMO"]='{ "style":"background:#BBFFBB;color:#FF0000;" }'; // replace text with icons mapped through values diz["REF_XRRCT.xml_RRCT3V4F.FLIMO"]='{ "type":"icon", "src":"RDPSTATUS" }'; // replace text with icons mapped through values, with a style diz["REF_XRRCT.xml_RRCT3V4F.FLIMO"]='{ "src":"RDPSTATUS", "type":"icon", "style":"background:#FFFFBB;color:#FF0000;" }'; // replace text with icons mapped through values, appending it before text diz["REF_XRRCT.xml_RRCT3V4F.FLIMO"]='{ "src":"RDPSTATUS", "type":"icon", "position":"left", "style":"background:#BBFFBB;color:#FF0000;" }'; // replace text with icons mapped through values, appending it after text diz["REF_XRRCT.xml_RRCT3V4F.FLIMO"]='{ "src":"RDPSTATUS", "type":"icon", "position":"right", "style":"background:#BBFFBB;color:#FF0000;" }'; // values mapped to image names, if extension is not defined, .gif appended diz["REF_RDPSTATUS_D"]="/images/iDraft"; diz["REF_RDPSTATUS_I"]="/images/iSubmit"; diz["REF_RDPSTATUS_V"]="/images/iOK"; diz["REF_RDPSTATUS_"]="/images/iDef"; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 65 di 167 // replace text with an override definition via a js function diz["REF_XRRCT.xml_RRCT3V4F.FLIMO"]='{ "func":"RDPSTATUSFUNC" }'; diz["REF_RDPSTATUSFUNC"]="_checkRDPSTATUS('!value','!key')"; function _checkRDPSTATUS(aValue,aKey){ //confirm('\n_RDPSTATUSFUNC:\n'+aValue+'\n'+aKey); switch (aValue) { case 'I': return '{ "src":"RDPSTATUS", "type":"icon", "style":"background:#BBFFBB;color:#FF0000;" }'; break; default: return '{ "src":"RDPSTATUS", "style":"background:#EEFFEE;color:#FF0000;" }'; break; } } // replace text with an override definition via a js function // attiva solo sulla query fido diz["REF_fido_AAC0.xml_ANCL200F.FIDOCE"]='{ "func":"FIDOCLIENTE" }'; // solo su tutte le query diz["REF_AAC0.xml_ANCL200F.FIDOCE"]='{ "func":"FIDOCLIENTE" }'; diz["REF_FIDOCLIENTE"]="_checkFIDO('!value','!key')"; function _checkFIDO(aValue,aKey){ //xshowMsg('\n _checkFIDO :\n'+aValue+'\n'+aKey+'...'+parseInt(aValue)); if(parseInt(aValue.replace(/$$thouSep/gi,''))>500){ return '{ "type":"icon", "src":"/images/_err.gif", "position":"right", "style":"background:#FF0000;color:#FFFFFF;", "action":"_warningFIDO(\'!key\',\'!value\')" }'; } else{ if(parseInt(aValue)>200){ return '{ "style":"background:#F9F793;color:#000000;" }'; } else{ return '{ "style":"background:#EEEEEE;color:#000000;" }'; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 66 di 167 } } } function _warningFIDO(aKey,aValue){ if(confirm('Attiva procedura fido eccessivo (' + aValue + ' EUR) \n cliente '+aKey+' ')){ xcrtWIN('F0','WIN_Sample2', '['+aKey+']'); } } © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 67 di 167 2.5.6 Componente HeaderRows 2.5.6.1 Definizione di un oggetto HeaderRows Un pannello di tipo headerRows è un panello che contiene una lista di righe, che non necessariamente sono il risultato di una query su database (come ad esempio le liste di tipo Persistent Filters). Le righe di un pannello hr, posso essere ad esempio il risultato di una elaborazione, il contenuto di un file di lavoro o rappresentare degli oggetti di business. Per creare un pannello hr è necessario dichiarare nel dizionario un oggetto PNL con property type=”headerRows”, ad esempio: diz["PNL_HEADERROWS"]='{"id":"HEADERROWS", "type":"headerRows","style":"overflow:auto;width:100%;height:100%;",' +'"linked":"DETAILP","rowsForPage":"4","menu":"MNU_HeaderRows",' +'"collapsed":"false",' +'"fields":['+$d2("FLD_CampoRow1") +','+$d2("FLD_CampoRow2") +','+$d2("FLD_CampoRow3") +','+$d2("FLD_CampoRow4") +','+$d2("FLD_CampoRow5") +','+$d2("FLD_CampoRow6") +']' +', "actions":[ ' + $d("ACT_Newrow") +','+ $d("ACT_EditRow") + ','+$d("ACT_Deleterow") +'] }'; Nella situazione più semplice un pannello hr contiene campi e azioni. I campi rappresentano le colonne della lista, le azioni sono i bottoni della toolbar della lista. Le proprietà che caratterizzano un pannello hr sono: checkbox: se a false viene nascosto il checkbox sulla prima colonna linked: il pannello di dettaglio della riga (se esiste) rowsForPage: numero righe max per pagina menu: il menu contestuale sulla lista editable: se a true, la lista è editabile rowNumber: se è uguale a true, viene visualizzato un contatore di righe 2.5.6.2 Gestione di una lista HeaderRows lato Controller Vediamo ora come gestire lato server una lista di tipo hr, ovvero cosa è necessario scrivere nella action che gestisce la window, per caricare le righe della lista e lavorare su di esse (creare una nuova riga, modificarla, cancellarla ecc.). 2.5.6.3 Response xml I dati necessari a gestire il pannello di tipo headerRows sono contenuti nella sezione della response <hr>...</hr>. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 68 di 167 2.5.6.4 Caricamento iniziale delle righe della lista HeaderRows Per caricare o forzare il ricaricamento di tutte le righe della lista, è necessario valorizzare il parametro datas della response tramite il metodo setHeaderRowsParms(actionForm, numberOfElements, currentPage, datas) della classe AddRowInfo: AddRowInfo addrinfo = getRowInfo(request,actionForm); oppure AddRowInfo addrinfo = getRowInfo(request,actionForm,id del pannello hr); nel caso sia necessario indicare esplicitamente l’id del pannello di tipo headerRows (ad esempio se si voglio gestire più liste in una stessa response o perché il valore di xform è blank). addrinfo.setHeaderRowsParms(actionForm, "" + element.getRows().size(), "1", getDatas(element),"");; Nell’ultima parametro (datas) deve essere passata la stringa che contiene le informazioni delle righe, opportunamente formattate. Se il parametro datas è uguale a blank, le righe non vengono rimosse. Per forzare la rimozione di tutte le righe della lista, è necessario richiamare il metodo xclearRowList (v. lista funzioni) come PREACTIONS/POSTACTIONS. 2.5.6.5 Formattazione della stringa dati da inviare alla lista Il valore da passare al parametro datas (xdatas) deve essere formattato secondo la seguente sintassi: ogni riga della lista deve essere separata dalla stringa “~;;” ogni colonna della singola riga deve essere separata dal carattere “~” nella prima posizione della riga va indicata la chiave (se esiste), che si vuole associare alla riga. Se non viene indicata, ogni riga verrà identificata con la posizione sulla lista. In caso di chiavi multiple, per convenzione ogni chiave viene separata dal carattere “|” nella seconda posizione può essere indicato il title, se non indicato, verrà utilizzata come title la chiave. Tale valore viene utilizzato nel menu del pannello a seguire vanno inseriti i valori delle colonne Ad es. per una lista di 4 colonne ogni riga dovrà essere così formata: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 69 di 167 chiave1|chiave2~title1|title2|title3~valore_colonna1~val_col2~val_col3~val_col4~;; Se non si vuole indicare un text-title: chiave1|chiave2~~valore_colonna1~val_col2~val_col3~val_col4~;; Se non si vuole indicare né il title né la chiave della riga: ~~valore_colonna1~val_col2~val_col3~val_col4~;; Per formattare secondo tale sintassi le righe da inviare ad una lista, sono disponibili alcune utility. In particolare viene fornita la classe ListCreator che permette di creare e viceversa di leggere la stringa delle righe della lista. Se quindi dobbiamo preparare il valore da passare al parametro datas possiamo definire nell’action un metodo getDatas() che ci restituirà la stringa delle righe, formattata secondo la sintassi precedentemente descritta. Questo metodo prenderà in input l’oggetto della testata da cui reperire tutte le righe della lista: public String getDatas(Rrct300f header){ Collection rows=header.getRows(); ListCreator lc = new ListCreator(); if (rows == null || rows.size() == 0) return ""; Rrcr300f elem = null; for (Iterator it = rows.iterator(); it.hasNext();) { elem = ((Rrcr300f) it.next()); addEntryToList(elem, lc); } return lc.getData(); } All’interno del metodo, ciclando sulle righe associate alla testata, viene richiamato il metodo addEntryToList per ogni riga. Tale metodo, da implementare nella action, formatta le informazioni delle righe tramite ListCreator: private void addEntryToList(Rrcr300f row, ListCreator lc) { lc.addEntry(); lc.addKey(""+row.getId().getNrrmr()).addKey(""+row.getId().getNrimr()); lc.addTitle(""+row.getId().getNrrmr()) .addTitle(""+row.getId().getNrimr()); lc.addValue(""+row.getId().getNrrmr()) © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 70 di 167 .addValue("" + row.getDtcrr()) .addValue(row.getCdarr().trim()) .addValue(row.getDsr1r().trim()) .addValue(row.getUnmsr().trim()) .addValue(""+row.getQtorr()); } In questo esempio ogni riga ha una chiave composta (nrrmr e nrimr) e la lista sarà composta di sei colonne. La stringa restituita dal metodo getDatas andrà a valorizzare la property datas della sezione <rows></rows> nella response, ad esempio: Per effettuare l’operazione inversa, ovvero reperire le informazioni delle righe a partire dalla stringa che ci arriva dal client, possiamo implementare nella Action la funzione getEntriesFromList: private HashMap getEntriesFromList(String datas) { HashMap hm = new HashMap(); String[] rows = datas.trim().split(“~;;”); for (int i=0;i<rows.length;i++) { ListCreator lc = new ListCreator(); lc.addEntry(); lc.addEntryFromString(rows[i]); © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 71 di 167 Rrcr300fId key = new Rrcr300fId(); key.setNrrmr((new Integer(((String)lc.getKey(1)).trim())).intValue()); key.setNrimr((new Integer(((String)lc.getKey(1)).trim())).intValue()); Rrcr300f row = null; row = new Rrcr300f(); row.setId(key); if (lc.getValue(1)!=null) row.setDtcrr((new Integer(((String)lc.getValue(1)).trim())).intValue()); if (lc.getValue(2)!=null) row.setCdarr(((String)lc.getValue(2)).trim()); if (lc.getValue(3)!=null) row.setDsr1r(((String)lc.getValue(3)).trim()); if (lc.getValue(4)!=null) row.setUnmsr(((String)lc.getValue(4)).trim()); if (lc.getValue(5)!=null) row.setQtorr(new BigDecimal(((String)lc.getValue(5)).trim())); hm.put(lc.getKeys(), row); } return hm; } Nella funzione, per prima cosa viene divisa la stringa nelle stringhe delle singole righe (tramite il metodo split a cui viene passata la stringa separatore delle righe ‘~;;’), e, per ogni stringa di riga, viene invocato il metodo addEntryFromString della classe ListCreator. A questo punto è possibile reperire le informazioni della riga tramite i metodi getKey, getTitle e getValue sull’oggetto ListCreator opportunamente inizializzato. L’indice da passare a tali metodi, rappresenta l’ordine con cui si sono inserite le informazioni nel metodo addEntrytoList. 2.5.6.6 Inserimento o modifica di una riga della lista Per inserire nuova riga o modificare una riga esistente della lista è necessario utilizzare la classe AddRowInfo. Ad esempio: AddRowInfo rowInfo = getRowInfo(request, actionForm); rowInfo.addData(actionForm, ""+getDatas(row, logon),""+ row.getId().getNrrmr(), ""); in questo caso si sta inserendo una riga della lista, passando nel metodo addData il valore della riga come secondo parametro e la posizione dove inserire la riga nella lista come terzo parametro. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 72 di 167 Se si voleva invece aggiungere la riga sempre alla fine della lista: rowInfo.addData(actionForm, ""+getDatas(row, logon),"", ""); mentre per emettere la riga in stato draft o in errore: rowInfo.addData(actionForm, ""+getDatas(row, logon),"", AddRowInfo.STATUS_DRAFT); rowInfo.addData(actionForm, ""+getDatas(row, logon),"", AddRowInfo.STATUS_ERROR); Nella response verrà aggiunto un elemento <row></row> per ogni riga aggiornata/aggiunta, nella sezione <rows></rows> della lista opportuna. Ad esempio: Se invece si vuole inserire la riga in una posizione particolare della lista, si può indicare nel metodo addData il numero di riga. Per modificare una riga esistente, si deve utilizzare lo stesso metodo, con la differenza di indicare come chiave della riga la chiave della riga da modificare. In questo caso il componente aggiornerà la riga esistente nella lista con i nuovi valori di colonna passati. 2.5.6.7 Cancellazione di una riga Esistono due modalità per cancellare una o più righe della lista: 1) utilizzare il metodo xdeleterow (precedentemente descritto) passando il metodo della action che dovrà gestire la cancellazione della riga/righe. La lista di chiavi delle righe selezionate per la cancellazione verrà passata nel parametro KEYS della request (ogni chiave è separata dalla stringa ~;;). All’interno del metodo indicato sarà poi necessario, per ogni riga la cui cancellazione ha avuto esito positivo, richiamare il metodo addData, passando la chiave della riga cancellata. Questo è necessario per indicare nella response la lista delle righe da rimuove sulla lista. 2) impostare lo stato della riga a DELETE tramite il metodo addData © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 73 di 167 2.5.6.8 Gestione del pannello piede Per gestire un pannello di dettaglio delle righe si può impostare la proprietà linked sia sul pannello lista che sul pannello piede. Tale proprietà serve ad indicare un legame tra i due pannelli e deve contenere l’id del pannello piede nella lista, e viceversa l’id del pannello lista nel piede. Ad esempio: diz["PNL_OperationsRowsList"]='{ "id":"OperationsRowsList", ' + ' "label":"OperationsRowsList", ' + ' "linked":"OperationsRowPanel",' + ' "style":"height:150;width:100%;" , ' + ' "rowsForPage":"5" , ' + ' "dblclick":"xopenDetail()" ' + ', "type":"headerRows", ' + ' "fields":[ ' + $d2("FLD_OperationsRowId") + ',' + $d2("FLD_OperationsRowFlag") + ',' + $d2("FLD_OperationsStatus") + '] ' + ', "actions":[ ' + $d("ACT_OperNewrow") + ','+ $d("ACT_OperCancelrow") + ','+ $d("ACT_OperDelrow") + ']' + ' } '; diz["PNL_OperationsRowPanel"]='{ "id":"OperationsRowPanel", "label":"OperationsRowPanel", "formId":"Form2", "rows":"5", "cols":"5", "style":"position:relative;top:100;left:100;width:100%;height:100%;"' +',"enterAction":"SAVEROW",' +' "linked":"OperationsRowsList",' + ' "fields":[ ' + $d2("FLD_OperationsRowId", "row:1, col:0") + ',' + $d2("FLD_OperationsRowFlag", "row:1, col:1") + ']' + ', "actions":[ ' + $d("ACT_Operations_saverow") + ']' + ' } '; In una situazione standard di window che contiene testata-lista-piede, la testata e la lista saranno contenute in una unica form, il piede sarà gestito tramite una form differente. FORM1 TESTATA LISTA RIGHE FORM2 PIEDE © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 74 di 167 Nello struts-config saranno definite due action (una per gestire la testata e una per il piede) e le due form relative, la prima conterrà la lista dei campi della testata, la seconda quelli del pannello di dettaglio. Le funzioni che a partire dalla lista prevedono il caricamento dei dati nel pannello piede, dovranno fare riferimento alla form di quest’ultimo pannello. Ad esempio supponiamo di voler editare una riga della lista. Tramite bottone della lista o doppio click sulla riga (se non si tratta di lista editabile), richiamiamo la funzione xsendCheckedDataLista o xsendCheckedKeysList, a seconda se vogliamo passare al server tutti i dati della riga od esclusivamente la chiave della riga. diz["ACT_OperEditRow"]='{ "id":"OperEditRow","icon":"Edt", "label":"Modifica",'+ '"url":"xsendCheckedKeysList(\'../operateRowAction.do?xmethod=editRow\',\Operation sRowPanel\')" } '; Nella funzione si dovrà passare come primo parametro il metodo della action relativa alla form del piede e (se non è stata impostata la proprietà linked) come secondo parametro l’id della form indicata nel pannello piede. Questo ci permetterà di valorizzare nel metodo richiamato i campi della form del pannello piede. 2.5.6.9 Gestione di più liste nella stessa response E’ possibile gestire più liste di tipo hr all’interno di una stessa response indicando l’identificativo della lista nel metodo getRowInfo. Ad esempio: AddRowInfo addrinfo1 = getRowInfo(request,actionForm, "PanelList1"); addrinfo.setHeaderRowsParms(actionForm, "", "1", getDatas1(element),"");; AddRowInfo addrinfo2 = getRowInfo(request,actionForm, "PanelList2"); addrinfo.setHeaderRowsParms(actionForm, "", "1", getDatas2(element),"");; … nella response si avrà: quindi ci sarà all’interno della sezione hr, una sezione rows per ogni differente lista gestita dalla response. La property name sta ad indicare l’id della lista (o della form se non viene indicato) mentre in datas ci sarà la lista delle righe da caricare. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 75 di 167 2.5.6.10 Label dinamiche E’ possibile indicare lato server le label delle colonne della lista utilizzando il metodo addLabels dell’oggetto AddRowInfo. Anche la classe di utility ListCreator fornisce il metodo addLabel per creare la stringa di labels da inviare alla lista: ListCreator lc = new ListCreator(); lc.addLabel("Col 1").addLabel("Col 4").addLabel("Col 5").addLabel("Col 6"); 2").addLabel("Col 3").addLabel("Col addrowinfo.setHeaderRowsParams(actionForm, "5", "1", getDatas(), lc.getLabels()); 2.5.6.11 Modifica del menu di una riga Per modificare o aggiungere il menu associato ad una riga si possono utilizzare due modi: 1. richiamare come preaction/postaction il metodo xsetRowMenu 2. utilizzare uno dei seguenti metodi della classe AddRowInfo - addData(DynaActionForm form, String value, String line, String status, - String datas, String menu) addAfter(DynaActionForm form, String value, String arow, String status, String datas, String menu) Ad esempio: AddTreeInfo rowinfo = (AddTreeInfo)getTreeInfo(request, actionForm); rowinfo.addData(actionForm,key,"","", "","MNU_Treemenu1"); dove in key viene passata o la chiave della riga o il valore di tutta la riga se si vuole aggiornare o aggiungere una nuova riga. 2.5.6.12 Aggiunta di una riga in posizione successiva alla riga corrente Se si vuole tramite menu contestuale aggiungere una riga alla lista in posizione successiva alla riga corrente (ovvero alla riga dove è posizionato il menu), si può utilizzare il metodo: addrowinfo.addAfter(actionForm, lc.getData(), key, "", "", ""); passando nel secondo parametro il dati della nuova riga, e nel parametro key la chiave della riga sotto la quale si vuole aggiungere la nuova. La chiave della riga corrente può essere reperita nel parametro KEYS della request, se nel menu contestuale si è utilizzato il metodo xsendTitleList: diz["CMD_hr1"]='js:xsendTitleList(\'../TestHRAction.do?xmethod=addRow\')'; 2.5.6.13 Creazione di una finestra tramite menu contestuale Se si vuole lanciare la creazione della finestra utilizzando il menu contestuale della lista e passando l’informazione di riga, si deve: dichiarare un cmd del tipo © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 76 di 167 diz["CMD_hr1"]='js:xcrtWIN(\'F1\',\'WIN_Test1\',\'[!key]\' )'; aggiungere il cmd al menu contestuale della lista hr/tree dichiarare un campo nella proprietà parms della finestra creata (WIN_Test1), dove passare il parametro della riga. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 77 di 167 2.5.7 Lista Editabile Una lista editabile è realizzabile con un pannello di tipo headerRows, impostando il parametro editable a true. Le caratteristiche dei campi di input presenti nella lista (lunghezza, obbligatorietà, tipologia, ecc.) derivano dalle definizioni degli oggetti di tipo field contenuti nel pannello. diz["PNL_HEADERROWS"]='{"id":"HEADERROWS", "type":"headerRows",' +'"style":"overflow:auto;width:100%;height:100%;",' +'"linked":"DETAILP","rowsForPage":"4","menu":"MNU_HeaderRows",' +'"collapsed":"false","label":"Lista editabile",' +'"editable":"true",' +'"checkbox":"true",' +'"fields":[ +$d("FLD_CampoRow1") +','+$d("FLD_CampoRow2") +','+$d("FLD_CampoRow3") +','+$d("FLD_CampoRow4") +','+$d("FLD_CampoRow5") +','+$d("FLD_CampoRow6") +']' +', "actions":[ ' + $d("ACT_Newrow") +','+ $d("ACT_Saverow") +']' +'}'; 2.5.7.1 Dblclick All’evento di doppio click sulla riga, viene automaticamente associata la funzione di editazione della riga. Per sovrascrivere tale comportamento è necessario indicare un valore nella property dblclick del pannello. Ogni volta che una riga passa dallo stato di editazione allo stato di output, vengono richiamate le validazioni lato client sui campi della riga. 2.5.7.2 Onedit Se si voglio inviare invece i dati della riga al momento dell’uscita della riga dallo stato di editazione, è necessario valorizzare la property onedit. Il metodo indicato nella proprietà sarà richiamato ogni volta che una riga passa dallo stato di editazione allo stato di output. diz["URL_TreeChange"]='../ca/SchemiRiclassificaAction.do?xmethod=changeRow'; diz["PNL_ANKR200FTreesp"]='{"id":"ANKR200FTreesp", "label":"ANKR200FTreesp","init":"true", "rowsForPage":"3",' © Copyright ACG Srl 2014 Tutti i diritti riservati. "type":"headerRows", Pagina 78 di 167 +'"editable":"true","menu":"MNU_RiclassificaSpTreeMenu",' +'"onedit":"URL_TreeChange",' ... Nel metodo richiamato verranno passati i dati della riga (ovvero i valori dei campi) tramite il parametro DATAS. 2.5.7.3 Xsend Per reperire i dati della lista, si possono utilizzare le stesse funzioni disponibili sulle liste hr (xsendCheckedKeysList, xsendDataList, ecc.). Oltre alle funzioni tipiche del hr, è possibile utilizzare la funzione: xsendEditDataList(aUrl,aFormId,aPnlId) che permette di inviare al server i dati della riga in editazione, senza rimuovere lo stato di editazione della riga (come fanno di solito le altre funzioni). I dati possono essere reperiti nel parametro DATAS della request. Se invece si vogliono invare solo le righe modificate (individuabili con un flag sulla riga), è possibile richiamare la funzione xsendChangedDataList (aUrl,aFormId,aPnlId) 2.5.7.4 Riga in errore Per mettere una o più righe in stato di errore, si deve per ogni riga richiamare il metodo addData: rowinfo.addData(actionForm,dati, "","ERROR"); dove nel parametro dati è possibile passare o la chiave della riga o il valore di tutta riga (se si vuole modificare la riga stessa). Per rimuovere lo stato di errore, è necessario passare lo stato della riga a blank, oppure utilizzare uno dei metodi js disponibili. 2.5.7.5 Riga in editazione Se si vuole aggiungere una riga o modificarla e automaticamente metterla in stato di editazione, si può utilizzare il parametro STATUS del metodo addData: rowinfo.addData(actionForm,chiave, "","EDIT"); © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 79 di 167 Se si vuole mettere la riga in errore e in stato di editazione: rowinfo.addData(actionForm,chiave, "","EDIT|ERROR"); Qualora si voglia che alcuni dei campi della riga siano di input solo se la riga si trova in inserimento e non in editazione, si possono sfruttare le proprietà disableonedit e disableonnew del campo, ed utilizzare lo stato EDIT_NEW (al posto di EDIT) nel caso si stia in inserimento. 2.5.7.6 Combo dinamica su lista editabile La lista editabile può contenere un campo combo, i cui valori vengono valorizzati dinamicamente lato server. Per indicare la lista di valori con cui valorizzare la combo, è necessario utilizzare il metodo: addDisplayInfo(DynaActionForm form, String item_name, String value) passando in item_name il valore di attrname del campo combo, ed in value la lista di valori della combo, secondo la stessa sintassi valida per i campi combobox dinamici. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 80 di 167 Per realizzare un drilldown su una lista di tipo headerRows si deve per prima cosa dichiarare a livello di dizionario la sotto-lista di drilldown nell’attributo panels della prima lista hr. Tale lista dovrà essere necessariamente una lista tipo HR. Ad esempio: diz["PNL_HEADERROWS"]='{"id":"HEADERROWS", "type":"headerRows",' +'"style":"overflow:auto;width:100%;height:100%;",' +'"rowsForPage":"4","menu":"MNU_HeaderRows"' +'"collapsed":"false","label":"prova",' +'"editable":"true",' +'"drilldownUrl":"../TestHRAction.do?xmethod=loadSubRows",' +'"fields":['+$d2("FLD_CampoRow1") +','+$d2("FLD_CampoRow2") +','+$d2("FLD_CampoRow3") +','+$d2("FLD_CampoRow4") +','+$d2("FLD_CampoRow5") +','+$d2("FLD_CampoRow6") +']' +', "actions":[ ' + $d("ACT_PROVANewrow") +']' +', "panels":['+diz["PNL_HEADERROWSDET"]+']' +'}'; dove PNL_HEADERROWSDET è a sua volta un pannello di tipo hr. Nella property drilldownUrl è possibile indicare il metodo della action da richiamare al click sul bottone di drilldown: Nel metodo richiamato (loadSubRows) posso recuperare la chiave della riga da cui è partita la richiesta tramite il parametro della request KEYS: String keys = request.getParameter("KEYS"); © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 81 di 167 AddRowInfo rowinfo = getRowInfo(request, actionForm); rowinfo.addData(actionForm, keys, "", "", getDatas()); Per caricare i dati della sotto-lista, utilizzando la chiave della riga si richiama il metodo addData(DynaActionForm form, String value, String line, String status, String datas) passando nel parametro datas, i dati della sotto-lista. Una volta caricata la sotto-lista, si può lavorare sulle righe nella stessa modalità di una semplice lista HR. Esempio di aggiunta di una riga tramite menu su sotto-lista: diz["CMD_AddRow "]='js:xsendTitleList (\'../TestHRRowAction.do?xmethod=addSubRow\')'; diz["MNU_Treemenu2"]='["CMD_AddRow"]'; Nel metodo addSubRow aggiungo un’altra riga, tramite il metodo: rowinfo.addData(actionForm,dati della nuova riga , "",""); © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 82 di 167 2.5.8 Drilldown Per realizzare un drilldown su una lista di tipo headerRows si deve per prima cosa dichiarare a livello di dizionario la sotto-lista di drilldown nell’attributo panels della prima lista hr. Tale lista dovrà essere necessariamente una lista tipo HR. Ad esempio: diz["PNL_HEADERROWS"]='{"id":"HEADERROWS", "type":"headerRows",' +'"style":"overflow:auto;width:100%;height:100%;",' +'"rowsForPage":"4","menu":"MNU_HeaderRows"' +'"collapsed":"false","label":"prova",' +'"editable":"true",' +'"drilldownUrl":"../TestHRAction.do?xmethod=loadSubRows",' +'"fields":['+$d2("FLD_CampoRow1") +','+$d2("FLD_CampoRow2") +','+$d2("FLD_CampoRow3") +','+$d2("FLD_CampoRow4") +','+$d2("FLD_CampoRow5") +','+$d2("FLD_CampoRow6") +']' +', "actions":[ ' + $d("ACT_PROVANewrow") +']' +', "panels":['+diz["PNL_HEADERROWSDET"]+']' +'}'; dove PNL_HEADERROWSDET è a sua volta un pannello di tipo hr. Nella property drilldownUrl è possibile indicare il metodo della action da richiamare al click sul bottone di drilldown: Nel metodo richiamato (loadSubRows) posso recuperare la chiave della riga da cui è partita la richiesta tramite il parametro della request KEYS: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 83 di 167 String keys = request.getParameter("KEYS"); AddRowInfo rowinfo = getRowInfo(request, actionForm); rowinfo.addData(actionForm, keys, "", "", getDatas()); Per caricare i dati della sotto-lista, utilizzando la chiave della riga si richiama il metodo addData(DynaActionForm form, String value, String line, String status, String datas) passando nel parametro datas, i dati della sotto-lista. Una volta caricata la sotto-lista, si può lavorare sulle righe nella stessa modalità di una semplice lista HR. Esempio di aggiunta di una riga tramite menu su sotto-lista: diz["CMD_AddRow "]='js:xsendTitleList (\'../TestHRRowAction.do?xmethod=addSubRow\')'; diz["MNU_Treemenu2"]='["CMD_AddRow"]'; Nel metodo addSubRow aggiungo un’altra riga, tramite il metodo: rowinfo.addData(actionForm,dati della nuova riga , "",""); © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 84 di 167 2.5.9 Componente Tree View Per creare un oggetto tree è necessario definire nel dizionario un componente panel, di tipo “tree”: diz["PNL_TreePanel"]='{"id":"TreePanel", "type":"tree", "rowsForPage":"3","editable":"true","menu":"MNU_Treemenu",' +'"onedit":"URL_TreeChange","fields":[' +$d2("FLD_Voce") +','+$d2("FLD_DescrVoce") +','+$d2("FLD_Segno") +','+$d2("FLD_CodiceNota") +','+$d2("FLD_Cambio") +'], "style":"width:100%;height:700px;" }'; dove: - rowForPage indica il numero massimo di righe di primo livello che possono essere visualizzate su ogni pagina della lista; - editable indica se le righe dell’albero sono editabili; - onedit è l’url da richiamare quando si esce dall’editazione di una singola riga (se le righe sono editabili); diz["URL_TreeChange"]='xsendCheckedDataList(\'../TreeViewAction.do?xmethod=chan geRow\')'; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 85 di 167 - menu è il flymenu sulle righe dell’albero //aggiunge una nuova riga, passando nel parametro KEYS il title della riga //corrente diz["CMD_tree1"]="js:xsendList(\'../TreeViewAction.do?xmethod=addRow&KEYS=!k ey\')"; //richiama la cancellazione della riga, passando nel parametro KEYS il //title della riga corrente diz["CMD_tree5"]="js:xdeleterow(\'../TreeViewAction.do?xmethod=deleteRow&KE YS=!key\');"; diz["MNU_Treemenu"]='["CMD_tree1","CMD_tree2",…]'; Per gestire lato server una lista di tipo albero, istanziare la classe AddTreeInfo nel seguente modo: AddTreeInfo addtreeinfo = getTreeInfo(request, actionForm) oppure, passando l’identificativo della lista (nel caso in cui il valore di xform è blank o si devono gestire più liste su diverse form): AddTreeInfo addtreeinfo = getTreeInfo(request,actionForm,”TreePanel”) Per caricare inizialmente le righe che comporranno l’albero, si può utilizzare il metodo: setTreeParams(DynaActionForm form, String numOfElems, String currentPage, String datas, String labels, ArrayList top, HashMap hierarchy) che prende in input la stringa (datas) contenente le righe dei nodi e delle foglie che costruiranno l’albero (la sintassi di tale stringa è la stessa valida per il componente testata-righe), la lista delle righe al primo livello (top) e infine un HashMap con le informazioni di gerarchia tra i nodi. Se ad esempio vogliamo costruire il seguente albero: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 86 di 167 NODO 1 NODO 3 NODO 2 NODO 4 NODO 5 LEVEL 1 NODO 6 NODO 7 LEVEL 2 LEVEL 3 dobbiamo passare come nodi TOP (a livello 1) il nodo 1 e 2, e le seguenti informazioni di relazione tra nodi, indispensabili per la costruzione dell’albero: nodo 1 ha come figli: nodo3 e nodo 4 nodo 4 ha come figli: nodo 5 nodo 2 ha come figli: nodo 6 nodo 6 ha come figli: nodo 7 Per la valorizzazione dell’ArrayList e della HasMap, è possibile utilizzare i metodi della classe ListCreator: getTreeHierarchy() getTreeTop() Ad esempio: AddTreeInfo treeinfo = (AddTreeInfo)getTreeInfo(request,actionForm); treeinfo.setTreeParams(actionForm, "", "1", lc.getData(), "", lc.getTreeTop(), lc.getTreeHierarchy()); dove lc è l’oggetto di tipo ListCreator: ListCreator lc = new ListCreator(); //aggiungo una riga con chiave 1 lc.addEntry(); lc.addKey(1+""); © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 87 di 167 lc.addTitle(""); lc.addValue("new row n. "+1) .addValue("ATTIVO").addValue("* TOTALE dell'ATTIVO") .addValue("D").addValue("").addValue(""); //imposto la riga corrente (chiave 1) come riga di primo livello: lc.addTop(); //indico che la riga corrente ha come figli la riga con chiave 3 e //chiave 4 lc.addChild(3+"").addChild(4+""); //aggiungo la riga con chiave 3 lc.addEntry(); lc.addKey(3+""); lc.addTitle(""); lc.addValue(3+"") .addValue("CRED.SOCI").addValue("Crediti dovuti") verso soci per versamenti .addValue("D").addValue("").addValue(""); //aggiungo la riga con chiave 4 lc.addEntry(); lc.addKey(4+""); lc.addTitle(""); lc.addValue(""+4) .addValue("IMMOBILIZZI").addValue("Immobilizzazioni") .addValue("D").addValue("").addValue(""); Per aggiungere una nuova riga all’albero, è possibile utilizzare uno dei seguenti metodi: - per aggiungere una riga figlia di un’altra riga: addChild(DynaActionForm form, String value, String status, String node) dove node è la riga padre e value il valore della nuova riga - per aggiungere una riga prima di un’altra riga e allo stesso livello: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 88 di 167 addBefore(DynaActionForm form, String value, String status, String node) - per aggiungere una riga dopo un’altra riga e allo stesso livello: addAfter(DynaActionForm form, String value, String status, String node) se invece è necessario aggiungere una riga al primo livello o modificare una riga già esistente (anche di livello superiore a uno), si può utilizzare il metodo: addData(DynaActionForm form, String value, String status, String children) oppure: addData(DynaActionForm form, String value, String status, String children, String menu) dove value è il valore della riga, e children la lista delle chiavi delle figle righe (se presenti), separate dal carattere ~ Ad esempio: addrowinfo.addData(actionForm,"0~~0~~ ATTIVO ~~;;", AddTreeInfo.TOP, "1~2"); E’ inoltre possibile impostare lato server il menù sulla singola riga (che quindi può essere diverso da quello delle altre righe dell’albero), passando come parametro la chiave della riga (o il valore della riga stessa) e l’id dell’oggetto menù: addrowinfo.addData(actionForm,"0","","","MNU_Treemenu1"); 2.6 Skin e font E’ disponibile una funzione di personalizzazione dello stile dell’intera applicazione, utilizzabile da parte dell’utente finale. Egli può cambiare il tema stilistico (“skin”) generale dell’applicazione e altri aspetti specifici quali il font e la sua dimensione. Le modifiche sono dinamiche ed immediatamente fruibili; le preferenze sono memorizzate nel cookie del browser. La funzione è accessibile cliccando sull’icona “Impostazioni utente” presente in alto a destra nel deskop ACG Vision4 o dal menu ad albero seguendo il percorso Strumenti > Impostazioni > Impostazioni client > Impostazione Client Utente. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 89 di 167 Temi / Skins I temi a disposizione, nella versione corrente, sono 3: “Classico” o di default “Demo” caratterizzato da una maggior cura dell’estetica del desktop, delle icone e dei pannelli con uso di sfondi grafici, trasparenze, set di icone specifico. “Classico 2” Simile al “Classico” ma con visibilità migliorata Immagini dei tre skin a disposizione: Tema “CLASSICO” (skin 0) Tema “DEMO” (skin 1) © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 90 di 167 Tema “CLASSICO2” (skin 2) © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 91 di 167 Tema personalizzato E’ possibile costruire uno o più temi personalizzati, validi per l’intera applicazione, in aggiunta ai tre citati nel paragrafo precedente. Le informazioni relative all’aspetto grafico (foglio di stile ed immagini) devono essere presenti nelle sottocartelle dell’applicazione acgv4 /addon/skins/0 /addon/skins/1 /addon/skins/2 ... /addon/skins/n dove il nome della cartella per convenzione è un valore numerico. Le sottocartelle 0 , 1 e 2 sono già presenti e contengono le informazioni per i tre temi CLASSICO, DEMO e CLASSICO2. Per quelle personalizzate si consiglia di usare come nome della cartella il valore da 10 in su. Questo identificativo è usato per associare il nome dello skin; è usato anche nell’elenco dei temi nella finestra delle IMPOSTAZIONI CLIENT UTENTE Esempio: Si vuole creare un tema a partire da quello CLASSICO2 in cui il testo nei campi disabilitati sia invertito (chiaro su sfondo scuro. Questo stile avrà come nome PERSONALIZZATO (CUSTOMIZED in inglese) e sarà disponibile a livello di applicazione a tutti gli utenti. 1) Copiare la cartella <acgv4>\addon\skins\2 nella cartella <acgv4>\addon\skins\ e ridenominarla come 10 2) Personalizzare il foglio di stile <acgv4>\addon\skins\10\ui.css per esempio inserendo INPUT.readonly{ background-color:#000066; border:1px solid #FFFFFF; } al posto di INPUT.readonly{ background-color:#FFFFFF; font-weight:bold; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 92 di 167 } 3) Ricercare nel file /diz/diz.js la voce diz["VAL_xCLIENTSkin"]="[0,1,2]"; da usare per aggiungere nella lista degli identificativi numerici degli stili, lo stile personalizzato 10, ottenendo il nuovo valore di questa chiave diz["VAL_xCLIENTSkin"]="[0,1,2,10]"; 4) Aggiungere nel file /customized/ext.js le voci diz["LBL_IT_xCLIENTSkin_10"]="Personalizzato"; diz["LBL_EN_xCLIENTSkin_10"]="Customized"; diz["VAL_xCLIENTSkin"]="[0,1,2,10]"; 5) Dai client (eventualmente cancellando la cache del browser) scegliere da IMPOSTAZIONI UTENTE il nuovo tema PERSONALIZZATO NOTA. Se alcuni moduli applicativi o personalizzazioni introducono nuove immagini alla cartella di base <acgv4>/images allora devono essere fornite le corrispondenti immagini nelle cartelle /images/ di ogni skin \addon\skins\<n>\images dove n è un valore numerico che identifica i vari stili. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 93 di 167 Variazione tema di default Qualora si desideri impostare un valore di default del tema diverso da “Classico”, inserire la riga UISKIN= valore numerico del tema scelto (es. 1,2..) in coda al file <root>\WEB-INF\classes\conf\Base.properties, indicando il numero che rappresenta il tema da utilizzare ad ogni collegamento per ogni utente. NOTA: Se l’utente finale desidera utilizzare un tema diverso da quello di default definito nel file Base.properties, potrà modificarlo nella finestra delle IMPOSTAZIONI CLIENT UTENTE. Tali impostazioni infatti vengono memorizzate nel cookie del browser ed hanno “priorità” rispetto a quella definita nel file Base.properties e valide dal successivo ricaricamento del desktop. E’ possibile eliminare queste impostazioni cancellando dalla cache del browser il cookie relativo (in IE TOOLS/INTERNET OPTIONS/SETTINGS/VIEW FILES/) Esempio: Si e’ creato un tema PERSONALIZZATO identificato dal valore 10. Se si vuole che questo tema diventi quello di default; per fare cio’ occorrera’ inserire , nel file Base.properties , la riga UISKIN=10 © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 94 di 167 2.7 FAQ Per realizzare un TABBED definire la proprietà type:"tabbed" Per realizzare un pannello contenente più pannelli diz["PNL_PannelloEsterno"]='{ id:"PannelloEsterno", … … "panels":[ diz["PNL_Pannello1"] , diz["PNL_ Pannello2"] ] } Per realizzare un form unico contenente un tabbed WIN CONTAININGPANEL formId, URL,…. TABBED TABPANEL1 TABPANEL2 diz["WIN_UniFormTabbedWindow"]= '{"window":{' + ' "panels":[ ' +diz["PNL_UniFormPanelContainingTabbed"] +' ] ' +' } }'; diz["PNL_UniFormPanelContainingTabbed"]='{ id:"UniFormPanelContainingTabbed", "formId":"FormT", collapsed:"false", "style":"position:relative;top:100;left:100;width:100%;height:100%;"' +',' + ' "panels":[ ' +diz["PNL_UniFormTabbed"] +',' +diz["PNL_DynaAddress"] +' ] ' +',' +' "bar":{"buttonstyle":"width:100;" } ' +','+ +' "actions":[ ' + $d("ACT_UniForm_SAVE") + ']' +' } '; segue definizione PNL_UniFormTabbed segue definizione PNL_DynaAddress Per realizzare una finestra con 2 pannelli uguali (risolvendo i problemi di ambiguità) Definire in pannello normalmente e usare la funzione $d2 per sovrascrivere l’ID. diz["WIN_SimpleWindow"]= '{"window":{' + ' "panels":[ ' + diz["PNL_SimplePanel"] + $d2("PNL_SimplePanel", " +',' id:'secondoPannello' © Copyright ACG Srl 2014 Tutti i diritti riservati. " ) Pagina 95 di 167 +' ] ' … +' } }'; Per ottenere il bordo del pannello (con/senza etichetta) definire la proprietà label del pannello "label":"", "label":"NomePannello", "label":"ChiaveDizionarioPerNomePannello", Per abilitare l’invocazione di CLOSE alla chiusura di una XWIN Definire la proprietà url nella window Esempio: diz["URL_MyWindow"]='../MyAction.do'; diz["WIN_ MyWindow "]= '{"window":{'… ' "panels":[ ' … +' ] ' +',' +' "url":"URL_ MyWindow " ' +' } }'; oppure +' "url":"../DynaAddress.do " ' Per passare dei parametri ad una window ed avvalorarne i campi Invocare la window in questo modo xcrtWIN('F0','WIN_RdP', ' [ 5,4]' ); o in modo dinamico xcrtWIN('F0','WIN_RdP', ' [!key,!key]' ); per invocazioni da liste con passaggio di chiavi definire i campi da avvalorare… diz["WIN_RdP"]= '{"window":{ "id":"RdPWindow", "width":700, "height":500, ' + ' "panels":[ ' +diz["PNL_RdPPanel"] +' ] ' +' }' +','+ ' "parms": [ "RdPPanel_Form1_NRIMO" , "RdPPanel_Form1_NRIM2"] ' +' }';. Oppure ' "parms": [ "RdPPanel_NRIMO" , "RdPPanel_Form1_NRIM2"] ' Per invocare un metodo specifico della action di STRUTS da un button Definire: diz["ACT_DynaAddress_RESTORE"]='{' +' id:"ACT_DynaAddress_RESTORE"' +' disableonedit:"true" ' +',' +',' +' "label":"ACT_DynaAddress_RESTORE"' © Copyright ACG Srl 2014 Tutti i diritti riservati. +',' Pagina 96 di 167 +' "url":"../DynaAncl200f.do?xmethod=READ"' +' } '; Per collassare/espandere una sezione all’esecuzione di una richiesta sul server Impostare come pre o postActions dall’dentificativo del pannello. il metodo xexpand, xcollapse o xtoggle (per alternare i due stati) seguito Esempio che disabilita un pannello addPropertyAsVector(actionForm,"xreadonly('BancaPanel','false')", POSTACTIONS); Esempio di invocazione refresh su una lista addPropertyAsVector( actionForm, mapping.getProperty("reloadRows"), POSTACTIONS); Esempio di espansione addPropertyAsVector(actionForm, POSTACTIONS); "xexpand('"+request.getParameter("xwin")+"_DynaAddressList_SPAN"+"')" , Per attivare il ReferencedObject su un campo (funzione ? di ACG) Prerequisito: file xml dei PersistentFilter nella cartella /WEB-INF/classes/com/ibm/acg/xml/ Attivabile sul campo inserendo nella sua voce nel dizionario la proprietà pf contenente la chiave del dizionario ReferencedObject. del diz["FLD_RdPCliente"]='{"attrname":"CDCSO","pf":"RO_Clienti", …} '; Il ReferencedObject deve essere definito nel dizionario con le seguenti proprietà il nome del file xml usato per produrre la lista i campi di riferimento in cui scaricare i valori del record selezionato (corrispondono agli attrname) i dataItemName (nel file xml) delle colonne da scaricare i dataItemName (nel file xml) delle colonne da visualizzare diz["RO_Clienti"]='{ "file":"AAC0", "show":["stato","cdcli","rascl"] }' "refFields":[" CDCSO "," CDCSO_rascl"], La funzione è richiamabile sul campo mediante tastiera con CTRL-space o cliccando sull’icona "key":["cdcli","rascl"], accanto al campo. Per aggiungere una voce al menu a tendina Supponiamo di voler aggiungere una voce di menu al nodo TOOLS, basterà aggiungere in un file /addon/diz/diz_<miaestensione>.js diz["NAV_TOOLS"]="...|4MiaGestione" diz["LBL_IT_4MiaGestione"]="Lancio MiaGestione"; diz["LBL_EN_4MiaGestione"]="..."; diz["CMD_4MiaGestione"]="js:xcrtWIN('F0','WIN_MiaLista');" Per creare una finestra con lista (da PF), con apertura della finestra di dettaglio al doppioclick 1) INSERIRE IN © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 97 di 167 /WebContent/WEB-INF/source/com/ibm/acg/xml/ gli xml delle liste 2) Personalizzare opportunamente: MiaLista MioDettaglio MIOFILEXML diz["ACT_DblClickMiaListaPanel"]='{ "id":"ACT_DblClickMiaLista" , \'[!key]\' ) " } '; "url":" xcrtWIN(\'F1\',\'WIN_MioDettaglio\', diz["URL_MiaLista"]=''+xPFContext+'/pf/XGenericTable.jsp?queryDefinitionFileName=com/ibm/acg/xml/MIOFILEXML .xml' + USER_AND_PASSWORD + '&'; diz["PNL_MiaListaPanel"]= '{ "id":"MiaListaPanel", "init":"true", ' +' "style":"height:100%;width:100%;" ' +' "url":"URL_MiaLista" ' +' , ' +' , ' +' "dblclick":"ACT_DblClickMiaListaPanel" ' +' } '; diz["WIN_MiaLista"]= '{"window":{ "id":"MiaListaWindow", "width":700, "height":500, ' + ' "panels":[ ' +diz["PNL_MiaListaPanel"] +' ] ' +' }' +' }'; e aggiungerlo in coda ad un diz_4acronimoprodotto.js 3) Aggiungere una voce al menu per lanciare la gestione diz["NAV_4...Menuesistente"]="...|4MiaGestione" diz["LBL_IT_4MiaGestione"]="Lancio MiaGestione"; diz["LBL_EN_4MiaGestione"]="..."; diz["CMD_4MiaGestione"]="js:xcrtWIN('F0','WIN_MiaLista');" Per personalizzare azioni sulle righe del componente TESTATA_RIGHE E’ possibile aggiungere azioni personalizzate sulle righe selezionate; il comando da eseguire invocherà l’API xexecrow(aUrl [,aHandler]) che eseguirà un’invocazione ajax verso l’url specificato passando come parametro KEYS gli identificativi delle righe selezionate; l’handler è opzionale. Esempio: diz["ACT_SampleCustomizedHRAction"]='{ "id":"SampleCustomizedHRAction", "icon":"Exp", "label":"Export", "url":"xexecrow(\'../MyServlet.do?\')" } '; diz["PNL_Sample"]='{ …actions":[ '+ $d("ACT_SampleCustomizedHRAction") Per lanciare direttamente una richiesta AJAX da menu E’ possibile definire una voce di menu (per esempio DIRECTAJAXREQ) © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 98 di 167 diz['NAV_NODOMENU]='AZIONE1|AZIONE2|…|DIRECTAJAXREQ'; diz['LBL_IT_DIRECTAJAXREQ']="Richiesta AJAX diretta "; diz['LBL_EN_DIRECTAJAXREQ']="Direct AJAX request"; diz["CMD_DIRECTAJAXREQ"]="js:xdirectsend('../DynaAddress.do?xmethod=launch')" Per lanciare una finestra alla risposta dal server Dall’esempio precedente è possibile invocare il server per ottenere una response ACG Vision4 che contenga nella sezione <preActions> il comando per istanziare la finestra ed eventualmente nella sezione <data> i dati per inizializzarla con i valori di default nella stessa richiesta. <preActions> <action name='xcrtWIN("F1","WIN_DocumentoVendita" )' /> </preActions> <postActions> </postActions> <data> <item name="ModalitaPagamento" value="A10" ></item> <item name="ModalitaSpedizione" value="SP1" ></item> </data> In alternativa si può istanziare la finestra con il comando nella sezione <preActions>, senza fornire alcun dato e lasciare che i pannelli impostati con init=true effettuino le realtive richieste di inizializzazione dei dati al server. Naturalmente il contenuto delle response presentate deve essere costruito mediante l’invocazione dei metodi forniti dall’architettura ACG Vision4. Per impostare il dizionario di un nuovo prodotto/personalizzazione E’ possibile eliminare ogni dipendenza del dizionario di un prodotto dai diz di base. Si impostano nel diz_mioprodotto tutte le voci, anche quelle del mioo menu e di aggancio ad nodo base ACG Vision4. Esempio: L’albero degli esempi, agganciato al nodo ACG Vision4viene completamente definito in diz_Samples.js. diz["NAV_4ACGV4"]+="|4SamplesMenu"; // 4SamplesMenu diz["LBL_EN_4SamplesMenu"]="Samples"; diz["LBL_IT_4SamplesMenu"]="Esempi"; diz["NAV_4SamplesMenu"]="4SamplesWINMenu|4SamplesLinksMenu"; … diz["FLD_SampleCustomer"]='… Come inserire o modificare una voce del dizionario E’ disponibile una API del UIENGINE che permette di inserire/modificare una voce del dizionario xaddToDictionary(<chiave>,<valore>); invocabile anche da response per modificare dinamicamente il dizionario in base ad esigenze applicative. Es. addPropertyAsVector(actionForm, POSTACTIONS); "xaddToDictionary('LBL_IT_AAC0.xml_ANCL200F.CDCLI','Clienti © Copyright ACG Srl 2014 Tutti i diritti riservati. VIP')", Pagina 99 di 167 Da notare che le modifiche apportate varranno da quel momento in poi anche in caso di chiusura e riapertura della finestra che ne fa uso. Un ricaricamento mediante ripristina la situazione iniziale. o CTRL-R del desktop o allo scollegamento o al lancio di una nuova istanza di browser, Esempio di operazione in 2 fasi Supponiamo di voler eseguire un salvataggio in 2 fasi. L’utente clicca sul SALVA e la richiesta con tutte le info del form viene elaborata dalla parte controller sul server che emette un comando fra le postAction. <postActions> <action name="CMD_SaveConfirm"/> </postActions> Tale comando è richiede una richiesta di conferma per il salvataggio definitivo diz["CMD_SaveConfirm"]="js:if(confirm('Sei sicuro?')){xdirectsend('../DynaAddress.do?xmethod=confirmedsave') }"; Nella richiesta saranno inviati il metodo, l’id della window (xwin), l’id del form (xform) correnti. E’possibile nel comando far riferimento ad una window o ad un form diversi, in questo caso gli id della window e della form correnti non saranno inviati. E’ possibile anche inviare un valore presente nella window sfruttando il meccanisco standard della voce URL_PARMS associata al comando. diz["URL_PARMS_SaveConfirm"]=' [ "SamplePanel_Form1_date" ] '; Nuova funzione “Controllo componenti” Nel menu “Raccolta informazioni è presente questa voce che stampa nella sesione log tutte le voci del dizionario di tipo VER_ che indicano la versione dei componenti in uso. Nuova funzione “Cambio locale EN/IT” Nel menu “Risoluzione problemi/Controlli” è presente questa voce che permette di passare dalla locale EN a IT per testare le notazioni anglosassone/latina delle informazioni DATE e NUMERIC. Introdotto limite al testo nella sezione “Log” La variabile $$maxlog impostata a 30000 char. Liste: Introdotto delay sul flyingmenu sulle liste Quando si abbandona il flyingmenu e si ritorna sulla lista, la chiusura del flyinmenu viene ritardata di circa un secondo. Se in questo lasso di tempo si ritorna sul flyingmenu la chiusura viene annullata. Pannelli: Inizializzazione di un pannello mediante esecuzione di metodi javascript E’ possibile eseguire una inizializzazione via metodi javascript per quelle gestioni in cui non è necessario recuperare alcuna informazione dal server. Naturalmente il metodo deve essere disponibile in uno dei dizionari caricati. diz["URL_xCLIENTSETTINGS"]='js:initializaClientSettings()'; diz["PNL_xCLIENTSETTINGS"]='{ "id":"xCLIENTSETTINGS", "label":"xCLIENTSETTINGS", "init":"true", ' +' "style":"height:400;width:100%;" ' +' , ' +' "url":"URL_xCLIENTSETTINGS" ' © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 100 di 167 +' } '; Sostituzione parametri mediante indice Finora era possibile definire un url con delle parti variabili (indicate con !key), sostituite dinamicamente con i valori effettivi (per esempio la chiave di un record di una lista). In presenza di una chiave composta la sostituzione avviene in modo posizionale (la prima parte verrà sostituita alla prima occorrenza di !key, e cosi via…) Con la modifica introdotta è possibile usare la notazione !keyi (dove i può andare da 0 a n-1 dove n sono le parti di una chiave) in modo da consentire la sostituzione delle singole parti di una chiave composta più volte o in un ordine diverso da quello posizionale. "where":" cdmag = \'!key1\' AND cdpar = \'!key0\' Focus Il focus è in generale impostato sul primo campo del pannello. In alcuni casi può essere necessario cambiare questa impostazione. in fase di costruzione della finestra specificando nel dizionario l’identificativo del campo che deve ricevere il focus diz["WIN_Sample1"]= '{"window":{ "id":"Sample", "width":700, "height":400 , ' +' "panels":[ +$d2("PNL_Sample1a", " collapsed:'false' ") +' ] ' +',' +' "focus":"SamplePanel_Form1_field3" ' +' } }'; a runtime mediante una action nella response del tipo descritto in cui l’identificativo assoluto del campo viene ricavato come composizione del parametro della request xform e del fieldId <postActions> <action name="xfocus(' 1_F0_WIN_Sample1a_SamplePanel_Form1_customerstatus ')"/> </postActions> Segnale acustico su errore in digitazione (beep) Aggiunta la possibilita di emettere un suono in fase di errore di digitazione. Selezionabile da menu “Impostazioni utente” Close della window (xclose, xconfirmedClose) Alla chiusura dei pannelli con sulla icona x in alto a destra viene invocato il metodo close se per la window è stata definita la proprietà l'url. Se non gestita o non prevista, il ServiceBus parte server risponde con la conferma della chiusura della window. Standard nella definizione della proprietà style dei field Per uniformità la definizione della proprietà style degli oggetti di tipo XFIELD da oggetto passa a stringa. L’UIEngine supporterà le due modalita per garantire la compatibilità con le definizioni dei dizionari già realizzati: come stringa (nuova) e come oggetto (vecchia modalità, deprecated). Il Wizard leggerà la vecchia modalità e in fase di salvataggio la convertirà usando la nuova modalità. diz["FLD_SampleA0"]='{"id":"CampoStyleStringa", "attrname":"CampoStyleStringa", "style":"{\'background\':\'#FF0000\', \'color\':\'#FFFFFF\' }" , "labelStyle":"left" } '; diz["FLD_SampleA1"]='{"id":"CampoStyleObj", "attrname":"CampoStyleStringa", "style":{"background":"#FFFF00", "color":"#FFFFFF"} , "labelStyle":"left" } '; Window: Introdotto meccanismo per l’apertura sfalsata delle finestre Si disporranno in modo sfalsato (20pixel piu in basso e più a destra della finestra precedente) fino a 4 livelli per poi riprendere dall’alto. Liste: UNION tra tabelle Per realizzare l’UNION tra due tabelle è possibile usare nel file XML la direttiva UNIONTABLE=”nomeTabella” ANRI200F.XML direttiva <union unionTable="ANRI200T" />) © Copyright ACG Srl 2014 Tutti i diritti riservati. (es. in Pagina 101 di 167 <?xml version="1.0" encoding="ISO-8859-1"?> <querydefinition additionalOrderClause="" additionalWhereClause="" name="ANRI200F" description="Registri IVA" table="ANRI200F" resourceFile="conf\ApplicationResources" pagingSize="20" maxSize="1000" > <union unionTable="ANRI200T" /> <attribute………………………………………………………………………………… ………………………………………………………………………………………………… Liste: Clausola DISTINCT nella SELECT. La clausola DISTINCT serve a non ripetere nei risultati della SELECT quelli con lo stesso valore. Per ottenere un query string del tipo :”SELECT DISTINCT …….” basta inserire nella definizione del dizionario il campo “distinct”:”true”. Per esempio: diz['ACT_PFCLPRO']='{id:"PFCLPRO", type:"PF", file:"AAC0" , "show":["cdcli","rascl","loccl","procl"], "sort":"procl", "distinct":"true"}'; Per default la distinct è false. L’informazione relativa al distinct è inserita anche nella response nel parametro _distinct=true(/false). Menu contestuale: Richiamo di azioni ACGWebEdition o Express da flyingmenu su liste e menu contestuali su campi E’ possibile definire un’azione nel menu contestuale associata ad una lista o ad un campo che invochi un’azione di ACGWebEdition o di ACGExpress. Esempio: Supponiamo di collegare il richiamo dell’Anagrafico clienti WebEdition al flyingmenu di una lista o su un menu attivo su un campo // definizione dell’azione diz["CMD_ClientiWF"]="js:xACG(\'AAC0\',\'ACGV3\',\'!key\' // da menu su campo diz["MNU_RdPList"]=' [ "CMD_RdPCreate", "CMD_RdPUpdate", "CMD_ClientiWF" ] '; // da menu tipo navigazione diz["NAV_PFAAC0menu"]='PFBOTE|PFBOTE1|XCLGX05|PFCLPRO|ClientiWF'; Menu sulle liste modificabile/disattivabile dinamicamente E’ possibile modificare dinamicamente la proprietà menu contestuale (flyingmenu sulle liste) da programma, inserendo nella response una action con la seguente sintassi xchangeMenu('<id assoluto del pannello>','<nuovo id menu>') Es. <postActions> <action name="xchangeMenu('1_F0_WIN_MyWindow_MyList','4RdPMenù)"/> </postActions> La stessa funzione può essere usata per disattivare il menu impostando a stringa vuota l’id del menu. <postActions> <action name="xchangeMenu('1_F0_WIN_ MyWindow_MyList',’’)"/> </postActions> La nuova impostazione del menu è valida sulla lista indicata e rimane attiva anche nelle operazioni successive di reload, nuova query utente,… Le nuove impostazioni si perdono con la chiusura della finestra. Per posizionare le label in modo che siano allineate con i campi input sottostanti E’ possibile disegnare un pannello di questo tipo con la label indicata nel riquadro rosso allineata con i campi sottostanti. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 102 di 167 Esempio: La prima stringa è locale-dipendente, la seconda è cablata. Rimangono valide le possibilità di modificare dinamicamente da server (vedi paragrafo relativo). diz["FLD_WHSRaggrNumero"]=' {"id":"WHSRaggrNumero" , "type":"label", "value":"'+xLbl('ID')+'", "labelStyle":"width:150px"}'; diz["FLD_WHSRaggrData"] =' {"id":"WHSRaggrData" , "type":"label", "value":"WHSRaggrData", "labelStyle":"width:60px"}'; "label":"", "label":"", Come effettuare dei controlli sui campi di un pannello Su un pannello con 2 campi (A e B) si vuole attivare un controllo sul browser che verifichi (all'uscita dal campo B), se il valore del campo B è maggiore del campo A, eventualmente emettendo un messaggio di errore sul campo B. // xisGT è il codice da inserire in una libreria del proprio prodotto // nel caso di funzioni generalizzate si provvederà a renderle disponibili nelle librerie di base // prerequisito il campo A e B appartengono allo stesso Form/pannello diz["LBL_IT_MSG_LTError"]="Valore minore del campo &1"; diz["LBL_EN_MSG_LTError"]="Valore less then field &1"; function xisGT(aId){ var aObj=$$(event.srcElement.formId+'_'+aId); if (typeof(aObj)!='undefined'){ if((event.srcElement.value<=aObj.value)){ var aMsg0=xmsg('MSG_LTError',aId); xaddMsg(event.srcElement.id,event.srcElement.winId,aMsg0) } } } diz["PNL_LaunchPanel"]='{ ' +' "id":"LaunchPanel", ... +','+ ' "fields":[ '+ $d2("FLD_SampleNum", " attrname:'A', row:1, col:0") +','+ $d2("FLD_SampleNum", " onblur:\"xisGT('A')\", attrname:'B', row:2, col:0") Campi: Disabilitazione formattazione numerica E’ possibile disabilitare la formattazione dei numeri (separatori delle migliaia) impostando l'attributo thousands al valore false nella proprietà dataformat del campo. Esempio © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 103 di 167 diz["FLD_SampleNum26"]='{ "dataformat":"{thousands:false,places:2}","labelStyle":"width:300px;position:left", "style":{"size":12,"maxlength":12}, … © Copyright ACG Srl 2014 Tutti i diritti riservati. "datatype":"DECIMAL", Pagina 104 di 167 2.8 Tool di navigazione Il tool di navigazione è accessibile tramite il menu Vision4, tramite la combinazione di tasti Ctrl + click sulla voce di menu. La voce di menu selezionata deve essere un menu o una lista di tipo PF: Si apre una window che carica le informazioni relative all’azione/menu selezionato. Cliccando ad esempio su Lista Client, si aprirà la seguente finestra: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 105 di 167 Nella testata del pannello è possibile visualizzare: il contenuto del menù (PFAAC0menu) associato alla Lista Clienti l’id dell’azione selezionata i campi di input obbligatori per inserire il nome della nuova azione e il suo valore la lista dei file xml presenti nell’applicazione (e a cui il cliente corrente è abilitato) Dalla lista dei file xml è possibile tramite menu contestuale scegliere “Nuova azione”. Questo permette di creare un’azione sul menu della Lista clienti che punti al file xml selezionato (e quindi alla lista corrispondente). Al lancio dell’azione si apre una nuova window, dove vengono caricate le informazioni relative al file xml selezionato. Supponiamo ad esempio di aver selezionato il file BOTE.xml. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 106 di 167 Nella parte centrale della window è presente la lista dei campi della tabella. Con doppio click sulla riga è possibile andare a modificare le proprie preferenze sulla lista, indipendentemente dalle impostazioni presenti nel file xml (key, show e sort): E’ possibile inoltre selezionare più righe ed indicare tali righe come chiavi con il click sull’icona chiave, e con la stessa modalità indicare le colonne visualizzate e i criteri di ordinamento © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 107 di 167 Cliccando sul tasto Edit del pannello Lista campi, vengono aggiornate le informazioni della nuova action, visibili esplodendo il primo pannello Azione: Supponiamo ad esempio di aver impostato 4 campi come campi chiave: Tramite l’ultimo pannello è infine possibile aggiungere delle condizione di where aggiuntive: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 108 di 167 cliccando su Add, la condizione viene aggiunta ad una lista di condizioni: A questo punto è possibile aggiungere altre condizioni in AND o in OR, o cancellare la condizione creata. Per aggiungere tali condizioni alla nuova azione, è necessario cliccare sul bottone di edit del pannello Condizioni di Where, che aggiornerà il campo Where del pannello di testata. Per completare la creazione della nuova azione cliccare su Salva, in questo modo le informazioni della nuova azione vengono riportate sulla prima finestra: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 109 di 167 A questo punto si può modificare la label dell’azione e completare con il salvataggio con il tasto Salva. Il salvataggio ha come effetto la scrittura in un file con estensione js (legato all’utente), la definizione della nuova azione e del menu aggiornato. Per visualizzare le modifiche effettuate è necessario un refresh del browser. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 110 di 167 3. Componente Controller 3.1 Introduzione In un’architettura che implementa il pattern MVC la componente Controller determina il modo in cui l'applicazione risponde agli input dell'utente. Essa esamina le richieste dei client, estrae i parametri della richiesta e li convalida, si interfaccia con lo strato di business logic dell'applicazione, sceglie la successiva vista da fornire all'utente al termine dell'elaborazione. In ACG Service Bus si è scelto di utilizzare la framework Struts versione 1.3.8 per implementare questa componente, per via della maturità ed affidabilità raggiunte nel corso degli anni (la prima versione risale al 2000 ed è tuttora supportata da una vasta e consolidata comunità di programmatori), dell’ampia diffusione e conoscenza dell’open-source e per la flessibilità che ha consentito di implementare una serie di comportamenti propri dell’architettura ACG. Ulteriori informazioni sulla framework Struts possono essere reperite sul web, accedendo al sito ufficiale sopra menzionato della Apache Software Foundation o consultando la vastissima bibliografia pubblicata, consistente in articoli, tutorial e documentazione per i programmatori (si leggano ad esempio gli articoli pubblicati sul sito IBM developerWorks, java.HTML.it o ONJava.com); nel seguito verranno fornite una serie di indicazioni sulle modalità di utilizzo della framework presupponendo una conoscenza adeguata della stessa. In ACG Vision4si è scelto, come detto nell’overview architetturale, di non utilizzare Struts con le sue tag-libraries per costruire le interfacce, avendo previsto la possibilità di definirle in modo dichiarativo attraverso l’uso di un dizionario dati e di un engine in grado di costruire e popolare a runtime i pannelli sulla base delle definizioni previste nel dizionario stesso. Tutto ciò è stato reso possibile grazie all’integrazione del template engine Velocity con Struts. 3.2 Integrazione Velocity – Struts in ACG Service Bus Velocity è un motore di template basato su Java. Può essere usato come utilità indipendente per generare codice sorgente, HTML, relazioni o può essere combinato con altri sistemi per fornire servizi di template. Velocity è basato sul paradigma "Model View Controller", che rafforza la separazione tra il codice Java e il template HTML. L’utilizzo di Velocity nello sviluppo di applicazioni web consente la conduzione in parallelo delle attività di disegno dell’interfaccia e codifica del codice servente da parte di web designer e programmatori Java sulla base del pattern MVC, consentendo ai primi di focalizzarsi sulla creazione di pagine web e dando la possibilità agli altri di concentrarsi sulla scrittura di codice che realizza la logica di business e di controllo. Velocity costringe infatti gli sviluppatori a separare il codice Java dalle pagine web, rendendo le applicazioni web più facilmente manutenibili e potendo costituire un’alternativa alle Java Server Pages (JSPs) or PHP. Le funzionalità di Velocity possono essere ulteriormente arricchite dalla creazione di custom tools. Un tool è semplicemente una classe java che può eseguire vari task quando viene resa disponibile al motore di Velocity. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 111 di 167 VelocityTools è un insieme di sottoprogetti di Velocity con il comune obiettivo di creare tool ed una infrastruttura per creare applicazioni web e non, utilizzando il “Velocity template engine”. Il sottoprogetto VelocityStruts integra Velocity con la framework Apache Struts consentendo l’uso dei template Velocity congiuntamente o in alternativa alle pagine JSP nell’implementazione della parte View di un’applicazione web. Diverse alter framework offrono un supporto nativo ai Velocity templates; questo progetto fornisce lo stretto necessario per fornire agli sviluppatori Struts un’alternativa alle JSP. Osservando il flusso elaboravo tipico di un’applicazione web nella figura precedente, si può osservare come l’aggiunta di Velocity non ne modifichi in maniera sostanziale la logica. La libreria java velocity-struts.jar introduce una servlet di Velocity stand-alone che elabora i template file (nello specifico, il jar file è velocity-tools-x.x.jar) ed usa una serie di tool predefiniti per fornire un accesso trasparente ad oggetti tipici di Struts (ad esempio. message resources, form beans, errors, links). Il file di mapping delle azioni conterrà semplicemente un ActionForwards che indirizza opportunamente il controllo delle view basate su Velocity invece di indirizzarle ad una JSP. Si noti che le tecnologie Velocity e JSP non sono mutuamente esclusive: entrambe le tecnologie possono essere usate nella stessa applicazione senza alcun problema. Questo consente agli sviluppatori di utilizzare l’opzione Velocity senza modificare pesantemente un’applicazione Struts. 3.2.1 Benefici Ci sono diversi motivi per utilizzare Velocity come tecnologia per la realizzazione dello strato View di un’applicazione web. Qui di seguito ne sono riportati alcuni: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 112 di 167 Velocity aiuta a realizzare una chiara separazione tra lo strato view e quello model/control. Ciò determina una netta separazione tra il disegno applicativo e la presentation, tra i Designers dell’interfaccia e gli sviluppatori back-end Velocity Template Language (VTL) ha poche direttive, è semplice e facile da imparare. Velocity si può estendere facilmente con i Tools che sono semplicemente delle classi con metodi pubblici. Tipicamente tali classi sono più semplici e facili da sviluppare rispetto al JSP custom tag libraries. usando il TilesTool è possibile effettuare un mix di Velocity e JSP tiles nella stessa pagina, consentendo una migrazione graduale da una tecnologia all’altra. le Velocity macros rappresentano uno strumento molto potente per i View Designer, consentendo la creazione di prototipi riutilizzabili o eliminando la necessità di sviluppare una custom tag library o altri tool server-side i template di Velocity NON sono limitati all’HTML e possono essere usati per qualsiasi tipo di output testuale, quale può essere un file XML, SQL, ASCII, PostScript, etc. Velocity, che realizza il caching dei template, consente il raggiungimento di livelli di performance uguali o superiori a quelli di una JSP Velocity è supportata da una comunità attiva di sviluppatori ed utenti. Alcune referenze: http://www-128.ibm.com/developerworks/java/library/j-sr1.html http://www-128.ibm.com/developerworks/java/library/j-velocity/ http://jakarta.apache.org/velocity/ http://jakarta.apache.org/velocity/tools/struts/ Nell’architettura ACG Vision4 è stato adottato Velocity con il sottoprogetto di integrazione con Struts allo scopo di demandare al template engine formattazione dei dati contenuti nei form bean in un file XML con struttura “interpretabile” dall’UI engine che costruisce l’interfaccia utente. Nello specifico, l’invocazione di una action Struts termina con un forward ad una pagina di velocity (vale a dire un file con estensione .vm) che, tramite i tool del sottoprogetto Velocity-Struts (FormTool, ErrorTool, EscapeTool, StrutsLinkTool), è in grado di leggere il contenuto del form bean associato all’action e produce una risposta in formato XML interpretabile dall’UI Engine. Il formato tipico di una risposta XML è il seguente <?xml version="1.0" ?> <response xwin="5_F1_WIN_RdP" xform="5_F1_WIN_RdP_PanelInPanel_Form1" xfield="" xmethod="save" xexec="" xcontext="2" > <preActions> </preActions> <postActions> <action name=” xreload(‘Rows_list’)“/> </postActions> <data> <item name="CDCSO" value="001700" © Copyright ACG Srl 2014 Tutti i diritti riservati. ></item> Pagina 113 di 167 <item name="FLIMO" value="D" ></item> <item name="DTREO" value="20073030" ></item> <item name="datas" value="" ></item> <item name="NRCTO" value="14 " visibility=”false”></item> <item name="xcontext" value="2" mandatory=”true” ></item> <item name="numberOfElements" value="0" ></item> <item name="CDCSO_rascl" value="A & A SPA" ></item> <item name="NRIMO" value="14" disabled=”true” ></item> <item name="currentPage" value="1" ></item> </data> <msgs> <msg tgt=”DTREO”>Data non valida</msg> </msgs> </response> In tale documento XML è possibile riconoscere i seguenti elementi: - l’intestazione <response> contenente informazioni sulla finestra cui è destinata, il metodo dell’action di Struts che ha prodotto la risposta, il campo cui si riferisce (opzionale), il contesto applicativo (si veda quanto riportato nel paragrafo “ACGDynaActionForm a proposito della property denominata xcontext) - la sezione <data> che include un insieme di elementi (<item>) contenenti i nomi degli property del form bean, i relativi valori ed (opzionalmente) le informazioni visuali (visibility, disabled, mandatory e display) - la sezione <postActions> che include un insieme di elementi (<action>) contenenti i nomi di operazioni da eseguire sul client ad opera dell’UI engine dopo aver riempito la finestra con i dati - la sezione <preActions> che include un insieme di elementi (<action>) contenenti i nomi di operazioni da eseguire sul client ad opera dell’UI engine prima di riempire la finestra con i dati - la sezione <msgs> che include un insieme di elementi (<msg>) contenenti i messaggi da visualizzare nella finestra, con l’indicazione opzionale del target (vale a dire del campo nella finestra cui si riferisce il messaggio) Le pagine di Velocity di utilizzo comune sono presenti nel percorso <context_root>\pages e sono denominati: - output.vm: usato per formattare la risposta di un metodo Struts invocato tipicamente dopo aver eseguito un’operazione che interessa l’oggetto di business nella suo complesso (ad esempio nel caso di immissione o modifica di un record sul DB) - check.vm: usato per formattare la risposta di un metodo Struts che tipicamente comporta la modifica di un numero limitato di attributi del form bean e una modifica “limitata” dell’interfaccia (ad esempio nel caso di invocazione di un metodo di decodifica all’uscita campo) - headerRows.vm: usato per formattare la risposta di un metodo Struts che realizza la gestione delle righe in un oggetto di business del tipo testata-righe © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 114 di 167 3.3 Action e Form Bean in ACG Service Bus Benché nel modello di sviluppo ACG Vision4sia possibile usare qualsiasi tipo di Form e di Action previsti dalla framework Struts, si è ritenuto opportuno selezionare tipi specifici allo scopo di semplificare il processo di sviluppo, standardizzando e generalizzando taluni comportamenti tipici dell’architettura ACG. 3.3.1 ACGDynaActionForm Nel modello di sviluppo ACG Vision4si è scelto di utilizzare le form dinamiche della framework Struts, vale a dire le istanze della classe org.apache.struts.action.DynaActionForm (si veda il seguente link per reperire ulteriori informazioni) al fine di rendere più agevole lo sviluppo delle applicazioni: creare e mantenere una classe concreata di tipo ActionForm per ogni oggetto di business, con la dichiarazione esplicita dei suoi attributi ed i relativi metodi set() e get(), è un’attività dispendiosa, oltre che noiosa. Nel file di configurazione di Struts della web application di ACG Vision4è stato definito in particolare un form bean di uso comune denominato ACGDynaActionForm dal quale sono stati fatti derivare i form bean specifici delle varie action, mediante la clausola extends nella dichiarazione degli stessi (si veda ad es. la dichiarazione del form bean DynaRdpForm). <form-bean name="ACGDynaActionForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="xcontext" type="java.lang.String" /> <form-property name="xexec" type="java.lang.String" /> </form-bean> <form-bean name="DynaRdPForm" extends="ACGDynaActionForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="DTREO" type="java.lang.String" /> <form-property name="NRCTO" type="java.lang.String" /> <form-property name="NRIMO" type="java.lang.String" /> <form-property name="CDCSO" type="java.lang.String" /> <form-property name="FLIMO" type="java.lang.String" /> <form-property name="CDCSO_rascl" type="java.lang.String"/> </form-bean> ACGDynaActionForm potrà essere suscettibile dell’aggiunta di ulteriori proprietà supportate dall’architettura, che potranno essere automaticamente estese a tutti i form bean che da esso derivano. In particolare il form bean ACGDynaActionForm contiene la property xcontext che definisce l’operazione in corso sull’oggetto di business (immissione, modifica, etc.) ed è in grado di innescare comportamenti visuali specifici in fase di presentazione dei dati. I valori attualmente gestiti sono: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 115 di 167 0 Non definito 1 Immissione oggetto di business 2 Modifica oggetto di business 5 Visualizzazione Al fine di sfruttare il supporto alla validation che Struts fornisce, grazie al quale è possibile effettuare in modo automatico e dichiarativo i controlli formali sui parametri passati alle Action, nel modello di sviluppo ACG Vision4si è scelto di specificare per i form bean il tipo org.apache.struts.validator.DynaValidatorForm, che fonde le caratteristiche di dinamicità e di validazione dichiarativa sopra menzionate. 3.3.2 ACGDispatchAction Per quanto riguarda la dichiarazione delle classi Action, nel modello di sviluppo ACG Vision4si è scelto di estendere la classe standard org.apache.struts.actions.DispatchAction (si veda il seguente articolo alla pagina http://www.mokabyte.it/2004/09/jstruts-8.htm che ne descrive l’utilizzo nella framework Struts), creando la classe com.ibm.acgv4.guiweb.ACGDispatchAction dalla quale sono state fatte derivare le action che realizzano le diverse funzioni applicative. L’uso della DispatchAction di Struts consente, in generale, un’implementazione di tipo dichiarativo e permette di compattare più funzionalità in un’unica classe java, mentre la classe ACGDispatchAction permette di implementare metodi e funzionalità di utilizzo comune alle varie Action in architettura ACG Vision4. Viene riportata qui di seguito una dichiarazione tipica di una Action in ACG Service Bus: <action path="/RdPAction" type="com.ibm.acgv4.action.RdPAction" name="DynaRdPForm" scope="request" validate="false" input="/pages/output.vm" parameter="xmethod"> <set-property key="actionId" value="RDP"/> </action> essendo, nel caso in oggetto, com.ibm.acgv4.action.RdPAction com.ibm.acgv4.guiweb.ACGDispatchAction. una sottoclasse di Nella dichiarazione delle Action il parametro “parameter” deve essere sempre impostato al valore xmethod per consentirne il corretto funzionamento in architettura ACG Vision4. Nella definizione di un’Action deve essere specificata la property “actionId” indicando un prefisso che viene utilizzato dall’architettura ACG Vision4per recuperare, insieme al parametro xmethod, l’identificativo dell’operazione cui è associata l’operazione. Ad esempio, nel caso di richiamo dell’action specificando l’URL http://servername/context-root/RdpAction.do?xmethod=approve © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 116 di 167 verrà ricercata l’operazione RDP_approve nel file delle operazioni KOPER00F, controllata l’autorizzazione all’esecuzione dell’operazione ed effettuato il logging con i dati relativi (si vedano i capitoli relativi ai due argomenti menzionati). In generale, nella dichiarazione dell’action è opportuno specificare nel parametro “validate” il valore false e richiedere la validazione di Struts solo nei metodi per i quali è richiesta la validazione del form bean, mediante scrittura della seguente linea di codice: ActionMessages errors = actionForm.validate(mapping, request); Ciò eviterà che venga attivata la validazione del form bean anche nel caso di invocazione di metodi che devono effettuare operazioni puntuali su di esso (ad esempio decodifica di dati), senza che siano impostate tutte le sue proprietà. Il parametro “scope”, in generale, dovrà essere impostato al valore request, consentendo così l’attivazione simultanea di più finestre nello stesso desktop relative alla medesima funzionalità (nell’esempio sarà possibile aprire nello stesso desktop due finestre per l’immissione di una Richiesta di Preventivo, essendo RdPAction la classe Action per la gestione dei preventivi ACG). ACG Service Bus è in grado di conservare lo stato di un Form bean nella sessione di lavoro, grazie all’utilizzo di un command specifico che è in grado di “fondere” i dati passati al form bean in una request con quelli impostati nelle request precedenti. Per garantire questo automatismo, è necessario che il programmatore, prima della uscita da un metodo che ha modificato lo stato di un form bean, inserisca la seguente linea di codice: putActionFormInSession(request, actionForm, mapping); essendo: request = parametro di tipo HTTPServletRequest in input al metodo actionForm = il cast a DynaValidatorForm del parametro di tipo ActionForm in input al metodo mapping = parametro di tipo ActionMapping in input al metodo Il metodo putActionFormInSession, ereditato da ACGDispatchAction, salva il form bean nella sessione http come attributo con chiave ottenuta dalla concatenazione dell’identificativo della frame (vedi documentazione della User Interface) e del nome del form bean.Ad esempio la chiave può assumere il valore 1_F0_WIN_Rdp_DynaRdpForm, essendo: - 1_F0_Win_RdP l’identificativo univoco della frame nel desktop DynaRdpForm il nome del form bean. Il nome della chiave può essere ottenuto dal metodo getKey(HTTPSerVletRequest request, String key_suffix) essendo key_suffix nello specifico il nome del form bean. Il mapping delle Action in ACG Service Bus tipicamente prevede l’impostazione del parametro “input” al valore pages/output.vm, che rappresenta la pagina di default verso la quale viene reindirizzata la risposta per essere formattata nel formato XML richiesto dalla User Interface. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 117 di 167 3.3.3 Global forwards Un metodo di un’azione Struts in ACG Service Bus tipicamente prevede un forward, tramite scrittura dell’istruzione return mapping.findForward(“ . . . ”), alle seguenti risorse: output: nel caso in cui si voglia aggiornare in modo “integrale” il pannello che ha invocato l’azione (la risposta viene inviata alla pagina output.vm precedentemente descritta nel paragrafo che descrive l’integrazione della framework Struts con il template engine Velocity) - check: nel caso in cui si voglia aggiornare in modo “puntuale” il pannello (ad esempio invocazione di un metodo di decodifica all’evento “uscita-campo”); la risposta, in questo caso, viene inviata alla pagina check.vm - headerRows: nel caso in cui si debba aggiornare un pannello contenente il componente grafico che rappresenta le righe di un business object che viene rappresentato nella forma testa-righe (la risposta viene inviata alla pagina headerRows.vm) - I tre forward sopra specificati rappresentano sono stati specificati come global forwards nei file di configurazione di Struts. 3.3.4 Alcuni metodi di ACGDispatchAction Nella classe ACGDispatchAction sono stati implementati alcuni metodi di comune utilizzo nelle action che da essa derivano; qui di seguito se ne menzionano alcuni: - getLogon(HttpServletRequest request, HttpServletResponse response): restituisce l’istanza della classe com.ibm.acgv4.base.ACGLogon con le informazioni collegamento al sistema - getHibernateSession(ACGLogon logon, HttpServletRequest request, HttpServletResponse response): restituisce un’istanza della Session di Hibernate necessaria all’esecuzione di una transazione Hibernate in un metodo dell’action - decode(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response): effettua la decodifica di un attributo del form bean eseguendo il metodo checkId() di una classe Home di Hibernate o uno statement SQL (specificato nel file decode.properties presente in <context_root>/WEB-INF/classes/conf). Richiede l’implementazione del metodo getResource(String propName) nella classe Action concreta, che deve resituire per ogni property del form bean che deve essere decodificata: la chiave nel file WEB-INF\classes\decode.properties dello statement SQL che deve essere eseguito (il nome della chiave coincide con il nome della tabella su cui deve essere eseguita la query il nome qualificato della classe Home che deve implementare il metodo checkId(String id, BaseException be) che recupera il contenuto del campo descrizione del record individuato dall’id passato (nel caso di record non trovato restituisce null ed imposta il messaggio d’errore nell’oggetto di tipo BaseException passato) - saveACGV4Errors(HttpServletRequest request, ActionMessages errors): da usarsi in luogo del metodo standard di Struts saveErrors(HttpServletRequest request, ActionMessages errors) - putObjInSession(HttpServletRequest request, Object obj, String key_suffix) e getObjFromSession(HttpServletRequest request, String key_suffix): vengono usati per salvare un © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 118 di 167 oggetto nella sessione http e per recuperarlo, usando un suffisso per l’identificazione dell’oggetto (come nel caso del metodo putActionFormInSession la chiave viene ottenuta dalla composizione dell’identificativo della finestra nel desktop con il prefisso passato) - removeObjFromSession(HttpServletRequest request): rimuove tutti gli oggetti salvati nella sessone http dall’Action - close(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response): viene richiamato alla chiusura di una finestra ed esegue la rimozione di tutti gli oggetti salvati nella sessione http dall’Action richiamando il metodo removeObjFromSession 3.4 Chain of Responsibility Nel modello di sviluppo ACG Vision4la catena dei command della framework Struts che elaborano una invocazione di una Action è stata integrata, sfruttando le caratteristiche di configurabilità offerte dall’open-source, con il richiamo di tre command dichiarati nel file di configurazione acgv4-chain-config.xml e referenziati nel file chain-config.xml, presenti nella cartella WEB-INF nella applicazione web,. I command sono classi presenti nel package com.ibm.acgv4.base.chain.commands che estendono la classe org.apache.struts.chain.commands.ActionCommandBase ed implementano il metodo execute(ActionContext actionCtx). Nello specifico i command sono: - AuthorizationCommand: esegue il controllo di autorizzazione all’esecuzione di un metodo di una Action tramite la classe AuthorizationManager, che verifica l’autorizzazione dell’utente all’esecuzione dell’operazione identificata dalla concatenazione della property “actionId” specificata sulla Action con il metodo - MonitorCommand: traccia l’esecuzione di un metodo di una Action nel file di log delle azioni del Modulo Base ACG (KFLOG00F) recuperando il codice dell’operazione associata all’invocazione del metodo come specificato nel caso precedente - PopulateCommand: è deputato a popolare il form bean da inviare all’Action di Struts, recuperando i dati presenti nella request http ed integrandoli con quelli salvati nella session dalla Action (si legga quanto scritto in merito al metodo putActionFormInSession nel paragrafo “ACGDispatchAction”) - CheckExitCommand: ha il compito di rilevare le exit automatiche e non associate all’operazione lanciata - ExecuteExitCommand: ha il compito di eseguire le exit automatiche associate all’operazione lanciata - OperationsConflictsMgmtCmd: ha il compito di creare i vincoli sulle risorse virtuali associate all’opeazione lanciata 3.5 Configurazione in moduli Si è sfruttata la possibilità propria di Struts di suddividere in moduli l’applicazione web, creando una corrispondenza biunivoca tra i moduli applicativi ACG e non che si innestano nell’applicazione web ed i moduli struts. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 119 di 167 Il file di configurazione principale struts-config.xml è riservato al Service Bus di ACG Vision4, mentre per gli altri moduli si è convenuto di referenziare il file di configurazione di un prodotto come struts-xx.xml, essendo xx l’acronimo abbreviato di un prodotto ACG (ad esempio struts-ca.xml nel caso di Contabilità Aziendale, struts-te.xml nel caso di Tesoreria, etc.). I file di risorse dei messaggi ApplicationResources.properties sono stati posizionati in: - WEB-INF\classes\conf nel caso del Service Bus WEB-INF\classes\conf\xx nel caso degli altri moduli, essendo xx, come detto precedentemente, l’acronimo abbreviato di un prodotto ACG 3.6 ACGValidator Come noto la framework Struts fornisce gli strumenti per effettuare in modo automatico e dichiarativo i controlli formali sui campi digitati in un form HTML. In particolare, usando il Validator non è necessario scrivere alcun codice di validazione nel metodo validate() degli ActionForm, ma è il Validator stesso che fornisce questa funzione purchè i form bean dell'applicazione estendano uno degli ActionForm del Validator stesso: è questo il motivo per il quale si è scelto di utilizzare la classe DynaValidatoForm come tipo d’elezione in ACG Service Bus. . Il Validator è costituito da un insieme di classi predisposte per eseguire tutti i più comuni controlli di validazione in genere usati nelle applicazioni, ma.esiste anche la possibilità di creare routine di validazione non fornite dal Validator. Il Validator inoltre supporta sia la validazione server-side che quella client-side mediante opportune funzioni JavaScript, cosa non fornita dal meccanismo standard degli ActionForm di Struts. La configurazione delle routine di validazione da applicare ad un campo di un form è fatta mediante un file di configurazione XML, quindi esternamente all'applicazione ed è facilmente modificabile al mutare delle esigenze applicative. Nel file validator-rules.xml vengono dichiarate tutte le routine di validazione disponibili, i loro nomi logici e il codice JavaScript corrispondente a ciascuna routine di validazione per l'esecuzione dei controlli client-side. Nel file validation.xml si specifica come queste routine vengano applicate ai vari campi di input dei form dell'applicazione, ai quali si fa riferimento mediante i nomi dei form bean dichiarati nello struts-config.xml. In ACG Service Bus è stata creata una classe di validazione, denominata ACGValidator e presente nel package com.ibm.acgv4.guiweb, tramite la quale è possibile gestire alcune validazioni proprie del mondo ACG dichiarate nel file di configurazione acg-validator-rules.xml. Nel file validation.xml presente nel sottopercorso WEB-INF è possibile, ad esempio, verificare l’uso della regola acgDate che esegue la validazione delle date, nelle due opzioni che consentono di considerare la data 9999999 una data valida (vedi validazione della property SCAFI) o viceversa non viene considerata ammissibile una data nel formato precedente (vedi validazione della property DTREO). I file validation.xml sono stati posizionati in: - WEB-INF nel caso del Service Bus © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 120 di 167 - WEB-INF\classes\conf\xx nel caso degli altri moduli 3.7 Messaggi applicativi Le classi java che rappresentano oggetti di business in genere contengono metodi atti a validare i dati (attributi) dell’oggetto prima che essi vengano resi persistenti sul database. ACG Service Bus mette a disposizione l’infrastruttura necessaria per creare un eccezione java che rappresenti uno più errori applicativi(messaggi), decodificare la descrizione dell’errore secondo la lingua dell’utente, spedire l’errore alla parte view. 3.7.1 La classe BaseException In ACG Vision4è stata definita la classe BaseException che rappresenta una eccezione applicativa (legata ad un testo di messaggio) Occorre all’interno delle classi java creare un’istanza di tale classe specificando: il path relativo che contiene il file dei messaggi ad eccezione della directory resources: ad esempio “com.ibm.acgv4.ca”; se non specificato si assume quello base (com.ibm.acgv4.base”) l’identificativo del messaggio: ad esempio “CA0002” i parametri eventuali del messaggio ( esistono costruttori fino a quattro parametri) eventualmente l’identificativo del campo visuale a cui l’errore si riferisce Ad esempio: BaseException be = new BaseException(“com.ibm.acgv4.ca”,”CA0002”,”NUMOV”); throw be; Da notare che la classe BaseException contiene il metodo addException(BaseException b1) per poter annidare gli errori (messaggi) applicativi: ciò è utile nei metodi di validazione che riguardano più campi o attributi e si vuole produrre una lista di messaggi applicativi. 3.7.2 Catalogo messaggi Per ogni modulo applicativo occorre definire il file dei messaggi Messages.properties nella directory relativa /com/ibm/acgv4/xx/resources dove xx è l’acronimo del modulo ACG. Esempio: per la contabilità occorre creare il file Messages.properties nella directory /com/ibm/acgv4/ca/resources Esempio di contenuto del file: ################################################## # {0} = $Token CA0001=CA0001: Registrazione contabile {0} non corretta ################################################## CA0002=CA0002: Specificare la riga avere ################################################## © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 121 di 167 3.7.3 Gestione dei messaggi in struts ACG Vision4fornisce la classe BaseexceptionHandler in grado di catturare i messaggi applicativi, decodificarli ed inviarli alla parte VIEW in formato xml. Per fare ciò ogni modulo deve dichiarare nel file di configurazione di struts relativo al modulo l’utilizzo della classe nel seguente modo: <!-- Global Exceptions --> <global-exceptions> <exception key="errors.exception" scope="request" handler="com.ibm.acgv4.guiweb.BaseExceptionHandler" type="java.lang.Exception" /> </global-exceptions> © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 122 di 167 4. Componente Model 4.1 Introduzione Il cuore dell'applicazione viene implementato dalla componente Model, che incapsulando lo stato dell'applicazione definisce i dati e le operazioni che possono essere eseguite su questi. Quindi definisce le regole di business per l'interazione con i dati, esponendo alle componenti View e Controller rispettivamente le funzionalità per l'accesso e l'aggiornamento. Per lo sviluppo del Model quindi è vivamente consigliato utilizzare le tipiche tecniche di progettazione object-oriented al fine di ottenere un componente software che astragga al meglio concetti importati dal mondo reale. Il Model può inoltre avere la responsabilità di notificare ai componenti della View eventuali aggiornamenti verificatisi in seguito a richieste del Controller, al fine di permettere alle View di presentare agli occhi degli utenti dati sempre aggiornati. Nel modello di sviluppo ACG Vision4la Componente Model è stata implementata secondo due modalità differenti, che possono essere adottate a seconda delle necessità e delle condizioni progettuali, che possono essere sintetizzate rappresentate da vari fattori quali: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 123 di 167 - competenze Java disponibilità di codice legacy riusabile necessità di re-ingegnizzare i processi tempi di sviluppo In dipendenza dalla possibilità/necessità di sviluppare codice “nuovo” Java o di riusare il codice legacy (programmi RPG e similari) è possibile adottare una delle due opzioni di sviluppo della parte Model: - sviluppo “pure-Java” mediante utilizzo della framework open-source Hibernate per accedere al database - riutilizzo del codice legacy mediante utilizzo della Componente ACG Service Bus denominata “Legacy Connector” Nel seguito verranno descritti la framework Hibernate e l’uso che si è fatto della stessa in ACG Service Bus, nonché la componente che consente il riuso dei programmi legacy, ferma restando la possibilità di utilizzare un approccio misto avendo la possibilità di sviluppare codice Java ex-novo e riutilizzare codice legacy. 4.2 Utilizzo della framework Hibernate in ACG Service Bus Lo strato di persistenza maggiormente diffuso (soprattutto in applicazioni “Enterprise”) è costituito da database ed in particolar modo da database relazionali. Imbattendosi in un’applicazione del genere ci si rende presto conto che esiste una stretta relazione tra classi e tabelle del DB, tra oggetti e “righe” delle tabelle e tra proprietà di oggetti costituite da altri oggetti dell’ applicativo e relazioni (uno-uno, uno-molti, molti-molti) tra le varie entità del DB. A questo punto ci si trova di fronte ad una scelta: scrivere “a mano” il codice SQL che ci serve per creare (INSERT ....), modificare (UPDATE ...) e recuperare (SELECT) gli oggetti il cui stato è memorizzato nel database oppure affidare questo compito (alla lunga ripetitivo) ad un “motore” di bridging: uno strato software che si interpone tra l'applicativo e il database il quale, opportunamente configurato, mantiene sincronizzato, con il DB sottostante e a prescindere dal tipo di DB, lo stato degli oggetti persistiti in modo trasparente allo sviluppatore. Esistono diversi software in grado di svolgere il compito descritto (Castor, OJB, ecc.) ma tra tutti quello che più ha avuto fortuna e diffusione è Hibernate. Hibernate permette un approccio alla persistenza completamente diverso da quello tradizionale, un approccio totalmente orientato agli oggetti e trasparente allo sviluppatore. Il meccanismo di bridging è alla base dei motori di persistenza CMP (Container Managed Persistence) degli application server; vi è però una sostanziale differenza: le entità in un sistema j2ee sono modellate da EJB di tipo entità e devono implementare interfacce o essere sottoclassi dello standard enteprise; per essere usata con Hibernate, invece, una classe deve essere un POJO (Plain Old Java Object) ovvero un semplice Java Bean costituito dalle proprietà e i rispettivi getters/setter (quando serve). Partendo da un database legacy Hibernate è in grado di mappare le tabelle in classi: ogni riga della tabella sarà un'istanza della classe corrispondente. Lo sviluppatore non dovrà più occuparsi di interagire con il DBMS per mezzo di JDBC, ma userà la più familiare forma degli oggetti e le comuni © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 124 di 167 strutture dati del linguaggio. Nonostante l'indubbio vantaggio di usare oggetti anziché statement SQL inviati tramite JDBC e l'onere di gestire istanze di ResultSet, non ci troviamo di fronte ad un modello Object Oriented, bensì ad un modello relazionale in cui le tabelle appaiono in forma di oggetti: per lavorare con un modello realmente ad oggetti è sempre necessario introdurre un ulteriore strato di traduzione tra oggetti-tabella e oggetti-modello, con un costo sia in termini di tempo che in termini di codice da mantenere. Nel seguito del paragrafo non si intende fornire una descrizione della framework, che può essere recuperata consultando la documentazione in linea ai siti: - Hibernate Official Site - Reference guide 4.2.1 Session Factory e Configurazione Per poter usare Hibernate è necessario realizzare una classe che implementi l'interfaccia SessionFactory definita nella distribuzione standard; questa classe serve per creare nuove sessioni “di lavoro” tramite le quali è possibile manipolare gli oggetti persistenti. Il SessionFactory per poter essere usato necessita di essere preventivamente configurata per via programmatica oppure tramite un file di configurazione. Il file di configurazione (hibernate.cfg.xml) presente in <context>\WEB-INF\classes, si presenta come segue: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.bytecode.use_reflection_optimizer">false</property> <property name="hibernate.connection.datasource">java:comp/env/jdbc_ds </property> <property name="hibernate.dialect">org.hibernate.dialect.DB2400Dialect </property> <!-- JDBC connection pool (use the built-in) --> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider </property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">false</property> </session-factory> </hibernate-configuration> Nel file di configurazione di Hibernate sono specificate, usualmente, righe del tipo: <!-- Mapping files --> © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 125 di 167 <mapping resource="Ancl200f.hbm.xml"/> <mapping resource="Anco200f.hbm.xml"/> Una dichiarazione esplicita dei file di mapping non è possibile nella soluzione ACG, in quanto lo schema (vale a dire la libreria sui sistemi iSeries) nel quale risiede una tabella dipende dal Sistema Informativo al quale si collega l’utente. Ciò implica che: - la configurazione viene automaticamente creata al logon, recuperando tutti i file con estensione hbm.xml presenti nella cartella <context>\WEB-INF\classes - i file con estensione .hbm.xml si presentano come segue: <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.ibm.acgv4.Ancl200f" table="ANCL200F" schema="ACG_SCHEMA"> <comment>Anagrafico clienti</comment> <id name="cdcli" type="string"> <column name="CDCLI" length="6" /> <generator class="assigned" /> </id> DTD La particolarità del file di mapping delle tabelle ACG risiede nel fatto che nell’attributo dell’elemento <class> deve essere specificata la parola chiave ACG_SCHEMA schema - in fase di creazione della configurazione di un sistema informativo, la costante ACG_SCHEMA viene sostituita con il nome effettivo della libreria nella quale risiede la tabella in quel sistema informativo - esisteranno, nella cache dell’applicazione web, tante configurazioni, create dinaminacamente all’atto del primo collegamento, quanti sono i Sistemi Informativi ai quali si sono collegati gli utenti. - il meccanismo è completamente trasparente al programmatore ed è realizzato dalla classe com.ibm.acgv4.base.HibernateSessionFactory presente nella libreria acg_base.jar Viene riportato qui di seguito l’utilizzo di Hibernate in un’Action Struts (si veda anche quanto riportato nel paragrafo Controller Component). Transaction tx = null; Session session = null; try { ACGLogon logon = getLogon(request, response); com.ibm.acgv4.model.Rrct300f element = (com.ibm.acgv4.model.Rrct300f) getObjFromSession(request, RRCT300F); if (element == null) element = new Rrct300f(); //Impostazione proprerty del POJO © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 126 di 167 element.setNrimo(getInt(actionForm, "NRIMO")); element.setDtreo(getInt(actionForm, "DTREO")); element.setNrcto((String) actionForm.get("NRCTO")); element.setCdcso((String) actionForm.get("CDCSO")); element.setFlimo(getChar(actionForm, "FLIMO")); element.setDtv20(logon.getCurrentCompany()); element.setNuv20(logon.getUser()); session = getHibernateSession(logon, request, response); tx = session.beginTransaction(); setContext(actionForm, "2"); session.saveOrUpdate(element); tx.commit(); putObjInSession(request, element, RRCT300F_SUFFIX ); } catch (Exception e) { if (tx != null && tx.isActive()) tx.rollback(); throw e; } finally { putActionFormInSession(request, actionForm, mapping); session.close(); } Le operazioni necessarie per eseguire una transazione consistono in: - recupero dell’istanza di logon al sistema - recupero dell’istanza (se esiste) del POJO rappresentante l’oggetto di business dalla sessione http; nel caso in cui non esiste, viene istanziato tramite il costruttore di default - impostazione delle proprietà del POJO, recuperando i valori dal form bean - recupero dell’istanza della Session Hibernate - inizio della transazione Hibernate - salvataggio dell’oggetto sul DB tramite invocazione del metodo saveOrUpdate() sulla session, passando l’oggetto POJO. Hibernate automaticamente mappa il bean sulla tabella di database, utilizzando il file di mapping associato alla classe - salvataggio del POJO nella sessione http per futuro utilizzo - commit della transazione - chiusura della session 4.3 Legacy Connector ACG Service Bus consente, tramite questo componente, il riutilizzo di codice legacy ACG da parte di applicazioni client, sia attraverso le tecnologie J2EE che attraverso le tecnologia standard dei web service. Nel presente documento, con l’espressione “codice legacy ACG” ci si riferisce a: qualsiasi programma RPG, RPG-ILE e CLP che gira su un server iSeries sotto architettura di Modulo Base, che riceva in input quale unico parametro la KPJBA; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 127 di 167 qualsiasi classe java ottenuta dalla conversione, effettuata tramite i tool Caravel e Interactive Converter, di un programma RPG, RPG-ILE e CLP che soddisfi le condizioni descritte nel punto precedente. Si ipotizza che il riuso del codice legacy possa prevedere una sua modifica per: aderire al programming model definito dall’architettura ACG Vision4relativamente alla componente di riuso estrapolare la logica di presentazione da quella di business o condizionare il flusso elaborativo in modo da non escludere/inibire l’interazione con l’utente tramite l’emissione di interfacce video prevedere una nuova modalità di ricezione di dati in input e di emissione in output, in considerazione del fatto che il codice legacy non deve essere più visto come software destinato a produrre dati da presentare ad un’interfaccia utente, ma come fornitore di un servizio ad un’applicazione terza che può utilizzare le informazioni ricevute per presentarle a video o per altri scopi elaborativi 4.3.1 Diagramma architetturale La figura sottostante rappresenta il diagramma architetturale delle componenti che realizzano il richiamo del codice legacy. I componenti rappresentati nel diagramma sono i seguenti: Legacy Program Interface: è una classe java che riceve in input i dati da un’applicazione client (requester) e li prepara per il Legacy Connector. Una volta terminata l’elaborazione del programma servente, riceve i dati dal Legacy Connector e li invia all’applicazione richiedente come risposta alla richiesta fatta da quest’ultima. Legacy Connector: è un insieme di classi Java, il cui scopo è quello di richiamare un’operazione di Service Bus V4, passando i dati ricevuti dalla Legacy Program Interface. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 128 di 167 Al termine dell’esecuzione dell’operazione, l’output prodotto dal codice legacy viene recuperato ed inviato alla Legacy Program Interface. Codice legacy: qualsiasi programma RPG, RPG-ILE e CLP che gira su un server iSeries sotto architettura di Modulo Base che riceva in input quale unico parametro la KPJBA e non abbia una interfaccia a video oppure qualsiasi classe java ottenuta dalla conversione, effettuata tramite i tool Caravel e Interactive Converter, di un programma RPG, RPG-ILE e CLP che soddisfi le condizioni descritte nel punto precedente. Operazione di Service Bus V4: rappresenta l’evoluzione del concetto di azione di Modulo Base ACG in ACG Vision4, così come specificato nel paragrafo “Operazioni”. Il codice legacy viene legato all’operazione di Service Bus, attraverso la quale viene controllata l’autorizzazione di un utente all’esecuzione della stessa. Database ACG: rappresenta la base dati di un sistema informativo ACG, sia nativo iSeries che DB2 su piattaforma Win/Linux, che viene acceduta dal codice legacy associato all’operazione di Service Bus V4 Tabelle temporanee: rappresentano un set di tabelle di lavoro che vengono utilizzate dal codice legacy per leggere i dati in input provenienti dalla Legacy Program interface e per restituire a quest’ultima una risposta. Sono usate tipicamente nel caso di passaggio di una grossa mole di dati che deve essere scambiata tra applicazione client e codice legacy. La sequenza delle operazioni può essere sintetizzata come segue: a) La Legacy Program Interface riceve in input l’utente di collegamento, la password, il nome del Sistema Informativo e l’indirizzo IP del server al quale collegarsi. Tali informazioni, in alternativa, potranno essere ricavati dall’architettura, accedendo alla componente di Logon b) La Legacy Program Interface riceve i dati in input da un’applicazione client. Si distinguono due tipi di input: - dati da passare al codice legacy tramite KPJBA, inserendoli nella parte utente KPJBU (possono cioè essere compattati in una stringa di 256 caratteri) - un insieme di dati che non possono essere passati attraverso la KPJBU, nel caso di una struttura dati maggiore di 256 caratteri o di una collezione di dati (ad es. un ordine con dati di testata ed un insieme di righe) c) La Legacy Program Interface compatta i dati di input formando una stringa unica o un insieme di stringhe nel caso di collection di dati. Ogni stringa rappresenta la concatenazione ordinata dei dati ricevuti che il codice client dovrà decifrare utilizzando un’opportuna maschera di decodifica (vale a dire tramite una DS nel caso di legacy AS/400). Nel caso di dati non impostati dall’applicazione client (dati facoltativi) questi vengono impostati a tutti spazi nel caso di alfanumerici o a tutti zero nel caso di valori numerici. La legacy program interface normalizza la lunghezza di ogni stringa recuperata dal bean di input alla lunghezza prevista per ciascun tipo dato. Ciascuna stringa che rappresenta una struttura dati di input, sarà accompagnata dall’informazione relativa al tipo di struttura dati che la interpreta. L’ordine con il quale vengono ricevute le stringhe di input sarà rispettato in fase di scrittura sulle tabelle temporanee © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 129 di 167 d) La Legacy Program Interface passa al Legacy Connector la stringa che rappresenta l’input della KPJBU o la collezione di stringhe che rappresentano i dati da scrivere sulle tabelle temporanee. Inoltre la Legacy Program Interface passa al Legacy Connector le informazioni di logon (utente, password, indirizzo server e sistema informativo) se le ha ricevute in input. e) Il Legacy Connector recupera il logon dal server, se non ha ricevuto le credenziali di accesso, altrimenti crea un logon utilizzando il Componente di Logon. Quindi verifica che l’utente sia abilitato all’esecuzione dell’operazione di Modulo Base utilizzando la Componente di autorizzazione utente f) Il Legacy Connector crea un identificativo di transazione e costruisce la KPJBA, impostando opportunamente la KPJBU. Nel caso in cui non debba passare dati al codice legacy tramite KPJBU, imposta quest’ultima con l’identificativo di transazione. g) Il Legacy Connector scrive la collezione di dati nelle tabelle temporanee e richiama l’operazione di Service Bus V4, utilizzando una programma assimilabile all’odierno programma BMRUN presente in ACG legacy (la chiamata viene eseguita tramite Java Toolbox per AS/400 o framework Caravel a seconda dell’ambiente di esecuzione). Tale programma predispone opportunamente l’ambiente di esecuzione dell’operazione di Service Bus V4, impostando la lista delle librerie. h) Il codice legacy associato all’operazione di Service Bus V4 legge la KPJBU ed, eventualmente, le tabelle temporanee per recuperare i dati di input. Quindi esegue la propria elaborazione accedendo ed aggiornando eventualmente il database, producendo stampe, lanciando lavori batch o richiamando altri programmi che non prevedano interfaccia a video i) Al termine dell’elaborazione il codice legacy associato all’azione di Modulo Base restituisce il controllo al Modulo Base e questo al Legacy Connector j) Il Legacy Connector legge la KPJBU ricevuta dal codice legacy; se questa contiene un identificativo di transazione, vengono lette le tabelle temporanee per recuperare l’output ed impostare i dati da inviare alla Legacy Program Interface. In caso contrario viene inviata la KPJBU alla Legacy Program Interface. k) La Legacy Program Interface scompatta le stringhe che rappresentano la KPJBU ed eventualmente la collezione di dati di output, utilizzando opportune maschere di decodifica. E’ previsto pertanto che il programma legacy possa lavorare nei modi seguenti: a) scambiare i dati in input tramite KPJBU b) scambiare i dati in input/output tramite tabelle temporanee c) scambiare (opzionalmente) i dati in input/output tramite un parametro aggiuntivo di 2000 caratteri in aggiunta alla KPJBA d) ricevere l’input tramite KPJBU e scrivere l’output sulle tabelle temporanee e) ricevere l’input tramite tabelle temporanee e scrivere l’output nella KPJBU Eccetto che nel primo caso, è previsto che l’identificativo di transazione dei record da leggere/scrivere nelle tabelle temporanee sia passato tramite KPJBU. L’identificativo di transazione, come detto, viene generato automaticamente dal Legacy Connector. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 130 di 167 L’elaborazione, in ogni caso, produce la scrittura di uno o più record nel file di log dell’elaborazione per tracciare l’esito dell’elaborazione (segnalazione di errori o di elaborazione terminata regolarmente). La figura sottostante illustra l’interazione del Legacy Connector con le altre componenti infrastrutturali di ACG Vision4 e le tabelle di database alle quali accede. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 131 di 167 4.4 Struttura delle tabelle temporanee e di log dell’elaborazione Il file temporaneo di input (INPUT00F), il cui scopo è quello di accogliere i dati di input da inviare al codice legacy che non possono essere passati tramite KPJBU, sarà strutturato come segue: - Identificativo di transazione (35A) - Numeratore record (intero di 9 cifre) - Identificativo del tipo record (10A) - Dati di input (2000 A) Il file temporaneo di output (OUTPT00F), il cui scopo è quello di accogliere i dati di output prodotti dal codice legacy che non possono essere passati tramite KPJBU, sarà strutturato come segue: - Identificativo di transazione (35A): Numeratore record (intero di 9 cifre) Identificativo del tipo record (10A) Dati di output (2000 A) Il file di log dell’elaborazione sarà strutturato come segue: - Identificativo di transazione (35A) Tipo record (10A) Numeratore record (9A) Catalogo messaggi (10A) Tipo messaggio (2,0); valori ammissibili: I=Info - W=Warn - E=Error - F=Fatal Identificativo del messaggio (7A) Testo del messaggio (80A) Testo di secondo livello del messaggio (256A) Primo campo in errore (10A) Secondo campo in errore (10A) Terzo campo in errore (10A) In ciascun file la chiave univoca è rappresentata da: - identificativo di transazione tipo record numeratore record Nei file temporanei di input e output l’identificativo di tipo record individua la struttura dati che decodifica il contenuto dei dati che il codice legacy deve leggere o scrivere. Ad esempio, se si devono passare al codice legacy i dati della testata di un ordine più un insieme di righe, nel file temporaneo di input viene scritto un record per la testata, indicando nel campo “Identificativo del tipo record” il nome della struttura dati per la decodifica della testata; analogamente verranno scritte tante © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 132 di 167 righe quante sono le righe dell’ordine, indicando come tipo record la struttura dati per la decodifica della riga d’ordine. Si è scelto di indicare una lunghezza di 2000 caratteri per la scrittura dei dati; nel caso particolare che un record abbia una struttura dati di lunghezza superiore, dovranno essere scritti due o più record con tipo differente. Sarà cura del programma chiamante scomporre la struttura su più record diversi ed il codice legacy dovrà leggere opportunamente tali dati, ricombinandoli logicamente a formare una struttura dati unica (si ritiene tuttavia che si tratti di una evenienza remota). La scrittura e la lettura sui file temporanei e su quello di log dell’elaborazione sarà effettuata da programmi generalizzati del Service Bus. Al termine dell’elaborazione i file temporanei saranno ripuliti dei record scritti per scambiare l’I/O con il codice legacy, su indicazione specifica dell’utente. 4.5 Amministrazione delle tabelle temporanee La scrittura e la cancellazione di un gran numero di record nei file temporanei potrebbe causare problemi di occupazione disco su server iSeries, nel quale la cancellazione di un record da programma determina la cancellazione logica ma non quella fisica. Per poter ovviare a questo inconveniente è necessario creare i file temporanei impostando il parametro REUSEDLT ad *YES. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 133 di 167 4.6 Generazione codice Java per la Legacy program interface La generazione del codice java di richiamo del codice legacy può essere effettuata a) utilizzando un semplice editor per la scrittura di due file funzionali allo sviluppo e lanciando la classe di genrazione da linea comando DOS avendo impostato opportunamente il CLASSPATH b) importando il progetto LegacyGenerator.jar in Rational Application Developer (RAD) creando un progetto Java, utilizzando i tool della piattaforma di sviluppo per editare i file e lanciare la classe di generazione. La generazione del codice java richiede fondamentalmente la stesura di due file: o File di property strutturato con le seguenti chiavi: src_path=source system=xxx.xxx.xxx.xxx user=QPGMR password=PWDQPGMR libraries=ACG_OBJV4;ACG_DATV4 pkg_action=com.ibm.acgv4.xx.action pkg_bean= com.ibm.acgv4.xx.bean DS=ACV4DS;ADV4DS TABLES= ACTION=ACV4Action essendo: src_path: directory sorgenti progetto in RAD) system: IP address sistema iSeries user: utenza di servizio per il logon al sistema password: password utenza di servizio libraries: una lista di librerie nelle quali sono presenti tutti gli oggetti da elaborare (file di database, DS) pkg_action: nome del package per la classe Action pkg_bean: nome del package per le classi bean DS: nomi delle DS utilizzate separati da ; TABLES: nomi dei file di database utilizzati separati da ; ACTION: nome del file XML da utilizzare per la generazione della classe Action o File XML di definizione della classe Action nel formato <?xml version="1.0" encoding='UTF-8'?> <action actionName="AC43" actionType='AS400RPG' deleteOutput="true" deleteInput="true" extendedParms="true" batch="true"> <configuration name="configuration" type="ConfBean"/> <parameter name="parameter" type="AC43DS"/> <param2 type="AC43DS"/> <input type="INP1DS"/> <output type="OUT1DS"/> </action> essendo: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 134 di 167 Elemento action ( <action actionName="AC43" ...) Cardinalità dell’elemento = 1 actionName = nome dell’azione di Modulo Base da lanciare actionType = tipo azione (‘AS400RPG’) per le azioni AS400 deleteOutput = specifica se deve essere cancellato il contenuto di OUTPT00F al termine dell’esecuzione dell’azione (valori ammissibili: true o false) deleteInput = specifica se deve essere cancellato il contenuto di INPUT00F al termine dell’elaborazione (valori ammissibili: true o false) extendedParams = specifica se il programma associato all’azione richiede come parametro di input solo la KPJBA (valore impostato a false) oppure, in aggiunta al parametro di architettura, richiede il parametro di I/O alfanumerico di 2000 caratteri param2 = deve essere definito nell'XML in accoppiata con il parametro extendedParms impostato a true. Specifica il nome della DS nell’attributo type da usare per l’impostazione del secondo parametro di I/O batch = specifica se il programma da richiamare deve essere lanciato in interattivo o in batch (valori ammissibili: true o false; default è false in assenza dell’attributo) Elemento configuration ( <configuration name="co ...) Cardinalità dell’elemento = 1 Non modificare: serve ad aggiungere all’action java un’istanza della classe ConfBean da utilizzare in alternativa all’istanza della classe com.ibm.acgv4.base.ACGLogon Elemento parameter ( <parameter name="parameter" ...) Cardinalità dell’elemento = 1 name = nome della variabile usate per impostare la variabile KPJBU nella KPJBA (non modificare) type = nome del bean rappresentativo della DS da usare per impostare la KPJBU Elemento input ( <input type=" ...) Cardinalità dell’elemento = 0, n (vale a dire, specificare sole se il programma prevede l’acquisizione di dati dal file INPUT00F) type = nome del bean rappresentativo della DS da usare per scrivere nel campo DATA del file di database INPUT00F; il nome della DS viene riportato nel campo TPREC del file INPUT00F Elemento output ( <output type=" ...) Cardinalità dell’elemento = 0, n (vale a dire, specificare sole se il programma prevede la scrittura di dati nel file OUTPT00F) © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 135 di 167 type = nome del bean rappresentativo della DS da usare per scrivere nel campo DATA del file di database OUTPT00F; il nome della DS viene riportato nel campo TPREC del file OUTPT00F I file .properties e .xml devono essere collocati nella medesima cartella 4.6.1 Lancio del code generator Selezionare il file acg_legacy.jar, quindi le voci di menu Run – Run As – Java Application (vedi fig.1) e successivamente selezionare la classe CodeGenerator dalla finestra RunType (vedi fig. 2). La classe richiederà l’indicazione, nella finestra “Console”, del percorso del file di properties da utilizzare per la generazione (ad. esempio, si può digitare gen\AC43 per eseguire la generazione a partire da AC43.properties presente nella cartella source\gen visibile nella view “Package Explorer” della prospettiva Java. Al termine dell’elaborazione verranno generati una classe Action nel package relativo alle action e una serie di bean (bean di input e di output, bean rappresentativi di DS e table) Fig. 1 © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 136 di 167 Fig. 2 Al lancio della classe di generazione verrà richiesta l’indicazione del percorso nel quale è presente il file di property (Vedi fig. 3). Al termine dell’elaborazione effettuare il refresh del progetto per visualizzare i file generati. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 137 di 167 Fig. 3 La classe che rappresenta la Legacy Program Interface dovrà includere il metodo runService() che si preoccuperà di leggere i dati in input, formattarli e di passarli al Legacy Connector, insieme ad informazioni quali il nome dell’operazione di Service Bus V4 ed il tipo di operazione ed alle credenziali di accesso. In ricezione dovrà recuperare la risposta dal Legacy Connector e caricarla nella collection dei dati di output da passare all’applicazione client. Le classi da utilizzare in fase di generazione possono essere impacchettate all’interno di una libreria di classi java, che può essere rilasciata con un progetto WAR da importare in RAD insieme ai package di base che realizzano le funzionalità di logon e controllo delle autorizzazioni, da cui dipendono. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 138 di 167 4.7 Accesso tramite servizio web Sulla base di quanto indicato precedentemente, la Legacy Program Interface è una classe java, che avvalendosi del Legacy Connector e delle Componenti di Logon ed Autorizzazione, è in grado di eseguire il lancio di un’operazione di Service Bus V4. A partire da tale classe è possibile pertanto creare un web service che consenta l’invocazione del codice legacy tramite le tecnologie standard dei web services (SOAP, WSDL, etc.). Ciò può essere ottenuto utilizzando il wizard di generazione dei web services messo a disposizione da RAD, indicando il metodo runService() come funzione pubblica accessibile da web service. A tal proposito si può pensare di utilizzare il progetto di generazione del codice java per la Legacy Program Interface per generare/aggiornare i file XML e le classi java associate ai web service. Si ricorda che la generazione di un web service a partire da una classe java comporta, oltre alla generazione di classi java di serializzazione e deserializzazione, anche: - la creazione di un file wsdl - la creazione di un file xml di mapping - l’aggiornamento del file webservices.xml - l’aggiornamento del file ibm-webservices-bnd.xmi - l’aggiornamento del file ibm-webservices-ext.xmi Ciò richiede l’inclusione degli ultimi tre file, presenti nella web application realizzata secondo il modello di sviluppo ACG Vision4, nel progetto di generazione dei web services al fine di aggiornarli con l’aggiunta degli elementi che fanno riferimento al web service creato. Dopo aver provveduto alla creazione di un web service, occorre aggiornare la web application che consente l’accesso alle funzionalità tramite WS aggiungendo gli oggetti nuovi e sostituendo i file xml ed xmi aggiornati. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 139 di 167 4.8 Reengineering dei programmi legacy L’adozione di un nuovo approccio architetturale, basato sul design pattern Model-ViewController (MVC), richiede un’attività di reengineering del codice legacy finalizzata al riuso e alla salvaguardia del codice legacy, che costituisce un patrimonio di know-how tecnico-applicativo che conserva il proprio valore anche in un ambiente di sviluppo più evoluto. Un programma legacy si presenta tipicamente come un monolite nel quale sono compresenti e, spesso intrecciati tra di loro, la logica di business (algoritmi di calcolo, controlli di validità, totalizzazioni, etc.) e di accesso al database (logica di lettura tramite codici nativi READ, READE, CHAIN o statement SQL, istruzioni di aggiornamento e scrittura delle tabelle), quella di controllo (che associa le variabili del database a quelle dell’interfaccia a video) e la parte di presentazione, che gestisce l’emissione dei formati video e le interazioni con l’utente. La migrazione ad un’architettura più moderna, multi-tier, che supporti le nuove tecnologie e nuovi standard di presentazione, impone una riorganizzazione del codice in modo da determinare una netta separazione tra le componenti Model e Controller da quella View, demandata, tipicamente, ad un’interfaccia di tipo web o rich client. Le applicazioni tradizionali 5250 interagiscono direttamente con l’utente, che, tipicamente, seleziona un’opzione da un menu e vede presentare uno schermo. Quanto l’utente immette i dati nel video, questi sono memorizzati in un set di variabili, che il programma mantiene in memoria finché il programma non termina. Questi dati sono mantenuti anche se l’utente effettua ulteriori interazioni con il programma. Questo approccio funziona in quanto il programma viene eseguito in un job associato ad una workstation e quel job lavora solo a fronte delle richieste provenienti da quel terminale. In questo ambiente di esecuzione, lo stato del programma viene mantenuto in variabili di programma associate univocamente ad un solo job. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 140 di 167 Nelle applicazioni browser-based, invece, ogni richiesta utente può essere servita da un job differente e le informazioni immesse dall’utente non possono essere mantenute in variabili di programma poiché la volta successiva che l’utente interagisce con l’applicazione, questa può girare in un lavoro differente. Un punto chiave da ricordare è dunque che un’applicazione browser-based può avere delle informazioni di stato, ma queste non sono contenute in variabili di programma, ma possono essere mantenute in un cookie e trasmesse in ogni richiesta. Un programma legacy può essere riutilizzato in un’architettura moderna provvedendo ad estrapolare la parte di codice strettamente legata alla gestione delle interfacce video, mantenendo le parti (subroutine) che sono deputate all’accesso al database, sia in fase di lettura che di scrittura, al controllo dei dati e all’implementazione della parte algoritmica di un programma. Per quanto detto precedentemente, occorre considerare che un programma legacy fruito da client browser o da altra applicazione dovrà essere visto come un programma state-less (privo di stato), essendo questo contenuto o gestito direttamente dall’applicazione client che gira su un tier diverso da quello in cui risiede il programma legacy. Le diverse funzionalità che attengono alla logica di business e di controllo, possono essere scritte sotto forma di routine in un programma o meglio ancora, possono essere fruibili come moduli RPG-ILE che possono essere assemblati in un ILE service program. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 141 di 167 Le routine devono avere un’interfaccia ben definita, che rappresenti i dati che richiede in input e quelli che fornirà in uscita. Un programma re-ingegnerizzato dovrà, pertanto, prevedere un’interfaccia costituita da un insieme di informazioni che rappresentano i dati che dovrà trattare e le operazioni che dovranno essere eseguite su tali dati. I programmi legacy dovranno prevedere, tipicamente, nella loro interfaccia un codice di ritorno che possa essere letto dall’applicazione client per verificare se un’operazione è andata a buon fine o piuttosto sia terminata con un errore. Il parametro da utilizzare per il passaggio dei dati ad un programma legacy sarà rappresentato dalla KPJBA, fermo restando che, nel caso di una mole di dati più consistente, sarà necessario ricorrere al database o ad altre soluzioni (ad esempio user space su AS/400). Da un’ispezione effettuata sul codice ACG, appare evidente che: - è opportuno, nella maggior parte dei casi, scrivere un programma nuovo piuttosto che riutilizzare un programma preesistente, modificato con l’introduzione di una serie di controlli che servano a far funzionare lo stesso programma sia in architettura legacy che in un’architettura più avanzata. - il/i programmi nuovi derivati dal codice legacy potranno includere una serie di routine che derivino da una riscrittura di routine legacy opportunamente modificate per rimuovere i riferimenti all’interfaccia video o all’architettura legacy (richiami a programmi che eseguono operazioni tipiche legate all’interfaccia utente, come SNDPGMMSG, programma di ricerca con ?, etc.) - si deve considerare che un programma che contenga la logica di business, può essere richiamato da contesti diversi (client web, altra applicazione, web service), per cui deve essere predisposto a lavorare correttamente indipendentemente da chi o da come sia stato richiamato - laddove si vogliano avere sensibili vantaggi in termini di modularizzazione, riuso e prestazioni, occorre far utilizzo della programmazione ILE. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 142 di 167 4.9 Table Manager La classe TableManager consente le operazioni di lettura,scrittura, aggiornamento e cancellazione sulle quattro tabelle standard ACG ANTA200F, ASTA300F,ANTB300F, ASTB300F. La classe può essere utilizzata in due modalità: utilizzando un file di properties standard utilizzando un file di properties da fornire a runtime 4.9.1 Utilizzo tramite file di properties Occorre utilizzare i costruttori public TableManager(ACGLogon logon, String tableId //deprecated) public TableManager(ACGLogon logon, String tableId, Connection conn) public TableManager(ACGLogon logon, String tableId, String tableCode) //deprecated) public TableManager(ACGLogon logon, String tableId, String tableCode, Connection conn) che prevedono che nel file table.properties presente in com.ibm.acgv4.table vi sia una entry del tipo: tableid=nome_tabella,file_descrittore_tabella,lunghezza_codice_elemento (ad esempio CAU=ANTA200F,BTCAU,3). E’ prevista inoltre la presenza di un file di properties descrittore della tabella (ad esempio BTIVA.properties, PTGEN1.properties, etc), presente in com.ibm.acgv4.table.resources che descriva il formato record della tabella. Ad esempio, per la tabella delle causali contabili, il file BTCAU.properties: XDCAU=CHAR(25),1 XCAUC=CHAR(1),26 XCAUF=CHAR(1),27 XDAAV=CHAR(1),28 XRGIV=CHAR(1),29 XNREG=CHAR(1),30 … Il file BTCAU.properties puo’ essere generato utilizzando la classe di utilità GenerateTablePropertiesFile presente nel package com.ibm.acgv4.utils nella web-app acgv4 a cui si passino come parametri: Property generation file (default: CodeGenerator.properties) dando blank Nome tabella (ad esempio BTVAL, e’ il nome della DS che descrive il tracciato record della tabella da gestire) © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 143 di 167 La classe richiede che venga reimpostato un file di properties, Profile.properties, presente o da creare in com.ibm.acgv4.test per le credenziali di logon: USER=ACGV4 PASSWORD=XXXXXXX IP_ADDRESS=xxx.xxx.xxx.xxx TYPE=AS400 Nel caso delle tabelle di personalizzazione, per le quali la DS di interpretazione del campo XDTAB è variabile in dipendenza del codice elemento, sarà necessario utilizzare il costruttore public TableManager(ACGLogon logon, String tableId, String tableCode) throws BaseException { in cui tableId deve essere impostato al nome della chiave nel table.properties tableCode deve essere impostato con il codice della tabella Ad esempio, nel caso della tabella di personalizzazione di Contabilità Generale, i record GEN0001 e GEN-0002 sono decodificati tramite le DS PTGEN1 e PTGEN2. In table.properties scriveremo: GEN_0001=ASTA300F,PTGEN1,4 GEN_0002=ASTA300F,PTGEN2,4 In com\ibm\acgv4\table\resources andremo a copiare i file PTGEN1.properties e PTGEN2.properties descrittivi delle due DS di interpretazione dei record 1 e 2 della personalizzazione GEN. Per il codice scriveremo: TableManager tm1 = new TableManager(logon, "GEN_0001", "GEN"); TableManager tm2 = new TableManager(logon, "GEN_0002", "GEN"); HashMap hm1 = tm1.read(“0001”); HashMap hm2 = tm1.read(“0002”); Nella classe sono disponibili i metodi: • public HashMap read(String elementId) © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 144 di 167 • • • • • • public HashMap read(String elementId, String company) public String getBytes(HashMap values) public void insert(String elementId, String value, char status, String company) public void update(String elementId, String value, char status, String company) public void delete(String elementId, String company) public void changeStatus(String elementId, char status, String company) Il metodo read(String elementId) ritorna una HashMap contenente come chiavi i campi del file di properties che descrive la tabella e come valori i valori corrispondenti. Oltre al suddetto metodo, è stato aggiunto un metodo di read che riceve anche la company; in questo secondo caso viene eseguita una read aggiungendo il codice azienda passato alla condizione di select. Se viene passato null, l'azienda viene impostata ad * per le tabelle che sono nella libreria (ovvero nello schema) comune, mentre viene impostata l'azienda di logon nel caso di tabelle aziendali. I metodi di insert ed update, prevedono l'indicazione del codice elemento, del codice azienda (company, passando null vale quanto detto precedentemente), il flag di stato (status, carattere blank tipicamente, ma può essere passato qualsiasi altro carattere) e la stringa da scrivere in XDTAB/XDATI (value). L'annullamento e la riattivazione dell'elemento viene eseguita tramite il metodo changeStatus, impostando opportunamente il flag status (A oppure blank). Un valore valido della stringa può essere ottenuto usando il metodo getBytes(), che restituisce la stringa formattata secondo la DS BTxxx/PTxxx leggendo i valori da una HashMap con le coppie chiave-valore, in cui la chiave è al solito il campo della DS ed il valore è una stringa (per i numeri vale usare la notazione anglosassone xxxx.xx con il punto come separatore della parte decimale). Sample code String tableId = "VAL"; int elemLen = 4; TableManager tm = new TableManager(logon, tableId); HashMap hm = tm.read(“DOL”); Utils.printHashMap(hm, "Values for " + tableId + " - DOL "); //Insert elemento AAAW tm.insert("AAAW", tm.getBytes(getValuesForTest()), ' ', "*"); //Delete elemento AAAW tm.delete("AAAW", "*"); © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 145 di 167 //Tabella descrittiva Tm = new TableManager(logon, "LIN", sqlTable, 1, getPropertiesForDescriptiveTable(25)); hm = tm.read("F"); Utils.printHashMap(hm, "Values for " + tableId + "-" + elementId); tm.delete("W", "*"); } catch (BaseException be) { .... /** * @return */ private static HashMap getValuesForTest() { HashMap values = new HashMap(); values.put("XDVAL", "Venticinque"); values.put("XDISO", "EUR"); values.put("XFLGE", "L"); values.put("XDTRE", "20080407"); values.put("XCAMF", "512.654980"); values.put("XOPRE", "0.0"); values.put("XDIVS", "1.0"); return values; } © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 146 di 167 4.9.2 Utilizzo file di properties a runtime In questo caso occorre specificare a runtime le informazioni che descrivono la tabella ed i campi della tabella che si vuole gestire. Si utilizza il costruttore public TableManager(ACGLogon logon, String tableCode, String sqlTable, int elemLen, Properties props) dove: tableCode e’ il codice tabella, ad esempio VAL sqlTable e’ la tabella SQL che contiene la tabella, ad esempio ANTA200F elemLen e’ la lunghezza del codice elemento nella tabella, ad esempio 4 Le informazioni di struttura della tabella vanno inserite in un file di Properties costruito “ al volo”, passato direttamente al TableManager anzichè caricarle accedendo ai file presenti in com/ibm/acgv4/table/resources, in cui vado a specificare il formato record della tabella. Pertanto occorre scrivere istruzioni del tipo: Properties props = new Properties(); props.put("XDVAL", "CHAR(25),1"); props.put("XDIVS", "PACKED(5.0),26"); props.put("XOPRE", "PACKED(5.0),29"); props.put("XCAMF", "PACKED(11.6),32"); props.put("XFLGE", "CHAR(1),38"); props.put("XDTEU", "PACKED(8.0),39"); props.put("XDTRE", "PACKED(8.0),44"); props.put("XDISO", "CHAR(3),49"); try { TableManager props); tm = new TableManager(logon, "VAL", "ANTA200F", 4, HashMap hm = tm.read(elementId); .... © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 147 di 167 oppure il file di properties potrà essere caricato usando la classe ACGPropertiesLoader presente in com.ibm.acgv4.base come segue: Properties props = null; try { props = ACGPropertiesLoader.loadProperties("com” + Utils.FILE_SEPARATOR + “ibm” + Utils.FILE_SEPARATOR + “acgv4” + Utils.FILE_SEPARATOR + “ca” + Utils.FILE_SEPARATOR + “resources” + Utils.FILE_SEPARATOR + “BTVAL.properties"); } catch (IOException e2) { e2.printStackTrace(); } essendo BTVAL.properties un file di proprietà costruito secondo lo standard previsto dal TableManager. Nel caso di tabelle descrittive è opportuno usare il secondo costruttore congiuntamente al metodo getPropertiesForDescriptiveTable; ad esempio, nel caso della tabella lingua che ha lunghezza dell'elemento 1 e descrizione 25 char si può scrivere: TableManager tm = new TableManager(logon, "LIN", sqlTable, 1, getPropertiesForDescriptiveTable(25)); HashMap hm = tm.read("F"); tm.insert("W", "Lingua W", ' ', "*"); tm.delete("W", "*"); Nel caso delle tabelle descrittive in hm sarà presente una chiave denominata XDESC il cui valore corrisponderà alla descrizione. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 148 di 167 5. Linee guida per sviluppo funzionalità Vision4 5.1 Esecuzione Operazioni in Batch In ACG Service Bus sono state definite le componenti architetturali per permettere l’esecuzione di un’operazione in batch: Panel standard per i parametri di schedulazione batch ( presente nel file ddi izionario diz.js) Form BatchSchedulingForm da cui deve ereditare la Form che gestisce la WIN di parametrizzazione batch ( presente nel file struts.config.xml; negli altri moduli occorre copiare tale definizione nel file di configurazione di struts del modulo in quanto non è supportata l’ereditarietà da form non appartenenti al modulo) Action com.ibm.acgv4.action. ACGV4BatchSchedulingAction da cui deve ereditare l’Action che gestisce la WIN di parametrizzazione batch API standard per lanciare in batch l’operazione Operazione per poter abilitare uno o più ruoli alla schedulazione avanzata con codice ADV_SCHEDULING: all’atto dell’installazione SYSTEM_ADM all’esecuzione di tale operazione viene abilitato il ruolo Passi operativi Parte View Innanzitutto occorre definire tramite UI Wizard la Win atta a contenere il Panel Standard dei parametri di schedulazione e uno o più panel che contengano i parametri specifici dell’operazione da lanciare in batch. Se l’utente è autorizzato ad eseguire una schedulazione avanzata il panel della schedulazione apparirà come segue: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 149 di 167 La finestra contiene due bottoni: Esegui per schedulare il lavoro Annulla per chiudere la finestra senza schedulare alcunchè Il significato dei campi del panel è il seguente: Nome lavoro E’ un campo in cui è possibile indicare il nome del lavoro da schedulare: esso viene concatenato con il codice dell’operazione e data ed ora di salvataggio della schedulazione in modo da costituire una chiave univoca per il lavoro da schedulare. Descrizione lavoro E’ il campo che consente di impostare uan descrizione al lavoro: esso viene concatenato con la descrizione dell’operazione Codice operazione Contiene l’indicazione del codice operazione da schedulare; il campo è di solo output, in quanto preimpostato dall’Action che visualizza la WIN Descrizione operazione © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 150 di 167 Contiene la descrizione dell’operazione da schedulare il campo è di solo output. Frequenza E’ un combobox a scelta singola che consente di specificare la frequenza di schedulazione. I valori ammessi sono: “Una sola volta”: il lavoro sarà eseguito una sola volta ( è il valore proposto come default “Settimanale”: il lavoro sarà eseguito su base settimanale “Mensile”: il lavoro sarà eseguito su base mensile Data pianificazione E’ costituito da due campi che consentono di specificare la data in cui si vuole eseguire il lavoro. E’ in alternativa al campo “Giorno Pianificazione. Il primo campo a sinistra è un campo di tipo data impostabile anche utilizzando il componente visuale calendario Il secondo è un combobox a scelta singola tramite cui è possibile scegliere tra i seguenti valori : blank: per non specificare alcun valore (è il valore proposto come default); “Data corrente”: per indicare come data di schedulazione la data corrente; “Primo del mese”: per indicare che il lavoro deve essere eseguito il primo giorno del mese. Se all’atto della schedulazione si è nel primo giorno del mese e non si indica l’ora di schedulazione oppure si indica un’ora successiva a quella corrente il lavoro partirà immediatamente o all’ora richiesta rispettivamente; se invece si indica un’ora già trascorsa il lavoro partirà il mese successivo a quello corrente nell’ora richiesta; “Ultimo del mese”: per indicare che il lavoro deve essere eseguito l’ultimo giorno del mese. Se all’atto della schedulazione si è nell’ultimo giorno del mese e non si indica l’ora di schedulazione oppure si indica un’ora successiva a quella corrente il lavoro partirà immediatamente o all’ora richiesta rispettivamente; se invece si indica un’ora già trascorsa il lavoro partirà il mese successivo a quello corrente nell’ora richiesta; I due campi sono in alternativa e quindi: se si imposta uan data specifica nel campo a sinistra, nel combobox occorre scegliere il valore blank; se si sceglie un valore dal combobox diverso dal blank occorre non impostare il campo a sinistra data di schedulazione. Giorno pianificazione Permette di specificare uno o più giorni della settimana in cui far partire il lavoro. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 151 di 167 E’ in alternativa ai campi “Data pianificazione”. E’ un combobox a scelta multipla tramite cui è possibile specificare uno o più dei seguenti valori: blank: per non specificare alcun valore (è il valore proposto come default); “Lunedì”: per schedulare il lavoro di Lunedi’ ……. “Domenica”: per schedulare il lavoro di Domenica; “Tutti”: per schedulare il lavoro tutti i giorni. Se si specifica blank e/o tutti non è ammessa la selezione di altri valori. Se si specifica un valore diverso da blank, non è ammessa limpostazione dei campi di “Data Pianificazione” e viceversa. Se oggi è uno dei giorni della settimana specificati e l’ora specificata non è ancora trascorsa oppure non è stata impostata il lavoro viene eseguito oggi, altrimenti il lavoro verrà eseguito alla ricorrenza successiva del giorno specificato. Ora pianificazione Specifica l’ora a cui eseguire il lavoro nel formato HH:MM Se non impostato si assume l’ora corrente al momento della schedulazione. Giorno relativo del mese Permette di specificare uno o più giorni relativi del mese in cui il lavoro verrà eseguito. Questo parametro può essere impostato solo se: si sceglie il valore “Mensile” per la “Frequenza” si è scelto uno o più valori in “Giorno pianificazione” eccetto il blank E’ un combobox a scelta multipla tramite cui è possibile specificare uno o più dei seguenti valori: blank: per non specificare alcun valore (è il valore proposto come default); “Primo” per indicare che il lavoro deve essere eseguito alla prima occorrenza del giorno/gironi specificati in Giorno di pianificazione; “Secondo” per indicare che il lavoro deve essere eseguito alla seconda giorno/gironi specificati in Giorno di pianificazione; occorrenza del “Terzo” per indicare che il lavoro deve essere eseguito alla terza occorrenza del giorno/gironi specificati in Giorno di pianificazione; “Quarto” per indicare che il lavoro deve essere eseguito alla quarta occorrenza del giorno/gironi specificati in Giorno di pianificazione; “Quinto” per indicare che il lavoro deve essere eseguito alla quinta occorrenza del giorno/gironi specificati in Giorno di pianificazione; “Ultimo” per indicare che il lavoro deve essere eseguito all’ultima occorrenza del giorno/gironi specificati in Giorno di pianificazione; Ad esempio se specifico i seguenti parametri: “Frequenza” = “Mensile” © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 152 di 167 “ Giorno pianificazione” = “Martedì” + “Giovedì” (scelta multipla) “Giorno relativo del mese” = “Terzo” il lavoro sarà eseguito il terzo Martedì ed il terzo Giovedì di ogni mese. Coda E’ una combobox a scelta singola che consente di specificare la coda (il WorkManager) in cui eseguire il lavoro. I valori ammessi sono: “Differita” se si vuol eseguire il lavoro tramite il WorkManager ACGV4_DIFFE, che l’amministatore del sistema può creare per definire un Therad Pool con numero massimo di lavori contemporanei posto a 1 ( valore di default impostato); “Immediata” se si vuol eseguire il lavoro tramite il WorkManager DefaultWorkManager, preimpostato all’atto dell’installazione di Websphere come Thread Pool con numero massimo di lavori contemporanei posto a 5 Se l’utente non è autorizzato ad impostare una schedulazione avanzata, la finestra di schedulazione consentirà solamente una schedulazione “semplice” in cui è possibile eseguire un lavoro per una sola volta ad una certa data ed ora ( viene sottesa la Frequenza “Una sola volta”). Si riporta un esempio di voci di dizionario relative alla finestra precedente: // Batch sample diz["NAV_4AppSamplesMenu"]+="|4BatchSample"; diz["LBL_IT_4BatchSample"]="Esempio Parametrico lancio batch "; diz["LBL_EN_4BatchSample"]="Batch scheduling sample"; diz["CMD_4BatchSample"]="js:xcrtWIN('F0','WIN_BatchSampleScheduling');" diz["LBL_IT_BatchSampleScheduling"]="Esegui"; diz["LBL_EN_BatchSampleScheduling"]="Execute"; //Action diz["ACT_BatchSampleScheduling"]='{ "id":"BatchSampleScheduling", "url":"../BatchSampleSchedulingAction.do?xmethod=run&xauth=N" } '; diz["ACT_BatchSampleSchedulingCancel"]='{ "id":"BatchSampleSchedulingCancel","label":"CANCEL", "url":"../BatchSampleSchedulingAction.do?xmethod=close&xauth=N" } '; diz["PNL_ACGV4BatchSchedulingActions"]='{ "id":"ACGV4BatchSchedulingActions",' + '"bar":{ "span":"1", "buttonstyle":"width:100;" }, "actions":[ ' + $d("ACT_BatchSampleScheduling") +',' + $d("ACT_BatchSampleSchedulingCancel") + ' ] } '; © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 153 di 167 diz["PNL_ACGV4BatchSchedulingContaining"]='{ "id":"ACGV4BatchSchedulingContaining","formId":"Form1", "init":"true","url":"../BatchSampleSchedulingAction.do?xmethod=init&xauth=N", ' + ' "panels":[ ' + diz["PNL_ACGV4BatchScheduling" +',' + diz["PNL_ACGV4BatchSchedulingActions"] + ' ] } '; //Windows diz["WIN_BatchSampleScheduling"]= '{"window":{ "id":"BatchSampleScheduling", "width":"700", "height":"500", ' + ' "panels":[ ' + diz["PNL_ACGV4BatchSchedulingContaining"] + ' ] ' + ' } }'; In esso come si vede è riutilizzato il pannello PNL_ACGV4BatchScheduling che contiene i parametri necessari alla schedalazione; inoltre è stato creato il pannello PNL_ACGV4BatchSchedulingActions atto a contenere i bottoni che chiamano i metodi run() e close() dell’action Struts BatchSampleSchedulingAction. All’apertura della finestra viene eseguito il metodo init(). © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 154 di 167 Parte Controller ACG Service Bus mette a disposizione degli sviluppatori la form e l’action da cui estendere per poter schedulare un’operazione: Form BatchSchedulingForm da cui deve ereditare la Form che gestisce la WIN di parametrizzazione batch ( presente nel file struts.config.xml; negli altri moduli occorre copiare tale definizione nel file di configurazione di struts del modulo in quanto non è supportata l’ereditarietà da form non appartenenti al modulo) Action com.ibm.acgv4.action.ACGV4BatchSchedulingAction da cui deve ereditare l’Action che gestisce la WIN di parametrizzazione batch Di seguito è riportato l’estratto dello struts-config.xml che defisce Form ed Action per gestire una elaborazione batch: <form-bean name="BatchSampleSchedulingForm" extends="BatchSchedulingForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="PARAM1" type="java.lang.String"/> <form-property name="PARAM2" type="java.lang.String"/> </form-bean> … … <!--Batch Sample Action -starts--> <action path="/BatchSampleSchedulingAction" type="com.ibm.acgv4.action.BatchSampleSchedulingAction" name="BatchSampleSchedulingForm" parameter="xmethod" scope="request" input="/pages/output.vm" validate="false"> </action> La classe BatchSampleSchedulingAction deve estendere la classe ACGV4BatchSchedulingAction in modo da ereditare due metodi: - Il metodo public void initScheduling(ActionForm form,HttpServletRequest request) throws Exception { che permette di inizializzare la Form di schedulazione ai dati di default ed in in base all’autorizzazione dell’utente ad eseguire la schedulazione avanzata Il metodo public void schedule(ActionMapping mapping,ActionForm form,HttpServletRequest request,Map jobParameters) throws Exception { che consente di eseguire la schedulazione utilizzando i parametri impostati dall’utente; esso effettua preventivamente una validazione formale dei dati immessi dall’utente Ecco un esempio di codice dell’action per gestire la finestra: il metodo init richiama il metodo ereditato initScheduling dopo aver impostato il codice dell’operazione che si vuole schedulare: © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 155 di 167 public class BatchSampleSchedulingAction extends ACGV4BatchSchedulingAction { protected Logger log = Logger.getLogger(getClass().getName()); public ActionForward init(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { DynaValidatorForm actionForm = (DynaValidatorForm) form; debugMethod(request, actionForm, "init"); log.debug("actionForm BAtchComplex "+actionForm); ACGLogon logon = getLogon(request, response); setContext(actionForm, Utils.CTX_INSERT); actionForm.set(ACGV4BatchSchedulingAction.KOPID,"PPP"); super.initScheduling(actionForm,request); putActionFormInSession(request, actionForm, mapping); log.debug("lista lavori in IMMED "); return mapping.findForward("output"); } Il metodo run() è quello che riceve quanto impostato dall’utente e tenta di schedulare l’operazione, impostando eventualmente dei parametri tramite una HashMap: nella HashMap è possibile caricare istanze purchè implementino l’interfaccia java.io.Serializabile. Se NON vi sono errori chiude la finesta dando un messaggio di schedulazione avvenuta con successo,. altrimenti salva e ritorna gli errori nella maniera usuale public ActionForward run(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { log.debug("BatchSampleSchedulingAction FACCIO RUN "); ACGLogon logon=null; DynaValidatorForm actionForm = null; try { actionForm = (DynaValidatorForm) form; ActionMessages errors = actionForm.validate(mapping,request); if (!errors.isEmpty()) { © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 156 di 167 saveACGV4Errors(request, errors); putActionFormInSession(request, actionForm, mapping); return mapping.findForward("output"); } logon = getLogon(request, response); Map exampleMap=new HashMap(); exampleMap.put("ESEMPIO"," PARAMETRO DI ESEMPIO"); super.schedule(mapping,actionForm,request,exampleMap); } catch (BaseException b) { log.debug(" BASEEXCEPTION errore "+ b.getMessage()+b.format(logon)); throw b; } catch (Exception e) { e.printStackTrace(); throw e; } String CATALOGNAME = "conf.ApplicationResources"; String msgtxt="Operazione Schedulata con successo"; ResourceBundle bundle=null; try { bundle = ResourceBundle.getBundle( CATALOGNAME,logon.getLocale() ); msgtxt=bundle.getString("SCHED_OK"); } catch (MissingResourceException m2) { m2.printStackTrace(); } String out="xclose('"+request.getParameter(XWIN)+"','"+msgtxt+"')"; log.debug(" FACCIO CLOSE "+ out); addPropertyAsVector(actionForm, out, PREACTIONS); putActionFormInSession(request, actionForm, mapping); return mapping.findForward("output"); } } © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 157 di 167 Parte Model Per poter schedulare ed eseguire in maniera asincrona un’operazione occorre: - sviluppare uan classe java che implementi l’interfaccia org.quartz.Job o sue estensioni - definire un’operazione che contenga nel campo URL il nome completo di tale classe compreso di package Di seguito un esempio della classe java atta ad eseguita in batch: in questo caso la classe implementa l’interfaccia InterruptableJob estensione di Job per consentire l’interruzione controllata del lavoro. E’ da rimarcare che: - la classe java deve contenere il costruttore vuoto - il metodo che contiene la logica da eseguire è il metodo execute public class SampleInterruptableObject implements InterruptableJob { // has the job been interrupted? private boolean _interrupted = false; // logging services private static Logger _log = Logger.getLogger(SampleInterruptableObject.class); /** * <p> * Empty constructor for job initilization * </p> */ public SampleInterruptableObject() { } /* (non-Javadoc) * @see org.quartz.InterruptableJob#interrupt() */ synchronized public void interrupt() throws UnableToInterruptJobException { System.out.println(" SETTING INTERRUPT ON SampleInterruptableObject "); _interrupted = true; } /* (non-Javadoc) * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) */ © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 158 di 167 public void execute(JobExecutionContext arg0) throws JobExecutionException { try { System.out.println(" EXECUTING INTERRUPTABLE JOB "); // main job loop... see the JavaDOC for InterruptableJob for discussion... // do some work... in this example we are 'simulating' work by sleeping :) //Recupero parametri JobDataMap jMap=arg0.getJobDetail().getJobDataMap(); String operationId=(String)jMap.get("KOPID"); System.out.println(" EXECUTING Operation "+operationId); ACGLogon logon=(ACGLogon) jMap.get("ACGLogon"); System.out.println(" User "+logon.getUser()); System.out.println(" Information System logon.getCurrentInformationSystem()); " + String kpjba = (String) jMap.get("kpjba"); System.out.println(" KPJBA "+kpjba); for (int i = 0; i < 100; i++) { try { System.out.println(" EXECUTING INTERRUPTABLE JOB "+i); } catch (Exception ignore) { ignore.printStackTrace(); } // periodically check if we've been interrupted... if(_interrupted) { System.out.println(" EXECUTING INTERRUPTABLE JOB INTERROTTO "); _log.info("--- " + arg0.getJobDetail().getFullName() + " Interrupted... bailing out!"); -- return; // could also choose to throw a JobExecutionException // if that made for sense based on the particular // job's responsibilities/behaviors } else System.out.println(" EXECUTING INTERRUPTABLE JOB NON INTERROTTO "); } } finally { _log.info(arg0.getJobDetail().getFullName() + " completed at " + new Date()); } © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 159 di 167 } } Il metodo execute riceve come unico parametro il contesto di esecuzione JobExecutionContext Esso conterrà i parametri impostati all’attoi della schedulazione ed in più i seguenti parametri di contesto iniettati dal componente di schedulazione: - il codice dell’operazione, tramite la chiave “KOPID” - istanza di ACGLogon tramite la chiave “ACGLogon” - la kpjba realtiva all’operazione tramite la chiave “kpjba” © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 160 di 167 5.2 Modalità di richiesta esecuzione report di IBM Cognos tramite ACG Service Bus In ACG Service Bus è stata resa disponibile la classe di utilità com.ibm.acgv4.cognosInt.ACGV4CognosIntegration per richiedere l’esecuzione di un report ACG Vision4 sul server IBM Cognos ad essa integrato. La classe richiede che sia stato indicato nella risorsa URL acgv4/CognosServer l’indirizzo del server Cognos che deve prendere in carico la richiesta e che il server Cognos sia stato correttamente configurato seguendo le istruzioni presenti nella documentazione di installazione. La classe espone diversi metodi dei quali vengono commentati i più significativi. Essi si possono classificare in: Metodi sincroni, cioè metodi che consentono la produzione immediata e sincrona del report, da utilizzare all’interno di classi lanciate in batch tramite le funzionalità Vision4 o nel caso ci sia il requirement di visualizzare immediatamente la stampa all’utente Metodi asincroni, cioè metodi che consentono la produzione differita del report, tramite l’utilizzo delle funzionalità di WorkManager presenti in ACG Service Bus, il cui utilizzo è il default ACG Vision4 Metodi sincroni public static byte[] runReport(ACGLogon logon, HttpServletRequest request, String report, int type,HashMap parametri,boolean saveOnDB,String reportDescr) throws Exception { Questo metodo consente di richiedere l’esecuzione sincrona di un report, il cui contenuto viene restituito come parametro di ritorno. Il significato dei parametri è il seguente: - istanza delle classe ACGLogon, contenente le credenziali di accesso - istanza delle classe HttpServletRequest - report, che è la Stringa contenente il percorso (path) del report che si vuole eseguire; un esempio di percorso completo è il seguente: "/content/folder[@name='ACG']/package[@name='ACG_FinanceContabile']/report[@name='A CGFINCON018']" - type, intero che definisce il tipo di report che si vuole ottenere; sono ammessi i seguenti valori, codificati nella classe di integrazione stessa: ACGV4CognosIntegration.REP_TYPE_CSV per ottenere il report in formato csv ( comma separeted value) ACGV4CognosIntegration.REP_TYPE_DOC per ottenere il report in formato word ACGV4CognosIntegration.REP_TYPE_MHT per ottenere il report in formato html ACGV4CognosIntegration.REP_TYPE_PDF per ottenere il report in formato pdf ACGV4CognosIntegration.REP_TYPE_XLWA per ottenere il report in formato excel 2003 © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 161 di 167 - parameter rappresenta i parametri necessari all’esecuzione del report in forma di HashMap ( coppia chiave-valore); - il booleano saveOnDB che stabilisce se salvare il report nella coda di stampa ACG Vision4 (valore true) o meno (valore false) - la stringa reportDescr che rappresenta la descrizione del report da associare ad esso all’atto del salvataggio della stampa nella coda di stampa ACG Vision4 Il metodo restituisce il report generato sotto forma di array di bytes. Metodi asincroni public static void runReportAsync (ACGLogon logon, HttpServletRequest request, String report, int type,HashMap parametri, boolean saveOnDB,String reportDescr) throws Exception { Questo metodo consente di richiere l’esecuzione asincrona di un report. Il significato dei parametri è identico al caso sincrono documentato in precedenza public void runReportAsyncWithDeleteTT(ACGLogon logon, HttpServletRequest request, String report, int type, HashMap parametri ,String reportDescr, String tableName, long jobNumber, String jobNumeberColumnName) throws Exception { Questo metodo consente di richiedere l’esecuzione asincrona di un report e la cancellazione dei dati presenti in una tabella di appoggio dopo la sua generazione: la cancellazione viene effettuata in modalità asincrona, utilizzando la coda dei lavori ACGV4_DIFFE. Il significato dei parametri è identico al caso sincrono documentato in precedenza, salvo i seguenti parametri aggiuntivi: - la stringa tableName è il nome della tabella contenente i dati necessari alla generazione del report - jobNumber è il numero identificativo della elaborazione con cui sono contrassegnati i dati necessari alla generazione del report - la stringa jobNumberColumnName è il nome della colonna che contiene l’identificativo di elaborazione sopracitato In questo caso, quindi, una volta generato il report verrà richiesta la cancellazione dei dati presenti in esso tramite l’invocazione del metodo statico clearTT(logon, tableName, jobNumber, jobNumberColumnName) della classe CleanTTWorkUtility © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 162 di 167 Visualizzazione report eseguito in modalità sincrona Nel caso in cui sia richiesta la produzione sincrona di report da visualizzare immediatamente all’utente, ACG Service Bus mette a disposizione nella classe ACGDispatchAction i metodi public ActionForward sendResponseForReport(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String title, int reportType, byte [] b) Il metodo serve a preparare la risposta da inviare al client (browser): i parametri richiesti, oltre a quelli usuali sono: - la stringa che sarà utilizzata come titolo della finestra atta ad ospitare la visualizzazione del report - il tipo di report che si è in procinto di mostrare; deve essere uno dei tipi report previsti in ACGV4CognosIntegration come specificato nei paragrafi precedenti - l’array di byte che contiene il report, che è stato restituito dal metodo runReport() di ACGV4CognosIntegration public ActionForward sendResponseForErrorOnReport(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, Exception e) Il metodo serve a preparare la risposta da inviare al client (browser) in caso di errori ritornati dal metodo runreport. Tra i parametri richiesti, oltre a quelli usuali, vi è la istanza di Exception sollevata. Il metodo dell’action di struts atto a richiedere l’esecuzione della stampa dovrà: 1. in caso di errore catturare l’eccezione ed effettuare il forward al metodo ereditato sendResponseForErrorOnReport() 2. in caso di esecuzione eseguita con successo (nessun errore) effettuare il forward al metodo ereditato sendResponseForReport() © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 163 di 167 5.3 Personalizzazioni in architettura ACG Vision4 5.3.1 Personalizzazione dello startup della web-application In ACG Vision4 è possibile eseguire una o più classi allo start-up dell’applicazioni senza necessità di dichiarare Servlet nel file web.xml (descrittore della distribuzione). Per fare ciò occorre creare nel percorso WEB-INF\classes\conf il file custom.properties che deve contenere una property dal nome STARTUP_SERVLET con la lista delle classi di personalizzazione da richiamare separate da punto e virgola. Ad esempio, supponendo di aver creato le classi mypkg.MyStartupServlet e mypkg.MyStartupServlet2 occorrerà inserire nel file custom.properties la seguente riga: STARTUP_SERVLET=mypkg.MyStartupServlet;mypkg.MyStartupServlet2 Le classi che vengono richiamate devono implementare il metodo public void init(ServletConfig sc) throws Exception essendo sc l’istanza di javax.servlet.ServletConfig inizializzata dalla web application al suo avvio. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 164 di 167 5.3.2 Personalizzazione della User Interface – posizionamento dizionari In ACG Service Bus, al fine di ottimizzare il caricamento dei dizionari visuali (presenti nella cartella addon/diz) è stato utilizzato l’OSS Jawr che compatta i dizionari dei diversi moduli ACG Vision4 in file con estensione .js, denominati bundle. Il file che dichiara i bundle specificando i dizionari che ne fanno parte si chiama jawr.properties ed è presente in WEB-INF\classes\conf. Il caricamento dei bundle avviene allo start-up della web application. Il file di configurazione dei bundle associati ai dizionari di prodotto (jawr.properties) è impostato per includere automaticamente i dizionari contenuti in cartelle predefinite in modo da consentire la personalizzazione della User Interface dei prodotti Vision4. Si è previsto che, qualora si voglia effettuare una personalizzazione che interessi un bundle di un prodotto identificato dall’acronimo XXX, il personalizzatore dovrà creare le cartelle addon/diz/customizations/XXX addon/diz/customizations/XXX/EN addon/diz/customizations/XXX/IT per le definizioni degli elementi grafici per le risorse in lingua inglese per le risorse in lingua italiano ed includere uno o più dizionari di personalizzazione in ciascuna cartella. I dizionari di personalizzazione potranno sovrascrivere elementi grafici definiti nei dizionari ACG di prodotto, così come potranno definirne di nuove. Si riporta qui di seguito la lista dei bundle dei prodotti ACG Vision4 Prodotto Service Bus Amministrazione Cespiti Controllo di Gestione CRM Logistica Produzione Provvigioni Ritenuta d’acconto Tesoreria plus © Copyright ACG Srl 2014 Tutti i diritti riservati. Bundle SVB, COM FIN, CAN, GEC CSP CTG CRM SCM MAN PRV RAC TSP Pagina 165 di 167 5.3.3 Personalizzazione del richiamo dei report Cognos In ACG Vision4 è previsto che i report da richiamare dalle classi java di lancio delle stampe siano dichiarati in file di property denominati cognos_path.properties. Tali file sono ubicati in WEB-INF\classes\conf\xxx essendo xxx il nome del modulo di appartenenza (scm per la Logistica Vision4, man per la Produzione Vision4, etc.); per il Service Bus il file è ubicato in WEBINF\classes\conf. Viene riportato qui di seguito un estratto di un file cognos_path.properties: #REPORT_report_name=/content/folder[@name='ACG']/package[@name='ACG_xxx']/report[@ name='report_name'] REPORT_Articoli=/content/folder[@name='ACG']/package[@name='ACG_SVB']/report[@name ='ACGSVB0001'] REPORT_Clienti=/content/folder[@name='ACG']/package[@name='ACG_SVB']/report[@name= 'ACGSVB0002'] REPORT_Fornitori=/content/folder[@name='ACG']/package[@name='ACG_SVB']/report[@nam e='ACGSVB0003'] La classe java di lancio di una stampa recupera il path del report cognos da lanciare accedendo al file .properties con la chiave appropriata (nell’esempio sopra riportato, le chiavi sono REPORT_Articoli, REPORT_Clienti e REPORT_Fornitori). L’architettura ACG Vision4 prevede, inoltre, che prima di recuperare il file standard cogno_path.properties venga recuperato il file cognos_path_informationSystem.properties essendo informationSystem il nome del Sistema Informativo su cui deve girare il report. Nel caso in cui non sia presente tale file, viene recuperato il file di default cognos_path.properties dallo stesso percorso di prodotto. Questo meccanismo di recupero del file che contiene le informazioni sui path dei report Cognos consente di realizzare delle personalizzazioni dei report per sistema informativo. Pertanto, nel caso in cui si voglia personalizzare un report, è necessario: 1. Creare un report in Cognos per copia da quello standard, specificando un nome package e opzionalmente un nome report diversi, e personalizzarlo. 2. Creare nella web application il file cognos_path_informationSystem.properties in WEBINF\classes\conf\module_acronym dove module_acronym è l'acronimo del prodotto Vision4 a cui appartiene il report (scm, man, etc) e _informationSystem e' il nome del sistema informativo (ad esempio se il sistema informativo si chiama DEMOSI ed il report appartiene al prodotto Produzione, occorre creare il file WEB-INF\classes\conf\scm\ cognos_path_DEMOSI.properties 3. Inserire nel file di properties suddetto la chiave che rappresenta il nome del report Vision4 associandogli il path Cognos del report personalizzato creato. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 166 di 167 5.3.4 Gestione tabelle utente nei file ANTA200F, ASTA300F, ANTB300F, ASTB300F In ACG Vision4 è prevista la possibilità, come descritto nel paragrafo 4.9, di gestire i dati tabellari mediante la classe com.ibm.acgv4.table.TableManager, la quale è in grado di recuperare le informazioni di struttura di una tabella standard ACG accedendo al file com/ibm/acgv4/table/table.properties. A partire dalla PTF SVB140034D del prodotto Service Bus V1R4M0 è stata data la possibilità di recuperare le informazioni di struttura di una tabella utente (vale a dire una tabella di personalizzazione creata da un utente) accedendo anche al file table_custom.properties, posizionato sempre all’interno del percorso com\ibm\acgv4\table. Come per le tabelle standard ACG, una riga del file table_custom.properties deve essere del tipo tableid=nome_tabella,file_descrittore_tabella,lunghezza_codice_elemento Ad esempio, se si è definita la tabella XYZ in ANTA200F con lunghezza dell’elemento 3, occorre inserire in table_custom.properties XYZ=ANTA200F,BTXYZ,3 Come per le tabelle stadandard ACG, occorrerà inserire in com\ibm\acgv4\table\resources il file BTXYZ.properties descrittivo del tracciato della tabella XYZ. © Copyright ACG Srl 2014 Tutti i diritti riservati. Pagina 167 di 167