Elaborato Formisano Daniele N46001429

Transcript

Elaborato Formisano Daniele N46001429
Scuola Politecnica e delle Scienze di Base
Corso di Laurea in Ingegneria Informatica
Elaborato finale in Programmazione I
Tecnologie e strumenti per la
realizzazione di applicazioni in ambiente
Eclipse
Anno Accademico 2015/2016
Candidato:
Daniele Formisano
matr. N46001429
A chi ha sempre creduto in me
Indice
Indice .................................................................................................................................................. III
Introduzione ......................................................................................................................................... 4
Capitolo 1: Eclipse ............................................................................................................................... 5
Capitolo 2: Extension e Extension Point.............................................................................................. 7
2.1 Workbench extension points ................................................................................................. 8
2.1.1
Views ............................................................................................................................. 8
2.1.2
Editors ............................................................................................................................ 9
2.1.3
Commands ..................................................................................................................... 9
2.1.4
Menus ........................................................................................................................... 10
2.1.5
Handlers ....................................................................................................................... 11
2.1.6
Bindings ....................................................................................................................... 11
Capitolo 3: Il Plug-in.......................................................................................................................... 12
3.1 Package dstm_plugin.supporto ............................................................................................ 14
3.2 Package dstm_plugin.handlers ............................................................................................ 16
Capitolo 4: Esempi di funzionamento ................................................................................................ 18
Bibliografia ........................................................................................................................................ 22
Introduzione
Il linguaggio DSTM (Dynamic State Machine) è un’estensione delle macchine a stati gerarchiche
sviluppata dal gruppo di ricerca del Dipartimento di Ingegneria Elettrica e delle Tecnologie
dell’Informazione nell’ambito del progetto europeo CRYSTAL (CRitical sYSTem engineering
AcceLeration).
DSTM è stato sviluppato per soddisfare i requisiti espressi da Ansaldo STS, azienda leader nel campo
dei sistemi di controllo ferroviario ad alta velocità, al fine di offrire uno strumento formale per lo
sviluppo di modelli del comportamento di sistemi di controllo per applicazioni critiche.
La sua principale caratteristica è di fornire opportune primitive per la modellazione di sistemi di
controllo ferroviario offrendo al contempo una semantica ed una sintassi formale e sufficientemente
espressiva.
L’obiettivo finale di tale attività di ricerca è lo sviluppo di linguaggi e strumenti a supporto del
processo di verifica e validazione. In particolare, il gruppo di ricerca ha sviluppato un generatore di
casi di test per la verifica di requisiti funzionali, a partire da un modello DSTM del sistema e da
opportune specifiche di test.
In questo contesto, lo scopo della presente tesi è stato quello di definire uno strumento in grado di
integrare il tool grafico per la modellazione DSTM di sistemi di controllo ferroviario con i relativi
servizi di verifica dei modelli, merging di data e control flow e generazione dei casi di test.
La tesi è strutturata come segue. Nel primo capitolo viene introdotto brevemente il framework
Eclipse (EMF), sul quale è basato lo sviluppo di tutti gli strumenti di supporto alla modellazione e
alla generazione dei casi di test; il secondo capitolo illustra il concetto di Extension e Extension Point
di EMF, meccanismo alla base dell’espansione delle funzionalità della interfaccia grafica dello
strumento di modellazione DSTM; il terzo capitolo fornisce una descrizione della struttura del plugin sviluppato ed il quarto alcuni esempi di funzionamento.
4
Capitolo 1: Eclipse
Eclipse è un potente IDE (integrated development environment) open source, scritto in Java e
quindi indipendente dalla piattaforma sottostante, grazie alle potenzialità della Java Virtual
Machine.
Il progetto Eclipse è sostenuto dalla Eclipse Foundation, un’organizzazione non-profit
composta da più di cento importanti società quali Intel, HP, IBM, Ericsson, ma anche da
un’estesa comunità di sviluppatori open source.
I componenti principali (core components) di Eclipse, sono contenuti nell’Eclipse Project o
anche detto Eclipse Software Development kit (SDK);
Essi sono: Equinox, Platform, Java Development Tools (JDT) e Plug-in Development
Environment (PDE).
Tali componenti rappresentano tutti gli strumenti necessari all’estensione del framework ed alla
creazione di tool basati su Eclipse.
Data infatti la natura Open source dell’IDE, è possibile creare prodotti da esso derivati e
ridistribuirli in forma gratuita sotto licenza Eclipse Public License (EPL).
Uno dei fattori chiave di Eclipse è appunto la possibilità di estenderne le funzionalità tramite
plug-in che consentono di implementare specifiche funzioni.
Il runtime engine della piattaforma ha il compito di caricare i plug-in in memoria
dinamicamente, cioè solo nel caso in cui l’utilizzo del plug-in si rendesse necessario. Esso
implementa l’OSGI Service Platform, una specifica che permette di costruire applicazioni
modulari a componenti (bundle o plug-in).
Attualmente, mediante il meccanismo dei plug-in, Eclipse consente di supportare lo sviluppo
di software in diversi linguaggi tra cui Java, C++, Perl, Phyton, PHP.
La modularità e la possibilità di ampliamento della piattaforma sono le caratteristiche che hanno
favorito la sua diffusione, ma anche la stabilità, il supporto di tanti sviluppatori e la
distribuzione gratuita.
5
In particolare per l’implementazione del linguaggio DSTM è stato utilizzato l’Eclipse
Modeling Framework.
EMF fornisce strumenti per la modellazione che permettono di costruire e manipolare modelli
strutturati, ma anche di generare automaticamente gli artefatti (incluso il codice Java) necessari
alla gestione automatica dei modelli.
EMF ha come scopo principale l’unificazione di tre differenti strumenti di rappresentazione di
un sistema, ovvero il codice Java, il diagramma delle classi UML e il modello XML.
A partire infatti da una delle tre rappresentazioni del sistema, il framework è in grado di
generare le restanti due utilizzando un modello EMF.
6
Capitolo 2: Extension e Extension Point
In base alle caratteristiche di Eclipse è necessario sviluppare i moduli in modo che siano
quanto meno accoppiati possibile. In questo modo è possibile favorire l'assemblaggio e la
sostituzione dei vari componenti, evitando quindi di dover apportare più modifiche al sistema.
L'accoppiamento lasco si ottiene tramite il meccanismo degli extension e extension points.
Un plug-in che vuole consentire ad altri di estendere le proprie funzionalità dichiara nel file
di manifesto un extension point. Esso rappresenta un contratto, formato da combinazioni di
markup XML e interfacce Java, a cui le extension dovranno fare riferimento.
Un plug-in che ne vuole quindi estendere le funzionalità deve dichiarare nel file di manifesto
un extension appositamente sviluppata.
Il file di manifesto, plugin.xml, descrive quindi come il plug-in estende la piattaforma, quali
extension point mette a disposizione, e come implementa le funzionalità.
Punto chiave è che il Plug-in che viene esteso non deve conoscere i dettagli
dell'implementazione del Plug-in estensore; per questo plug-in sviluppati da aziende e persone
diverse riescono ad interagire senza problemi pur non avendo una conoscenza approfondita
l’uno del funzionamento dell’altro.
La piattaforma Eclipse mette a disposizione diverse tipologie di Extension point:

