FeedReader
Auteur: Rich Unger
Cet article fut soumis lors du concours Win With NetBeans.
Un exemple et tutoriel de Plateforme NetBeans
Que fait FeedReader?
FeedReader est un simple browser pour les nouvelles RSS/Atom, modelé d'après le
plugin Sage de Mozilla Firefox. Il est
constitué de:
Une liste de fournisseurs (URLs vers les fichiers de descripteur rss/rdf/atom)
Une liste de titres de chaque fournisseur
Une fenêtre de navigateur (mozilla inclut dans une JFrame)
La frame intégrant Mozilla est fournie par la bibliothèque JDIC
qui utilise des appels JNI. J'ai configuré cet exemple pour utiliser
aussi bien les versions Linux et Windows des binaires JDIC.
A qui s'adresse ce tutoriel?
Le public cible est, évidemment, les personnes qui
désirent concevoir des applications depuis la plateforme NetBeans.
Je vais essayer de documenter en détail comment j'ai créé FeedReader,
et ce que fait chaque ligne des fichiers manifests, layers et sources Java,
surtout dans les cas où j'ai du faire quelque chose ...
d'un peu quirky.
C'est également la deuxième raison de ce tutoriel.
Tout ce qui est Quirkiness et workarounds seront mis
en évidence dans une “Quirk Box”:
Vous devez faire quelque chose quirky pour arriver à vos fins.
Dans tous ces cas, je donne le lien du ticket approprié dans
IssueZilla. En espérant que cela soit un document vivant, et que ces Quirk
Boxes puissent disparaître avec le temps, donnant comme résultat une plateforme
à partir de laquelle il est facile de créer des applications.
Pour Commencer
L'entièrerté du code source est disponible.
Pour ce tutoriel, il ne faut pas disposer d'un EDI particulier.
En fait, disons qu'il faut juste un éditeur de texte,
JDK 1.5.0 (un bogue dans 1.4.x empêche la compilation de cet exemple, bien qu'il
peut tourner sous 1.4.x), et Apache Ant 1.6.2 ou supérieur.
Pour simplifier mes exemples de ligne de commande, je présente du bash (unix ou cygwin).
Faites vous-même la transposition en ligne de commande windows si vous le désirez. Cela marche
impécablement bien, également.
Je ne désires pas que vous perturber en parlant de l'EDI NetBeans et de la
plateforme NetBeans. Suivez tout d'abord cet tutoriel. Comprenez comment les modules
fonctionnent. Ensuite, inquiétez-vous des outils (comme l'EDI NetBeans) pour que vous puissez les
créer plus facilement.
La première chose qu'il vous faut pour commencer est une copie de la
plateforme NetBeans. Vous n'avez pas besoin de la générer depuis le code source.
Vous pouvez juste télécharger une
version binaire et la désarchiver quelque part. J'ai mis la mienne dans
/home/rich/netbeans.
Ensuite, il sera très utile d'avoir quelques code source d'exemple pour commencer.
J'ai commencé avec mon cluster
build harness. C'est utile parce qu'il a déjà des scripts Ant pour compiler et
créer les modules, et il contient un module de démarrage, appelé 'snipe'. Bien sûr,
maintenant que je l'ai écrit, vous pourrez facilement démarrer votre application
avec l'arborescence de FeedReader.
J'ai désarchivé ma harness dans /home/rich/src/rss.
Finallement, vous devez configurer vos scripts Ant, pour qu'ils sachent où trouver votre plateforme netbeans:
Modifiez quelques lignes dans
/home/rich/src/rss/nbbuild/user.build.properties:
netbeans.dest.dir=/home/rich/netbeans
clustername=rssreader1
Par conventions, les clusteurs reçoivent des numéros de versions à la fin, bien que cela ne soit pas
strictement nécessaire.
Définition: Un cluster est un ensemble de modules et fichiers
ressources associés. Une installation NetBeans peut être composés depuis un ensemble de clusters, qui sont
sélectionnés lorsque vous lancez NetBeans. L'EDI NetBeans, par exemple, exécute les clusters
platform4, ide4, nb4.0, et extra. L'idée derrière les clusters est que vous avoir une installation de la plateforme
NetBeans et plusieurs applications partageant les mêmes clusters. Je peux par exemple installer le cluster
rssreader1 dans l'installation de l'EDI Netbeans, et en exécutant deux lanceurs différents, j'obtiens l'EDI et le FeedReader,
chacun partageant le cluster platform4, mais étant bien des applications totalement séparées.
Ensuite, modifiez quelques lignes dans
/home/rich/src/rss/nbbuild/user.cluster.properties,
dans le but de synchroniser le nom du cluster avec ce que nous venons de spécifier
dans user.build.properties:
user.cluster=cluster.rssreader1
cluster.rssreader1.dir=rssreader1
cluster.rssreader1= snipe
Maintenant, je peux exécuter ant dans le
répertoire nbbuild/, et j'aurai un répertoire
/home/rich/netbeans/rssreader1
qui contient le module snipe. Bien sûr, je ne désire pas un module snipe. Je vais donc tapez
ant clean pour m'en débarrasser.
Il est temps maintenant de passer aux modules que l'on désire.
Library Modules
Vous pourriez livrer l'application FeedReader en un seul module. C'est juste que ce n'est pas très modulaire.
Il se peut que FeedReader requière les bibliothèques JDOM, Rome, et JDIC.
Si vous désirez étendre cette application avec plusieurs modules qui pourraient utiliser
ces bibliothèques, il serait mieux de dépendre de seulement le module de la bibliothèque que
de FeedReader dans son entièreté. Aussi, vous pouvez faire les modules de bibliothèques
auto-chargeables.
Definition: Un module autochargeable est un module
qui sera automatiquement chargé dans NetBeans lorsqu'il sera requis (par un autre module).
En attendant ce moment, il ne prendra pas de mémoire à l'exécution.
Ajouter des Modules à l'Arborescence
Lorsque vous ajoutez un nouveau module, vous devez le faire connaitre au build build. Modifiez deux fichiers.
/home/rich/src/rss/nbbuild/user.cluster.properties:
cluster.rssreader1=snipe, \
anothermodule, \
yetanother
Cela indique aux scripts quels modules sont dans votre cluster, pour qu'il puisse savoir comment construire le cluster en tant qu'unité,
et dans quel répertoire installer les fichiers JAR en résultant.
/home/rich/src/rss/nbbuild/modules.xml:
<module>
<path>snipe</path>
<cnb>org.netbeans.modules.snipe</cnb>
</module>
Cela fait correspondre le nom du répertoire contenant le code source du module avec le nom connu par NetBeans au runtime
(“cnb” est l'abbréviation de code-name-base).
JDOM
Je suppose que la plupart des lecteurs savent ce qu'est JDOM. C'est une API de parseur XML, et la seule raison pour
laquelle FeedReader en a besoin est parce que la bibliothèque Rome l'utilise.
Commencons par ajouter “jdom” à la list de cluster et
<module>
<path>jdom</path>
<cnb>org.jdom.api</cnb>
</module>
à la liste de module. Maintenant, voici un extrait du fichier du module JDOM:
build.xml
manifest.mf
nbproject/project.xml
nbproject/project.properties
lib/jdom.jar
src/org/jdom/api/Bundle.properties
Examinons les un à la fois.
build.xml: Le script commence par importer un autre script appelé projectized.xml.
La plupart du temps, c'est tout ce dont vous avez besoin dans le fichier build de votre module.
Cependant, dans le module JDOM, vous devez inclure jdom.jar dans le packaging du module, en plus du propres JAR du module.
Quirk #1: You shouldn't have to override the build
behavior in order to include additional libraries. You should be able
to declare such dependencies in the project.xml file and the build
scripts should know how to create the appropriate manifest entries
and include them in the nbm file.
http://www.netbeans.org/issues/show_bug.cgi?id=52354
Donc, surchargeons 2. La target “files-init” est une copie exacte de la target de projectized.xml,
avec cette ligne supplémentaire:
<include name="${nb.modules.dir}/ext/jdom.jar"/>
La target “files-init” permet aux scripts de savoir quels fichiers appartiennent au module. Ce sont les fichiers qui
seront effacés lorsque vous exécuterez la target “clean”.
Par convention, les JARS qui ne sont pas des modules vont dans le répertoire
ext/.
L'autre target que vous devez surcharger est
“netbeans-extra”. C'est un hook fournit par les scripts pour vous permettre de faire des choses comme la
copie de fichiers, dans le processus de déployement.
<target name="netbeans-extra" depends="init">
<mkdir dir="${netbeans.dest.dir}/${cluster.dir}/${nb.modules.dir}/ext"/>
<copy todir="${netbeans.dest.dir}/${cluster.dir}/${nb.modules.dir}/ext">
<fileset dir="lib">
<include name="jdom.jar"/>
</fileset>
</copy>
</target>
Cela copie le fichier jdom.jar file dans
/home/rich/netbeans/rssreader1/modules/ext.
manifest.mf: Chaque fichier JAR
a besoin d'un manifest. Un module NetBeans est simplement un fichier jar
dont le manifest contient au moins ces deux lignes:
OpenIDE-Module: org.jdom.api/1
OpenIDE-Module-Specification-Version: 1.0
La première ligne est juste le nom du module, et optionellement, une
release-version. Remarquez que ce nom correspond au <cnb>
(code-name-base) du fichier modules.xml.
La seconde ligne est la
specification-version du module.
Un mot sur les numéros de version... Un module peut avoir trois
numéros de version différents: un release-version, un
specification-version, et un implementation-version.
Disons, par exemple, que le module A a
release-version 1, specification version 2.0, et implementation-version beta3. Maintenant, module B déclare une dépendance sur le module A
(dans son fichier project.xml). Il doit
spécifier une version de release de 1. Il peut
, optionellement, spécifier une dépendance sur le numéro de spécification 2.0.
S'il le fait, et que l'auteur de A sort une version 2.1, c'est okay,
la dépendance continue à fonctionner. Le contrat est que les classes de l'API publique
exposées par le module A (voir l'élément <public-packages> du project.xml)
ne va pas casser la compatibilité. Une dépendance sur la version de spécification ne donne à
B que l'accès à ces classes d'API.
Si le module
B spécifie une dépendance sur implementation-version beta3, il ne fonctionnera
qu'avec cette version du module A. Cependant, il aura accès à toutes les classes
publiques de ce module A. (Si vous recevez une
NoClassDefFoundException lorsque vous exécutez votre module, cela peut être du au fait que vous essayez d'accéder
à des classes non-API sans spécifier une dépendance d'implémentation.)
Plus que deux lignes supplémentaires dans le manifest:
OpenIDE-Module-Localizing-Bundle: org/jdom/api/Bundle.properties
Class-Path: ext/jdom.jar
La première ligne est completement optionelle. Elle pointe vers un bundle
contenant d'autres entrées de manifest. Ces entrées sont toutes des
chaines de caractères traduisible, comme un nom d'affichage et une description pour ce module.
La seconde ligne est l'entrée standard d'un manifest Class-Path, qui rajoute le fichier
jdom.jar dans le classpath du fichier jar du module.
Remarquez que le chemin “ext/jdom.jar” correspond à l'emplacement où vous avez copié
jdom.jar dans le fichier build.xml.
Quirk #2: On ne devrait pas spécifier de Class-Path. Il
devrait être généré automatiquement (voir Quirk #1).
nbproject/project.xml: C'est le fichier qui dit au script
(et l'EDI si vous l'utilisez) comment générer des déclarations de dépendances et
classpath. Ici, aussi, vous devez spécifier les imformations familières
<code-name-base> et <path>.
Ensuite, vous devez spécifier toutes dépendances sur d'autres modules.
Maintenant, vu qu'il n'y a pas de code dans le module lui-même, et que
jdom.jar ne demande pas d'autre code en dehors des classes du JRE,
il n'y a pas de dépendance de modules à déclarer. Cependant, vous devez toujours
déclarer une dépendance sur les classes de OpenAPI:
<dependency>
<code-name-base>org.openide</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
<specification-version>4.5</specification-version>
</run-dependency>
</dependency>
C'est un cas spécial. Si vous ne spécifiez pas cela, avec une
specification-version récente, NetBeans va supposer qu'il s'agit d'un ancien module,
et va charger automatiquement tout un série de dépendance au runtime, à des fins de
backwards-compatibility.
Ensuite, vous devez déclarer le <public-packages> du module.
Cela sert à deux choses: public packages sont visibles par d'autres modules qui déclarent des dépendances
sur ce module, et ils constituent l'ensemble des packages qui veront san Javadoc générée lors de l'exécution de la
target ant “javadoc”.
Vous pouvez soit spécifier chaque package individuellement:
<public-packages>
<package>org.jdom</package>
<package>org.jdom.adapters</package>
<package>org.jdom.input</package>
...
</public-packages>
Ou, vous pouvez mettre toute l'arborescence sur une seule ligne:
<public-packages>
<subpackages>org.jdom</subpackages>
</public-packages>
Cependant, cette méthode ne fonctionne pas avec la targer “javadoc”.
project.properties: A few more hints to the build
process...
is.autoload=true
cp.extra=lib/jdom.jar
module.javadoc.packages=org.jdom
Les premières lignes indique que ce module est autoload. La
seconde ligne est rajoutée au classpath de compilation. La troisième ligne
est requise si vous utilisez méthode <subpackages>
pour déclarer vos <public-packages>
dans project.xml.
Quirk #3: Specifying module.javadoc.packages
should not be necessary here. The build scripts should have a sane
backoff, with the assumption that the user isn't interested in
javadocs for this module. Right now, the build fails if this is not
specified.
http://www.netbeans.org/issues/show_bug.cgi?id=52135
* Update: Fixed for NetBeans 4.1
Rome
Rome
La bilbiothèque Rome lit les
services RSS et Atom (avec une très simple API, devrais-je rajouter). Le module Rome et
le module JDOM differe en deux aspects. Le module Rome contient deux fichiers JAR
(rome-0.4.jar and rome-fetcher-0.4.jar) au lieu d'un seul,
et project.xml déclare une dépendance au module JDOM:
<dependency>
<code-name-base>org.jdom.api</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
<specification-version>1.0</specification-version>
</run-dependency>
</dependency>
JDIC
La bibliothèque JDIC permet
aux programmes Java de tirer avantage de certaines fonctionnalités natives du desktop
comme browsers, mailers, system trays, and MIME type registries.
FeedReader utilise un composant intégrant un browser natif pour rendre des pages
web dans une JFrame avec le moteur de rendu de IE ou Mozilla..
Pour accomplir cela, JDIC effectue des appels Java Native Interface
(JNI) à ces bibliothèques partagées (jdic.dll
ou libjdic.so), ainsi que des
exécutables natifs (IeEmbed.exe
ou mozembed-linux-gtk2). Vous
devez faire un peu de magie dans le fichier build du module pour rendre ces
bibliothèques et exécutables disponible à NetBeans au runtime.
Notez l'ensemble de fichiers déclaré dans la tâche “files-init”:
<include name="${nb.modules.dir}/ext/jdic.jar"/>
<include name="lib/libjdic.so"/>
<include name="lib/libmozembed-linux-gtk1.2.so"/>
<include name="lib/libmozembed-linux-gtk2.so"/>
<include name="lib/mozembed-linux-gtk1.2"/>
<include name="lib/mozembed-linux-gtk2"/>
<include name="lib/jdic.dll"/>
<include name="lib/IeEmbed.exe"/>
<include name="lib/nspr4.dll"/>
<include name="bin/${shell.script}"/>
<include name="bin/${batch.script}"/>
A coté de la déclaration familière de fichiers Jar, il y a une liste de bibliothèques natives et exécutables
dans lib/ et une paire de. La décision de mettre les éléments natifs
dans ce répertoire particulier est quelque peu arbitraire, bien que ce soit l'emplacement suggéré dans le
document d'architechture.
La seule autre chose qui a besoin de connaître cet emplacement est le script de lancement.
Le shell script et batch script sont générés par la target shellscript
du fichier build. Ces scripts lance la plateforme NetBeans avec le clusteur
rssreader1 et les binaires jdic dans leur propre chemin.
Module FeedReader
Maintenant que nous avons tous les modules de bibliothèques nécessaires, vous êtes prêts pour faire un
module qui fait vraiment quelque chose.
Commencez par créer les scripts et
éléments déclaratifs du module. Le fichierbuild.xml
est tout simplement vide, excepté la ligne d'import pour
le standard “projectized.xml”. Le fichier nbproject/project.xml
déclare simplement les dépendances sur vos 3 modules de bibliothèques, et n'a
pas de paquetages publics (ces modules ne fournissent pas d'API
pour d'autres modules).
Le fichier nbproject/project.properties
est également vide. Le fichier manifest.mf
devrait également vous sembler famillier. Il n'y a qu'un seul nouvel élément ici:
OpenIDE-Module-Layer: org/netbeans/modules/feedreader/resources/layer.xml
Contrairement aux modules de bibliothèques, vous devez déclarer des choses concernant
l'interface utilisateur du FeedReader dans un fichier layer, pour l'inclure dans le
“system filesystem.”
Définition: Le system filesystem est
où NetBeans stoque tous les paramètres systèmes, information GUI layout,
actions, modèles. Et tout le reste, qui est requis pour maintenir l'état
d'une installation NetBeans. Il comprend des fichiers bien réels situés sous
le répertoire "config" de chaque cluster, le répertoire "config" du répertoire
utilisateur de NetBeans, et les "fichiers" vituels déclarés dans les fichiers layer du
module.
Fichier Layer
La première entrée est situé juste en dessous du dossier principal appelé
Actions.
C'est un repository pour les implémentations des
javax.swing.Action.
Ils peuvent être utilisés pour les éléments de menu, les boutons des
barres d'outils et les raccourcis clavier. En ajoutant cette entrée, cela permettra
aux utilisateurs d'assigner des raccourcis clavier à l'action
ViewFeedsAction en sélectionnant “Tools ... Keyboard Shortcuts” depuis le menu.
La seconde entrée met l'action ViewFeedsAction dans le menu “View”.
Notez la syntaxe de fichier "fantôme", ce qui correspond à
un 'soft link' sous UNIX ou un raccourcis sous Windows..
Les autres entrées se trouvent sous le dossier principal Windows2.
Ce dossier fournit à la plateforme des informations quant aux types de
TopComponents
qui seront instanciés, et où les mettre. (Incidemment,
c'est appelé "Windows2" parce que "Windows" est l'ancien système de
fenêtre d'avant NetBeans 3.6, qui est gardé pour des raisons
de compatibilité.) FeedReader contient deux définitions de TopComponent:
SiteListComponent et EntryListComponent. Ce dernier doit être ouvert dans l'emplacement
par défaut (le centre), il n'est donc pas nécessaire de surcharger ce comportement dans
le fichier layer.
Le SiteListComponent,
cependant, sera situé sur le coté gauche, également appelé mode "explorer".
Donc, rajoutez une entrée:
<folder name="Modes">
<folder name="explorer">
<file name="rss_list.wstcref" url="feedList.wstcref"/>
</folder>
</folder>
Cela déclare qu'un TopComponent
avec id “rss_list” sera mis, par défaut, dans le mode “explorer”.
(Incidemment, l'extension “wstcref” est l'abbréviation de Window
System Top Component REFerence.) L'emplacement de “rss_list”
dans le mode déclaré est dérivé du fichier feedList.wstcref:
<tc-ref version="2.0">
<module name="org.netbeans.modules.feedreader/1" spec="1.0" />
<tc-id id="rss_list" />
<state opened="true" />
</tc-ref>
La valeur de <tc-id id>
doit correspondre avec le basename du fichier déclaré dans le layer
(“rss_list”). Le même nom doit être présent dans le dossier
Windows2/Components
avec une extension “.settings”:
<folder name="Components">
<file name="rss_list.settings" url="feedList.settings" />
</folder>
Le contenu du fichier de paramètres indique à NetBeans comment instancier une
“rss_list”. Vous pouvez écrire le contenu de ce fichier à la main,
ou vous pouvez enlever les commentaires de toute la section Windows2 de ce fichier
layer, démarrer NetBeans, instancier un SiteListComponent
en exécutant l'action ViewFeedsAction,
et saisir le fichier de paramètres qui fut autogénéré dans
$userdir/config/Windows2Local/Component.
Remplacer ensuite la section
<serialdata>
avec:
<instance class="org.netbeans.modules.feedreader.SiteListComponent"/>
Cela crée le composant en utilisant le constructeur par défaut de
SiteListComponent. Si la classe
requiert une méthode 'static factory', vous pouvez ajouter un attribut method:
<instance class="org.netbeans.modules.feedreader.SiteListComponent" method=”makeSiteListComponent”/>
ViewFeedsAction.java
Maintenant, tout ce qui reste à faire, c'est en fait le code Java. La première classe
déclarée dans le fichier layer est ViewFeedsAction.java.
C'est une simple sous-classe de CallableSystemAction,
qui est une implémentation singletion de javax.swing.Action.
public void performAction() {
SiteListComponent.activate();
}
L'exécution de cette action va ouvrir et donner le focus à l'instance singleton
de SiteListComponent (voir implémentation de SiteListComponent ci-dessous).
public String getName() {
return NbBundle.getMessage(SiteListComponent.class, "SLC_title");
}
Le nom de l'action est stoquée dans Bundle.properties, ce qui permet sa
traduction.
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
Vous devez changer ceci si vous avez un id JavaHelp ou une
URL avec une aide plus spécifique pour cette action.
protected boolean asynchronous() {
return false;
}
Voir l'entrée javadoc
pour plus de détails.
SiteListComponent.java
Quirk #4: Some of the more confusing code in this
class (the stuff dealing with IDs) has to do with ensuring a
singleton instance of SiteListComponent, with the id specified in the
layer file. Ideally, there should be a subclass of TopComponent
called something like SingletonTopComponent, as this seems like a
common thing to want to
do.
http://www.netbeans.org/issues/show_bug.cgi?id=53252
Le code suivant assure un singleton:
/** A hint to the window system for generating a unique id */
private static final String PREFERRED_ID = "rss_list"; // NOI18N
/** The actual id of the (singleton) instance */
private static String s_id = PREFERRED_ID;
...
public static synchronized SiteListComponent getInstance()
{
TopComponent c;
c = WindowManager.getDefault().findTopComponent(s_id);
if (c == null)
{
c = new SiteListComponent();
s_id = WindowManager.getDefault().findTopComponentID(c);
}
return (SiteListComponent)c;
}
...
protected String preferredID() {
return PREFERRED_ID;
}
NetBeans maintient une map de tous les TopComponents
chargés en mémoire. La clé de cette map est ce que j'appelle l'“ID”.
l'ID “preferred” est juste une allusion au système de fenêtre à utiliser
lors de la création d'une nouvelle instancel. Il n'y a aucune
garantie que cela fasse quelque chose. C'est généralement très utile
pour que vous puissiez reconnaître le nom du composant si vous êtes en train
de tracer les problèmes en dépouillant le contenu de
$userdir/config/Windows2Local.
Le champ static s_id,
cependant, représente l'ID d'une instance de
SiteListComponent en mémoire.
Par défaut, il vaut “rss_list” parce que c'est la valeur de l'ID donnée dans
feedList.wstcref. Aussi,
si c'est la première fois que l'utilisateur utilise ce module, le composant avec
l'ID “rss_list” sera instancié déclarativement. Cependant, si l'utilisateur ferme
ce composant et redémarre NetBeans, et ensuite réinvoque l'action
ViewFeedsAction,
le nouveau composant pourrait avoir un autre ID. C'est pourquoi la méthode
getInstance() method peut assigner une nouvelle valeur à s_id.
Si l'ID change, il n'y a rien dans le fichier
layer file pour indiquer que le nouveau composant apparaisse en mode
“explorer”. Pour forcer cela, surchargez la méthode open():
private static final String MODE = "explorer"; // NOI18N
public void open()
{
Mode m = WindowManager.getDefault().findMode(MODE);
m.dockInto(this);
super.open();
}
Le restant de cette classe n'est pas spécifique à NetBeans.
Le composant est consistuée d'une JList et de deux boutons: un pour
rajouter un nouveau fournisseur, et un pour supprimer un fournisseur sélectionné.
La liste est maintenue par un SiteListModel.
SiteListModel
La classe SiteListModel
ajouter uniquement la sérialisation au model Swing DefaultListModel.
A sa construction, il charge la liste des fournisseurs depuis le disque. Il
met à jout la liste sur disque à chaque édition de la liste.
(Overkill? Peut-être, mais cela ne sera jamais une très longue liste,
et une fois que l'utilisateur a établi la liste, les éditions ne seront pas
si fréquentes.)
Le plus important est de décider où sérialiser la liste.
En la mettant dans le system filesystem, vous pouvez faire en sorte que le fichier sérialisé
fasse partie de l'arborescence de fichier dans
$userdir/config:
private static final String DIR = "FeedReader"; //NOI18N
private static final String FILENAME = "feeds.ser"; //NOI18N
private FileObject getSerializedFile(boolean create) throws IOException
{
FileSystem sysFs = Repository.getDefault().getDefaultFileSystem();
FileObject dir = sysFs.findResource(DIR);
if (dir == null)
{
if (create)
dir = sysFs.getRoot().createFolder(DIR);
else
return null;
}
FileObject fo = dir.getFileObject(FILENAME);
if (fo == null)
{
if (create)
fo = dir.createData(FILENAME);
else
return null;
}
return fo;
}
Le paramètre booléen create
va déterminer si vous désirez créer le fichier s'il n'est pas déjà là
(true pour l'écriture, false pour la lecture).
Aussi, la liste sera sérialisée dans le fichier
$userdir/config/FeedReader/feeds.ser.
C'est une approche quelque peu bas niveau pour écrire la liste des
"fournisseurs". C'est, en fait, une API NetBeans qui s'en charge
pour vous. Vous pouvez implémenter la sérialisation en créant une sous-classe de
SystemOption, et faire un
override des méthodes readExternal() et
writeExternal(). J'ai choisi d'écrire
directement dans le système filesystem pour vous donner une meilleur idée de
ce qui se passe derière la scène. Aussi, l'implémentation vous donne
plus de flexibilité pour utiliser un format de fichier différent. Une fois que
vous avez le
FileObject retourné par
getSerializedFile(), vous pourriez
y écrire des données XML aussi facilement qu'en utilisant
ObjectOutputStream.
EntryListComponent
Cette classe définit un composant qui
qui s'ouvrira dans le mode centre, ou “éditeur”, et contient une liste
de nouvelles depuis un simple fournisseur.
public class EntryListComponent extends TopComponent
{
protected static final String PREFERRED_ID = "rss_entry_list"; //NOI18N
protected String preferredID() {
return PREFERRED_ID;
}
Il n'y a rien de funky avec les ID dans cette classe.
Ils sont juste là pour agir en tant que conseil pour le système de
fenêtrage.
public static TopComponent getInstance(Feed feed)
{
// look for an open instance containing this feed
Iterator opened = TopComponent.getRegistry().getOpened().iterator();
while (opened.hasNext())
{
Object tc = opened.next();
if (tc instanceof EntryListComponent)
{
EntryListComponent elc = (EntryListComponent)tc;
if (feed.equals(elc.m_feed))
{
elc.initData(feed);
return elc;
}
}
}
// none found, make a new one
return new EntryListComponent(feed);
}
La méthode getInstance() assure
que seule une instance existe pour chaque fournisseur unique. Ainsi, il peut
y avoir plusieurs onglets ouverts en même temps, mais seulement un pour
chaque URL de fournisseur.
protected EntryListComponent(Feed feed)
{
super();
initComponents();
if (feed != null)
initData(feed);
}
Marquer le constructeur protected, pour que les clients aient à utiliser getInstance().
protected void initData(Feed feed)
{
m_list.setFeed(feed);
setDisplayName(feed.toString());
m_feed = feed;
}
Donner le fournisseur à la JList,
et indiquer le nom d'affichage, qui sera utilisé sur l'onglet components,
et dans menus, etc.
public int getPersistenceType() {
return PERSISTENCE_NEVER;
}
Lorsque vous fermez NetBeans, ne passez pas votre temps à sérialiser ces
composants.
EntryList, BrowserFrame, Feed
Ces classes ne contiennent pas beaucoup de code spécifique à NetBeans.
Je laisse les commentaires en ligne parler d'eux-même.
Essayez-le!
Dans le répertoire nbbuild/,
exécutez ant. Ensuite exécutez
/home/rich/netbeans/rssreader1/bin/rss-reader.sh
(ou rss-reader.bat sous
Windows). Vous devriez vois une liste vide sur la gauche, auquel vous pouvez ajouter
des URLs de fournisseurs de nouvelles XML.

Un FeedReader en action! Il y a juste une chose ennuyante.
Il semble toujours que les capacités RSS sont liées à NetBeans. Il y a toujours
des boutons dans la barre d'outils et des entrées de menu qui ne sont pas
du tout utile pour la lecture de RSS.
Apposer Sa Marque
Le Module de Marque de FeedReader est un module assez simple.
Il n'y a aucun code Java. Juste un fichier layer qui cache
toute une série de barre d'outils et entrée menu inutilisée:
<folder name="Toolbars">
<folder name="File_hidden"/>
<folder name="Edit_hidden"/>
</folder>
<folder name="Menu">
<folder name="File">
<file name="org-openide-actions-SaveAction.instance_hidden"/>
<file name="org-openide-actions-SaveAllAction.instance_hidden"/>
<file name="org-netbeans-core-actions-RefreshAllFilesystemsAction.instance_hidden"/>
<file name="org-openide-actions-PageSetupAction.instance_hidden"/>
<file name="org-openide-actions-PrintAction.instance_hidden"/>
</folder>
<folder name="Edit_hidden"/>
<folder name="View">
<!-- hide the default web browser that doesn't render stuff very well -->
<file name="org-netbeans-core-actions-HTMLViewAction.instance_hidden"/>
</folder>
<folder name="Window">
<file name="org-netbeans-core-actions-GlobalPropertiesAction.instance_hidden"/>
</folder>
</folder>
La question que je suis sûr que vous vous posez est,
“Comment connait-il les noms de ces fichiers à cacher?”
Il n'est certainement pas évident que, pou cacher l'entrée du menu
“File ... Save”, vous avez besoin de spécifier
<filename=”org-openide-actions-SaveAction.instance_hidden”/>.
En fait, il y a deux façons. Vous pouvez rechercher dans le code source de NetBeans,
après les fichiers layer dans lequel c'était déclaré à l'origine.
Ou, vous pouvez utliser le Bean Browser, un outil inclut dans le module
“Open APIs Support” (org-netbeans-modules-apisupport) disponible par le
Centre de Mise à Jour de NetBeans. Cela crée un noeud dans la fenêtre
“Windows...Runtime” appelé “Bean Browser” qui vous permet de
naviguer parmis le système filesystem.
Petit Outil Très Utile.
A coté du fichier layer, il y a un autre aspet inhabituel au module
de marque. Dans le script Ant, vous verrez les parties maintenant familières
qui rajoutent des fichiers ressources à l'ensemble de fichiers déployés:
<!--
Identifie tous les fichiers à considérer comme faisant partie de ce module
lorsque déployé
-->
<target name="files-init" depends="basic-init">
<patternset id="module.files">
<include name="${module.jar}"/>
<include name="${javahelp.jar}" if="has.javahelp"/>
<include name="${nb.system.dir}/Modules/${code.name.base.dashes}.xml"/>
<!-- ajouts pour FeedReader commence ici -->
<include name="${nb.lib.dir}/locale/core_rss.jar"/>
<include name="${nb.modules.dir}/locale/org-netbeans-core-windows_rss.jar"/>
</patternset>
</target>
<!--
netbeans-extra est un "hook" fournit pour rajouter une copie des fichiers .
-->
<target name="netbeans-extra" depends="init">
<mkdir dir="${netbeans.dest.dir}/${cluster.dir}/${nb.lib.dir}/locale"/>
<mkdir dir="${netbeans.dest.dir}/${cluster.dir}/${nb.modules.dir}/locale"/>
<jar destfile="${netbeans.dest.dir}/${cluster.dir}/${nb.lib.dir}/locale/core_rss.jar"
basedir="core"/>
<jar destfile="${netbeans.dest.dir}/${cluster.dir}/${nb.modules.dir}/locale/org-netbeans-core-windows_rss.jar"
basedir="core-windows"/>
</target>
Dans ce cas, vous ajoutez deux fichiers Jar: core_rss.jar
et org-netbeans-core-windows_rss.jar.
Ces jars correspondent à core.jar
et org-netbeans-core-windows.jar.
En fait, tout Jar de l'installation NetBeans standard peut être
“branded” en plaçant un autre Jar relatif à l'original:
/path/to/original.jar
/path/to/locale/original_brandname.jar
Rappelez vous que le script shell dans le module JDIC passe
“–branding rss” comme paramètre. Le nom de la marque dans ce cas est donc “rss”.
En fait, tout fichier ressource peut avoit sa propre marque de fabrique.
Les icônes, bundles, fichiers layers, etc.
Dans core_rss.jar,
J'ai marqué l'écran d'accueil et un resource bundle, qui contient les clefs
qui affectent comment le splash screen est accueilli. Dans
org-netbeans-core-windows_rss.jar,
Je n'ai marqué qu'un seul fichier bundle, pour modifier le texte de la barre de titre
principale.
Réessayez!
Maintenant, vous devriez voir un écran semblable à la copie d'écran
au début de cet article. Et maintenant vous savez comment écrire un réelle application
pour la plateforme NetBeans.
Questions?
Veuillez diriger vos questions vers dev@openide.netbeans.org
(en anglais uniquement).
Voyez http://www.netbeans.org/community/lists/top.html
pour plus d'informations quant aux liste de mailing NetBeans.