<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>NetBeans Platform Quick Start for NetBeans Platform 7.1</title> | |
<meta name="AUDIENCE" content="NBUSER"/> | |
<meta name="TYPE" content="ARTICLE"/> | |
<link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css"/> | |
<meta name="EXPIRES" content="N"/> | |
<meta name="developer" content="gwielenga@netbeans.org"/> | |
<meta name="indexed" content="y"/> | |
<meta name="description" | |
content="A short guide to getting started with the NetBeans Platform."/> | |
<!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. --> | |
<!-- Use is subject to license terms.--> | |
</head> | |
<body> | |
<h1>NetBeans Platform 7.1 Quick Start</h1> | |
<p>Welcome to the <a href="https://platform.netbeans.org/"><b>NetBeans Platform</b></a>!</p> | |
<p>The NetBeans Platform is a generic application framework primarily for Java desktop applications. | |
The main benefit of the NetBeans Platform is its predefined modular architecture. Secondary | |
benefits are the NetBeans Platform's reusable solutions such as its | |
docking framework and its out-of-the-box pluggable components, in combination with the tools | |
provided by its SDK, NetBeans IDE, in particular | |
its award winning "Matisse" GUI Builder for designing GUI components.</p> | |
<p>In this quick start, you are introduced to the benefits and usages of modularity | |
on the Java desktop via | |
a very simple example, contributed by Thomas Würthinger, a | |
PhD student at the Johannes Kepler University in Linz, Austria. Once you have | |
grasped the concepts introduced in this quick start, you will be ready to | |
step onto the <a href="https://netbeans.org/kb/trails/platform.html">NetBeans Platform Learning Trail</a>, | |
providing a very rich variety of tutorials for many different scenarios | |
relating to the NetBeans Platform.</p> | |
<p class="tips"> If you are new to the NetBeans Platform, it is highly | |
recommended to watch the screencast series <a href="https://platform.netbeans.org/tutorials/nbm-10-top-apis.html">Top 10 NetBeans APIs</a>.</p> | |
<p><strong class="notes">Note: </strong>This document uses NetBeans Platform 7.1 and | |
NetBeans IDE 7.1. If you | |
are using an earlier version, see <a href="../70/nbm-quick-start.html">the previous version | |
of this document</a>.</p> | |
<p><b>Contents</b></p> | |
<p><img src="../../images/articles/71/netbeans-stamp.png" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 7.1" title="Content on this page applies to NetBeans IDE 7.1"/></p> | |
<ul class="toc"> | |
<li>Part 1: <a href="#single">A Single Module NetBeans Platform Application</a></li> | |
<li>Part 2: <a href="#lookup">A Modular Application Using Lookup</a></li> | |
<li>Part 3: <a href="#listener">LookupListener and InstanceContent</a></li> | |
</ul> | |
<p><b>To follow this tutorial, you need the software and resources listed in the following | |
table.</b></p> | |
<table> | |
<tbody> | |
<tr> | |
<th class="tblheader" scope="col">Software or Resource</th> | |
<th class="tblheader" scope="col">Version Required</th> | |
</tr> | |
<tr> | |
<td class="tbltd1"><a href="https://netbeans.org/downloads/index.html">NetBeans IDE</a></td> | |
<td class="tbltd1">version 7.1 or above</td> | |
</tr> | |
<tr> | |
<td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">Java Developer Kit (JDK)</a></td> | |
<td class="tbltd1">version 6 or above</td> | |
</tr> | |
</tbody> | |
</table> | |
<p></p> | |
<p><b class="notes">Note:</b> Even though it is a separate product, there is no | |
need to download the NetBeans Platform separately. Typically, you develop the | |
application in NetBeans IDE and then exclude the modules that are specific to | |
the IDE but that are superfluous to your application.</p> | |
<p class="tips"> Do some background reading before diving into | |
this tutorial. In particular, read the <a href="http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html">Modules API Reference</a> document, | |
which explains what modules are and provides some | |
context for this tutorial, while noting that there is an extensive Reference Material section | |
on the <a href="https://netbeans.org/kb/trails/platform.html">NetBeans Platform Learning Trail</a>.</p> | |
<!-- ===================================================================================== --> | |
<h2 class="tutorial"><a name="single"></a>A Single Module NetBeans Platform Application</h2> | |
<p>We start by creating a new NetBeans Platform application, | |
containing a single module.</p> | |
<ol> | |
<li><p>Choose File | New Project and then choose NetBeans Modules. | |
Select "NetBeans Platform Application". You should see this:</p> | |
<p><img src="../../images/tutorials/quickstart-platform/71/wordapp01.png" alt="Fig 1"/></p> | |
<p>Click Next.</p></li> | |
<li><p>Name your new application "WordApp" and specify a folder on disk | |
for storing it:</p> | |
<p><img src="../../images/tutorials/quickstart-platform/71/wordapp02.png" alt="Fig 2"/></p> | |
<p>Click Finish. The new project appears as follows in the Projects window:</p> | |
<p><img src="../../images/tutorials/quickstart-platform/71/wordapp021.png" alt="Fig 2-1"/></p></li> | |
<li><p>Right-click the "Modules" node, shown in the screenshot above, | |
and choose "Add New". Name the new | |
module "WordEditorCore":</p> | |
<p><img alt="create new module" src="../../images/tutorials/quickstart-platform/71/wordapp03.png"/></p> | |
<p>Click Next.</p></li> | |
<li><p>Specify "org.word.editor.core" as the unique string identifying the module, which is its code name | |
base, together with a project display name that will be shown in the Projects window.</p> | |
<p><img alt="create new module" src="../../images/tutorials/quickstart-platform/71/wordapp031.png"/></p> | |
<p>Click Finish. The new module is created and its structure is | |
shown in the Projects window:</p> | |
<p><img alt="specify a name" src="../../images/tutorials/quickstart-platform/71/wordapp04a.png"/></p></li> | |
<li><p>Right-click the "WordEditorCore" module and choose New | Other. | |
In the Module Development category, select "Window":</p> | |
<p><img alt="create new window" src="../../images/tutorials/quickstart-platform/71/wordapp04b.png"/></p> | |
<p>Click Next. You should now see a dialog for specifying the position where the new window | |
will appear in the application frame, as well as whether it will open automatically when the application | |
starts, among other settings:</p> | |
<p><img alt="create new window" src="../../images/tutorials/quickstart-platform/71/wordapp04c.png"/></p></li> | |
<li><p>In the wizard step above, select "editor", which is the default central | |
position within the application frame, and "Open on Application Start". Then click Next.</p></li> | |
<li><p>Set the class name prefix to "Text" and the package to "org.word.editor.core":</p> | |
<p><img alt="set definitions" src="../../images/tutorials/quickstart-platform/71/wordapp04d.png"/></p> | |
<p>Click Finish. The new window is added to the source structure | |
of your module:</p> | |
<p><img alt="set definitions" src="../../images/tutorials/quickstart-platform/71/wordapp08.png"/></p></li> | |
<li><p>Now double click on the file "TextTopComponent.java" to open | |
it in the Design view of the "Matisse" GUI Builder. Use the Palette (Ctrl-Shift-8) to | |
drag and drop a button and a text area onto the window:</p> | |
<p><img alt="palette" style="border: 1px solid black;" src="../../images/tutorials/quickstart-platform/71/wordapp15.png"/></p> | |
<p>Do the following to make the new GUI components meaningful:</p> | |
<ul> | |
<li>Right-click the text area, choose "Change Variable Name", and then | |
name it "text". That is the name that will enable you to access the | |
component from your code.</li> | |
<li>Right-click the button, choose "Edit Text", and then set the text of the button to "Filter!"</li> | |
</ul></li> | |
<li>Double click on the button, causing an event handling method | |
to automatically be created in the Source editor. The method is called whenever the button is clicked. Change the body | |
of the method to the following code. | |
<pre class="examplecode">private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { | |
String s = text.getText(); | |
s = s.toUpperCase(); | |
text.setText(s); | |
}</pre></li> | |
<li><p>Right-click the application and choose Run. Doing so will start up your | |
new NetBeans Platform application and install your module. You will have a new | |
window, as well as a new menu item for opening it, | |
as shown below:</p> | |
<p><img alt="show new app" src="../../images/tutorials/quickstart-platform/71/wordapp081.png"/></p></li> | |
<li><p>Enter a text in lowercase in the text area, and click "Filter!". | |
You should see that the text is now shown | |
in uppercase:</p> | |
<p><img alt="uppercase" src="../../images/tutorials/quickstart-platform/71/wordapp082.png"/></p></li> | |
</ol> | |
<p>You have learned how to create a new NetBeans Platform application and how | |
to add new modules to it. In the next section, you will be introduced | |
to the NetBeans Platform's pluggable service infrastructure.</p> | |
<!-- ===================================================================================== --> | |
<h2 class="tutorial"><a name="lookup"></a>A Modular Application Using Lookup</h2> | |
<p>In this section, you create two additional modules. The first | |
new module, "TextFilterAPI", | |
contains a service provider interface. The second module, | |
"UppercaseFilter", is a service provider for the interface.</p> | |
<p> The GUI module, which you created in the previous section, will be loosely coupled from | |
the "UppercaseFilter" service provider because the GUI module | |
will not refer to any code from the "UppercaseFilter" service provider. | |
That will be possible because the "UppercaseFilter" | |
service provider will be registered in the META-INF/services folder | |
and loaded via the NetBeans Lookup class, which is comparable to | |
the JDK 6 ServiceLoader class.</p><p> You will then | |
create another loosely coupled service provider, | |
named "LowercaseFilter".</p> | |
<ol> | |
<li><p>Expand the new application in the Projects window, | |
right-click the Modules node, and choose "Add New". Name the new | |
module "TextFilterAPI":</p> | |
<p><img alt="uppercase" src="../../images/tutorials/quickstart-platform/71/wordapp083.png"/></p> | |
<p>Click Next. Use code name base "org.word.editor.api" and | |
complete the wizard, which adds the module to your previously created | |
application, as you did in the previous section:</p> | |
<p><img alt="uppercase" src="../../images/tutorials/quickstart-platform/71/wordapp084.png"/></p></li> | |
<li><p>Right-click the "TextFilterAPI" module and choose | |
New | Java Interface. Name the Java interface "TextFilter", | |
in the package "org.word.editor.api", and use the editor | |
to define it as follows:</p> | |
<pre class="examplecode">package org.word.editor.api; | |
public interface TextFilter { | |
String process(String s); | |
}</pre></li> | |
<li><p>Right-click the "TextFilterAPI" module, | |
choose Properties, and use the "API Versioning" | |
tab to specify that the package containing the | |
interface should be available throughout the | |
application:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp085.png"/></p> | |
<p>Click OK.</p> | |
<p class="tips">In the Projects window, expand "Important Files" in the | |
"TextFilterAPI" project and then | |
double-click "Project Metadata". The "project.xml" file | |
opens and you should see that the package has now been declared | |
public:</p> | |
<pre class="examplecode"><?xml version="1.0" encoding="UTF-8"?> | |
<project xmlns="https://netbeans.org/ns/project/1"> | |
<type>org.netbeans.modules.apisupport.project</type> | |
<configuration> | |
<data xmlns="https://netbeans.org/ns/nb-module-project/3"> | |
<code-name-base>org.word.editor.api</code-name-base> | |
<suite-component/> | |
<module-dependencies/> | |
<b><public-packages> | |
<package>org.word.editor.api</package> | |
</public-packages></b> | |
</data> | |
</configuration> | |
</project></pre> | |
</li> | |
<li><p>Repeat step 1 in this section, creating a third module in your application, | |
name it "UppercaseFilter":</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp11.png"/></p> | |
<p>Click Next. Use "org.word.editor.uppercase" as the | |
code name base:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp12.png"/></p> | |
<p>Click Finish.</p> | |
</li> | |
<li><p>Right-click the "UppercaseFilter" module, | |
choose Properties, and use the "Libraries" | |
tab to add a dependency on the "TextFilterAPI" module:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp13.png"/></p> | |
<p>Click OK.</p> | |
<p class="tips">In the Projects window, expand "Important Files" in the "UppercaseFilter" project, | |
and then | |
double-click "Project Metadata". The "project.xml" file | |
opens and you should see that a new dependency has been declared:</p> | |
<pre class="examplecode"><?xml version="1.0" encoding="UTF-8"?> | |
<project xmlns="https://netbeans.org/ns/project/1"> | |
<type>org.netbeans.modules.apisupport.project</type> | |
<configuration> | |
<data xmlns="https://netbeans.org/ns/nb-module-project/3"> | |
<code-name-base>org.word.editor.uppercase</code-name-base> | |
<suite-component/> | |
<b><module-dependencies> | |
<dependency> | |
<code-name-base>org.word.editor.api</code-name-base> | |
<build-prerequisite/> | |
<compile-dependency/> | |
<run-dependency> | |
<specification-version>1.0</specification-version> | |
</run-dependency> | |
</dependency> | |
</module-dependencies></b> | |
<public-packages/> | |
</data> | |
</configuration> | |
</project></pre> | |
<p class="notes">In the same way as shown above, | |
set a dependency on the Lookup API module, which provides the | |
@ServiceProvider annotation that you will use in the next step.</p></li> | |
<li>Because of the Lookup API dependency you defined above, | |
you can now implement the | |
interface defined in the TextFilterAPI module. Do so in the "UppercaseFilter" module, by creating | |
a new class named "UppercaseFilter", in the "org.word.editor.uppercase" package, | |
as shown below: | |
<pre class="examplecode">package org.word.editor.uppercase; | |
import org.openide.util.lookup.ServiceProvider; | |
import org.word.editor.api.TextFilter; | |
@ServiceProvider(service=TextFilter.class) | |
public class UppercaseFilter implements TextFilter { | |
@Override | |
public String process(String s) { | |
return s.toUpperCase(); | |
} | |
}</pre> | |
<p class="tips"> At compile time, the @ServiceProvider annotation will create a META-INF/services | |
folder with a file that registers your implementation of the | |
TextFilter interface, following the JDK 6 ServiceLoader mechanism.</p></li> | |
<li><p>The code that handles a click on the | |
filter button now needs to be changed, so that an | |
implementation of the interface "TextFilter" is located | |
and loaded. | |
When such an implementation is found, it is invoked to | |
filter the text.</p> | |
<p>Before we can do this, we need to | |
add a dependency in the Project Properties dialog of | |
the "WordEditorCore" module to the "TextFilterAPI" module:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp12a.png"/></p> | |
<p>Now, you can load implementations of the | |
"TextFilter" class, as shown below:</p> | |
<pre class="examplecode">private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { | |
String enteredText = text.getText(); | |
<b>Collection<? extends TextFilter> allFilters = Lookup.getDefault().lookupAll(TextFilter.class);</b> | |
StringBuilder sb = new StringBuilder(); | |
for (TextFilter textFilter : allFilters) { | |
String processedText = textFilter.process(enteredText); | |
sb.append(processedText).append("\n"); | |
} | |
text.setText(sb.toString()); | |
}</pre> | |
<p class="tips"> The above could be achieved via the | |
JDK 6 "ServiceLoader" class, except that the | |
"Lookup" class can be used in JDK's prior | |
to JDK 6. Aside from that, the "Lookup" class | |
has a number of additional features, as the | |
next section will illustrate.</p> | |
</li> | |
<li><p>Now you can run the application again and check | |
that everything works just as before. While the functionality | |
is the same, the new modular design offers a clear separation | |
between the GUI and the implementation | |
of the filter:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp12b.png"/></p> | |
<p>The new application can also be extended | |
quite easily, by adding new service providers to | |
the application's classpath.</p> | |
<p class="tips">As an exercise, add a new module that provides a "LowercaseFilter" | |
implementation of the API to the application.</p></li> | |
</ol> | |
<p>You have now used the default Lookup, that is, "Lookup.getDefault()", to load | |
implementations of an interface from the META-INF/services folder.</p> | |
<!-- ===================================================================================== --> | |
<h2 class="tutorial"><a name="listener"></a>LookupListener and InstanceContent</h2> | |
<p>In this section, we create a fourth module, which receives texts | |
dynamically whenever we click the "Filter!" button | |
in our first module.</p> | |
<ol> | |
<li>In the "WordEditorCore" module, we will publish a String whenever | |
the user clicks the "Filter!" button. To do so, change the | |
constructor of the "TextTopComponent" as follows: | |
<pre class="examplecode"><b>private InstanceContent content;</b> | |
private TextTopComponent() { | |
initComponents(); | |
setName(NbBundle.getMessage(TextTopComponent.class, "CTL_TextTopComponent")); | |
setToolTipText(NbBundle.getMessage(TextTopComponent.class, "HINT_TextTopComponent")); | |
// setIcon(Utilities.loadImage(ICON_PATH, true)); | |
<b>content = new InstanceContent(); | |
associateLookup(new AbstractLookup(content));</b> | |
}</pre><p>Change the code of the filter | |
button so that the entered text is added to | |
the <tt>InstanceContent</tt> object when the button is clicked.</p> | |
<pre class="examplecode">private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { | |
String enteredText = text.getText(); | |
Collection<? extends TextFilter> allFilters = Lookup.getDefault().lookupAll(TextFilter.class); | |
StringBuilder sb = new StringBuilder(); | |
for (TextFilter textFilter : allFilters) { | |
String processedText = textFilter.process(enteredText); | |
sb.append(processedText).append("\n"); | |
<b>content.add(enteredText);</b> | |
} | |
text.setText(sb.toString()); | |
}</pre> | |
</li> | |
<li><p>In the same way as done in the previous sections, | |
create another module in your application and name it "WordHistory". | |
Use code name base "org.word.editor.history".</p> | |
</li> | |
<li><p>In the WordHistory module, right-click the "org.word.editor.history" package | |
and choose New | Window. Use the New Window wizard to create a | |
new window component that will automatically be opened on the left side of the | |
application frame, which is the "explorer" position:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp17.png"/></p> | |
<p>Click Next. Use prefix "History" and specify that the new window will be stored | |
in the "org.word.editor.history" package:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp17a.png"/></p> | |
<p>Click Finish.</p> | |
</li> | |
<li><p>Once | |
you have created the window, add a | |
<tt>JTextArea</tt> to it:</p> | |
<p><img alt="" style="border:1px solid black" src="../../images/tutorials/quickstart-platform/71/wordapp17b.png"/></p> | |
<p>Change the variable | |
name of the text area to "historyText".</p> | |
</li> | |
<li>In the Source view, add code to the constructor of the HistoryTopComponent class so | |
that it listens to the lookup of the <tt>String</tt> class | |
of the current active window. It displays all retrieved | |
<tt>String</tt> objects in the text area: | |
<pre class="examplecode">... | |
... | |
... | |
public final class HistoryTopComponent extends TopComponent <b>implements LookupListener</b> { | |
<b>private org.openide.util.Lookup.Result<String> result;</b> | |
... | |
... | |
... | |
<b>@Override | |
public void componentOpened() { | |
result = org.openide.util.Utilities.actionsGlobalContext().lookupResult(String.class); | |
result.addLookupListener(this); | |
} | |
@Override | |
public void componentClosed() { | |
result.removeLookupListener(this); | |
} | |
@Override | |
public void resultChanged(LookupEvent le) { | |
Collection<? extends String> allStrings = result.allInstances(); | |
StringBuilder sb = new StringBuilder(); | |
for (String string : allStrings) { | |
sb.append(string).append("\n"); | |
} | |
historyText.setText(sb.toString()); | |
}</b> | |
... | |
... | |
... | |
</pre></li> | |
<li><p>Then you can start the application and | |
experiment with it. The result should look similar | |
to that shown in the screenshot below:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp19.png"/></p> | |
<p class="tips">As an exercise, redesign the user interface of the "TextTopComponent" | |
in such a way that a <tt>JList</tt> displays the filters, as shown below:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp22.png"/></p> | |
<p>The "Filter!" button should use the currently selected filter | |
to process the text in the <tt>JTextField</tt>.</p> | |
</li> | |
</ol> | |
<p>Congratulations! At this stage, with very little coding, | |
you have created a small example of a loosely-coupled modular | |
application:</p> | |
<p><img alt="" src="../../images/tutorials/quickstart-platform/71/wordapp20.png"/></p> | |
<p>The application consists of four modules. Code from one module can only | |
be used by another module if (1) the first module explicitly exposes | |
packages and (2) the second module sets a dependency on the first module. | |
In this way, the NetBeans Platform helps to organize your code in a | |
strict modular architecture, ensuring that code isn't reused randomly | |
but only when there are contracts set between the modules that provide | |
the code.</p> | |
<p>Secondly, the <tt>Lookup</tt> class has been introduced as | |
a mechanism for communicating between modules, as an extension of the | |
JDK 6 ServiceLoader approach. Implementations are loaded via their | |
interfaces. Without using any code from an implementation, the "WordEditorCore" | |
module is able to display the service provided by the implementor. Loose | |
coupling is provided to NetBeans Platform applications in this way.</p> | |
<p class="tips"> To continue learning about modularity | |
and the NetBeans Platform, head on to | |
the four-part "NetBeans Platform Selection | |
Management" series, <a href="https://platform.netbeans.org/tutorials/nbm-selection-1.html">which starts here</a>. | |
After that, get started with the <a href="https://netbeans.org/kb/trails/platform.html">NetBeans Platform Learning Trail</a>, choosing | |
the tutorials that are most relevant to your particular business scenario. Also, | |
whenever you have questions about the NetBeans Platform, of any kind, feel free | |
to write to the mailing list, dev@platform.netbeans.org; its related | |
archive <a href="https://netbeans.org/projects/platform/lists/dev/archive">is here</a>.</p> | |
<p>Have fun with the NetBeans Platform and see you on the mailing list!</p> | |
</body> | |
</html> |