extension point interamente Dichiarative che non contribuiscono con alcun codice,
come ad esempio l’extension bindings che lega una combinazione di tasti ad un
particolare comando;

extension point per l'override del comportamento originale di un componente;

extension point per gestire elementi dell’interfaccia.
7
Ogni extension point è definito da uno schema file, necessario per dichiarare i propri attributi,
indispensabili ai fini dell’estensione corretta dello stesso. Questo schema viene utilizzato
durante lo sviluppo del plug-in per verificare la presenza di eventuali dichiarazioni invalide
nel file di manifesto ed è impiegato dal wizard del Plugin Development Environment per
guidare l’utente nei passi richiesti per la corretta definizione dell’estensione.
Di seguito sono presentati gli extension points messi a disposizione dal workbench, che
rappresentano la via primaria per estendere la piattaforma e che saranno utilizzati per lo
sviluppo del plug-in;
2.1 Workbench extension points
Il Workbench è la finestra principale dello strumento, ciò che l'utente vede quando avvia
Eclipse, per questo definito anche come “il desktop di Eclipse”.
È formato da view e editor, le cui diverse disposizioni prendono il nome di perspective.
Come detto gli extension point permettono ai plug-in di estendere le funzionalità di editor e
view esistenti o di provvedere all'implementazione di nuovi. Di seguito vengono introdotti
gli extension point relativi al Workbench tra cui quelli utilizzati per lo sviluppo del plug-in.
2.1.1 Views
Una view è una finestra, usata per presentare liste, gerarchie, informazioni relative al
contenuto dell'editor attualmente utilizzato, proprietà di un oggetto.
Tipicamente solo una istanza di una view può essere presente nel Workbench e ogni
cambiamento effettuato in essa è istantaneamente applicato al Workbench.
L’extension point org.eclipse.ui.views permette ai plug-in di aggiungere view al Workbench
o di estendere le funzionalità di view già esistenti.
In accordo con ciò precedentemente esposto, plug-in che forniscono o estendono una view
devono registrarla nel loro plugin.xml file e fornire le informazioni di configurazione
necessarie, come la classe di implementazione, la category alla quale appartiene, il nome e
l’icona che deve essere usata per rappresentare la view nei menu e nelle label.
8
Per quanto riguarda invece l’implementazione si può far uso dell’interfaccia IViewPart,
oppure si può specializzare la classe ViewPart.
2.1.2 Editors
Un editor è una parte del Workbench che permette all’utente di modificare un oggetto o un
file. Operano in maniera simile agli editor forniti dal sistema operativo, ma sono fortemente
integrati nella UI del Workbench. Un Editor è sempre associato ad un input object
IEditorInput, che rappresenta l’oggetto o il file con cui interagisce. Le modifiche all’oggetto
hanno effetto solo quando vengono confermate dall’utente.
Una differenza con le view è che più tipi dello stesso editor possono essere istanziati nello
stesso Workbench, associati però ad input differenti; se si prova infatti a modificare un oggetto
già associato ad un editor, non verrà istanziato un nuovo editor ma si verrà reindirizzati a
quello già esistente.
L’extension point org.eclipse.ui.editors è usata dai plug-in per aggiungere editor al
Workbench o estendere editor già esistenti.
Plug-in che contribuiscono al Workbench con un editor devono registrare l’extension
dell’editor nel loro file plugin.xml, insieme alle informazioni di configurazione.
Alcuni attributi sono simili a quelli necessari per le view, anche qui bisogna dichiarare infatti
la classe di implementazione, il nome e l’icona da usare per il menu del Workbench e per le
label.
Deve essere però aggiunto l’attributo relativo all’estensione del file con cui l’editor andrà ad
operare.
Per l’implementazione si può far uso dell’interfaccia IEditorPart, oppure si può specializzare
la classe EditorPart.
Quando un editor è attivo, crea una voce nel menu e nella toolbar del Workbench,
usando gli extension point org.eclipse.ui.commands e org.eclipse.ui.menus.
2.1.3 Commands
I command rappresentano un comportamento, a cui viene associato un ID.
Il comportamento sarà poi implementato attraverso uno o più handler.
9
Separare il command dall’implementazione significa dare possibilità a più plug-in
di implementare lo stesso comportamento.
Sono definiti usando l’extension point org.eclipse.ui.commands e richiede gli attributi name,
description e id.
È possibile anche specificare l’id di una categoria di comandi in cui inserirlo.
Un command non ha una implementazione di default, ma diviene concreto solo quando un
plug-in associa ad esso un handler tramite il command id.
Quindi i command possono essere implementati usando gli org.eclipse.ui.handlers ed
essere abbinati ad una combinazione di tasti (key) usando l’extension point
org.eclipse.ui.bindings; E ancora essere associati ad una voce nel menu, nella toolbar ed in
altre aree del Workbench tramite l’extension point org.eclipse.ui.menus.
2.1.4 Menus
L’extension point org.eclipse.ui.menus , permette di definire la posizione in cui un command
deve essere visibile nell’applicazione.
Consente infatti l’aggiunta di un elemento come una label o un bottone ad una parte del
workbench quale il menu o la toolbar principale o il menu di una view.
Per questo tra gli attributi necessari alla definizione dell’estensione sono presenti l’id del menu
o della toolbar o della trim area di interesse ed un punto di inserimento, rappresentato dal
locationURI dell’area di interesse.
Seguono alcuni esempi di locationURI:
o menu:file?after=additions

inserisce l’elemento nel menu File dopo il gruppo additions;
o menu:org.eclipse.ui.views.ContentOutline?after=additions

inserisce l’elemento nella vista Outline dopo il gruppo additions;
o toolbar:org.eclipse.ui.views.ContentOutline?after=additions

inserisce l’elemento nella toolbar della vista Outline dopo il gruppo additions;
Verrà poi associato il Command id del comando da eseguire quando l’elemento sarà attivato.
10
Gli elementi che compongono il menu seguono nell’interfaccia le stesse posizioni che hanno
nel file plugin.xml.
2.1.5 Handlers
L’handler dunque rappresenta l’implementazione di un command. Ogni plug-in può fornire
un handler per ogni command. Sarà poi il workbench a determinare quale handler è attivo in
ogni momento.
Per ogni comando può esserci un solo gestore attivo alla volta.
Mentre un command può avere un handler di default, più handler possono essere associati allo
stesso command.
È possibile utilizzare l’espressione <activeWhen/> nel file plugin.xml per specificare lo scope
di attivazione dell’handler, ad esempio una finestra specifica o una parte attiva.
L’implementazione dell’handler prevede l’uso dell’interfaccia IHandler oppure la
specializzazione della classe AbstractHandler.
All’attivazione dell’handler sarà invocato il metodo execute(ExecutionEvent) che raccoglierà
quindi il codice di interesse per l’implementazione del command.
Tramite l’oggetto ExecutionEvent si può ricavare qualsiasi parametro dall’oggetto chiamante,
tra cui il contesto dell’applicazione in cui il command è eseguito. Si possono quindi ottenere
molte informazioni relative allo stato corrente del workbench, come ad esempio la finestra
attiva del workbench, la shell attiva, la parte attiva, l’editor attivo.
2.1.6 Bindings
Il seguente extension point permette di associare un command ad una specifica combinazione
di tasti.
L’associazione è detta Key binding ed i plug-in possono definirle tramite l’extension point
org.eclipse.ui.bindings.
La combinazione di tasti usata per invocare un comando è definita dall’attributo sequence,
mentre l’attributo contextID indica in quale contesto la binding è attiva.
Viene ovviamente specificato il commandId del comando da eseguire.
11
Capitolo 3: Il Plug-in
Il fine del plug-in sviluppato è quello di integrare il tool grafico per la modellazione di sistemi
di controllo ferroviario con i relativi servizi di verifica dei modelli, merging di data e control
flow e generazione dei casi di test.
Eclipse fornisce un wizard che crea la struttura standard del plug-in e prevede l’aggiunta di
un Sample Menu nella menu bar e un bottone nella tool bar.
Sia l’elemento del menu che il bottone nella toolbar invocano lo stesso Sample Command
che apre una semplice finestra di dialogo. Il Sample Command è richiamabile anche da una
combinazione di tasti.
12
Il plug-in in sviluppo prevede l‘aggiunta di un singolo Menu alla menu bar di Eclipse, che
conterrà diversi elementi, ognuno dei quali invocherà un differente servizio.
Quindi, prendendo in analisi il file plugin.xml, avremo bisogno di diversi Command,
uno per ogni servizio che il plug-in deve essere in grado di invocare e altrettanti Handler da
collegare a ciascun Command, ed infine i Menu.
Sono superflui dunque gli extension points relativi al bottone nella toolbar e quello relativo al
key bindings, che saranno rimossi.
Ad ogni Handler come visto nel capitolo 2 sarà associata una classe java che si occuperà di
implementare il comando vero e proprio.
Il plug-in prevede i seguenti comandi:
COMANDI
VerifyAll
DESCRIZIONE
Verifica la correttezza del modello delle strutture di controllo e delle
strutture dati
VerifyData
Verifica la correttezza delle strutture dati
VerifyTrigger
Verifica la correttezza dei Trigger
VerifyCondition
Verifica la correttezza delle Condizioni
VerifyAction
Verifica la correttezza delle Azioni
VerifyModel
Verifica la correttezza del modello delle strutture di controllo
VerifyParameter
Verifica la correttezza dei Parametri
Merge
Genera un modello Merged a partire dal modello delle strutture di
controllo e dalle strutture dati
Generate
Genera casi di test a partire dal modello delle strutture di controllo e
dalle strutture dati
Generate with Input Genera casi di test a partire dal modello delle strutture di controllo e
dalle strutture dati e dagli input
13
Gli input del plug-in sono dunque il file Model ed il file Dataflow che dovranno essere
contenuti nella cartella model di ogni progetto di interesse.
La struttura del plug-in prevede tre package:
dstm_plugin contiene la classe Activator, che gestisce il ciclo di vita del plug-in stesso;
dstm_plugin.handlers contiene i 10 handler;
dstm_plugin.supporto contiene le classi Console e GetResource.
Segue dunque l’analisi di alcune delle classi principali suddivise per package.
3.1 Package dstm_plugin.supporto
1) Console
La classe Console si rende necessaria dato che per offrire un output in console il plug-in,
non può utilizzare il classico outputstream System.out, deve invece creare una nuova istanza
di IConsole e connetterla alla Console view.
È chiara dunque la finalità della classe.
Avendo a che fare con output di tipo testuale, sarà istanziata una MessageConsole.
Necessaria l’aggiunta al plug-in della nuova dipendenza org.eclipse.ui.console.
La classe quindi espone il metodo print(String) che localizza o istanzia una console etichettata
come “Console”, apre uno stream verso di essa e trasmette la stringa.
2) GetResource
La classe GetResource è da considerarsi tra le più funzionali del plug-in.
Il suo ruolo è quello di prelevare da un qualsiasi progetto presente nel workspace di Eclipse,
i due file DSTM e Dataflow.
14
Essa espone due metodi: getDSTM e getDataflow.
Funzione primaria dei due metodi è quella di localizzazione dei file;
la procedura si divide in due passi:

Utilizzando le classi IWorkbenchWindow, IStructuredSelection, IProject si risale al
path del progetto attualmente selezionato.

Dal path, usufruendo del metodo list(), si localizza la cartella model e se presente
vengono cercati in essa rispettivamente il file DSTM per il metodo getDSTM e il file
Dataflow per il metodo getDataflow. A questo punto si dispone del path di entrambi i
file.
Ora il comportamento si specializza:
Il metodo getDSTM, invocando la funzione loadXmiModel della classe DSTM4RailReader,
messa a disposizione dalla libreria DSTMv5.0.1, restituisce un Oggetto DSTM facendo uso
del path precedentemente ricavato.
Il metodo GetDataflow restituisce invece una stringa rappresentante il data flow del modello;
la lettura è effettuata utilizzando l’apposita funzione file2string, messa a disposizione dalla
medesima libreria DSTMv5.0.1.
Sono ovviamente presenti i controlli necessari ad evitare la richiesta dei servizi senza aver
prima selezionato un progetto o qualora nel progetto non sia presente la cartella
model o i file di interesse.
L’attuale implementazione porta con sé due vincoli legati ai file in ingresso.
Il primo prevede che i file DSTM e Dataflow siano contenuti nella cartella model del progetto
selezionato.
Il secondo vincolo pone una limitazione sul numero di file presenti nella cartella model;
deve necessariamente essere presente un solo file DSTM ed un solo file Dataflow, in quanto
non è stata gestita la presenza di più file, scenario di poco interesse.
15
3.2 Package dstm_plugin.handlers
Sono stati definiti tre gruppi di handler, ognuno relativo ad uno specifico proxy per
l’invocazione dei servizi di seguito descritti.
1) DSTMVerifier
Gli handler relativi al proxy DSTMVerifier, consentono di invocare tutti i servizi necessari
alla verifica del modello. La loro struttura è molto omogenea, segue quindi il singolo esempio
relativo al VerifyAll_Handler.
Come anticipato nel capitolo 2, il codice relativo al command rappresenta il corpo della
funzione execute della classe handler;
Vengono istanziate le classi Console e GetResource del package dstm_plugin.supporto ,
di cui invocati i metodi getDSTM() e getDataflow() per leggere i file di input.
È
poi
istanziato
l’oggetto
DSTMVerifierProxy
ed
invocato
il
suo
metodo
verify(dstm,dataSpecification), che invia l’opportuna richiesta al server e ricevuta risposta
ritorna l’esito in forma booleana, che sarà stampato in console.
Gli altri Handler di verifica seguono la medesima struttura, con la differenza che, in base al
comando, sarà istanziato un differente proxy ed invocata la funzione necessaria.
2) Merger
Più articolato il funzionamento dell’handler Merge.
Esso fa uso della libreria ModelMergeProxy.
La prima parte del codice segue la struttura vista per gli handler di verifica istanziando un
proxy di tipo ModelMergeProxy, e invocando la sua funzione mergeModel con input il DSTM
e Dataspecification ottenuti tramite l’oggetto getResorce.
16
Elemento di differenza è il tipo di oggetto restituito dalla funzione che non sarà un booleano,
bensì un oggetto DSTM da memorizzare nella root del progetto selezionato.
L’operazione di memorizzazione è cosi implementata:
1) Ottenuto il path del progetto di interesse, è creato alla radice il file merged.dstm4rail;
2) Viene associato ad esso lo stream FileOutputStream;
3) Quindi si converte l’oggetto DSTM in stringa, utilizzando la funzione modelToString
della classe DstmProxyUtils, fornita dalla libreria modelmergeproxy;
4) Si trasmette dunque la stringa al file tramite lo stream di output.
3) TestCaseGenerator
Il Generate_Handler e GenerateWithInput_Handler fanno uso del proxy TestCaseGenerator.
Anche in questo caso la struttura del codice è la medesima e l’elemento di differenza sarà
ancora l’oggetto restituito dalle funzioni del proxy invocate.
Il proxy istanziato è di tipo TestCaseGeneratorProxy e generate l ’identificativo della
funzione invocata. I parametri sono il DSTM e il Dataspecification a cui si affianca un vettore
di stringhe in qualità di input per il command GenerateWithInput.
Saranno quindi ritornati i casi di test raccolti in un vettore di stringhe che saranno stampate in
Console.
17
Capitolo 4: Esempi di funzionamento
In questo capitolo saranno mostrati tre diversi esempi di funzionamento, uno per ogni proxy
utilizzato.
Verranno invocate le funzioni VerifyAll, Merge e Generate.
Sarà fornito in input il Modello di un FlipFlop RS, circuito elettronico sequenziale utilizzato
nell’elettronica digitale come dispositivo di memoria, caratterizzato da due ingressi
denominati Set e Reset e da un’uscita Q.
In qualità di macchina sequenziale, il valore dell’uscita Q dipende oltre che dagli input anche
dallo stato precedente della macchina.
Control flow
Data Flow
18
4.1 Esempio di chiamata al Verifier
Prima operazione da eseguire è selezionare il progetto all’interno del Package Explorer, da
cui il plug-in leggerà i file da analizzare.
Non è possibile procedere altrimenti, il plug-in infatti stamperebbe in console un messaggio
di errore.
Quindi utilizzando la voce VerifyAll del menu Verify, sarà richiesto al proxy Verifier l’analisi
degli input, e l’esito sarà stampato in console. L’implementazione è stata analizzata in
dettaglio nel paragrafo 3.2.1 .
In figura è presente un esempio di output.
19
4.2 Esempio di chiamata al Merger
Selezionato il progetto di interesse nel Package Explorer, utilizzando la voce Merge del menu
Verify sarà richiesto al proxy ModelMerge il servizio di merging di data e control flow.
L’esito della procedura è riportato in console e se positivo comporterà la creazione del
modello merged nella directory radice del progetto.
L’implementazione è stata analizzata in dettaglio nel paragrafo 3.2.2 .
In figura è presente un esempio di output.
20
4.3 Esempio di chiamata al TestCaseGenerator
Selezionato il progetto di interesse nel Package Explorer, utilizzando la voce Generate del
menu Verify sarà richiesto al proxy TestCaseGenerator il servizio di generazione dei casi di
test. L’implementazione è stata analizzata in dettaglio nel paragrafo 3.2.3 .
I casi di test saranno riportati in Console.
In figura è presente un esempio di output.
21
Bibliografia
[1]
Dave Steinberg, Frank Budinsky, Marcel Paternostro, Ed Merks,
EMF Eclipse Modeling Framework
[2]
Eclipse documentation,
http://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Fguide%2Fworkbe
nch_cmd_commands.htm
[3]
clipse Plug-in Development FAQ
http://wiki.eclipse.org/Eclipse_Plugin_Development_FAQ#How_do_I_read_from_a_file_that_I.27ve_included_in_my_bundle.2Fplug
-in.3F
22