blob: 8518b064eec4e8f4a159cfade8aa1c8703503218 [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= NetBeans Platform Quick Start
:jbake-type: platform_tutorial
:jbake-tags: tutorials
:jbake-status: published
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:icons: font
:experimental:
:description: NetBeans Platform Quick Start - Apache NetBeans
:keywords: Apache NetBeans Platform, Platform Tutorials, NetBeans Platform Quick Start
Welcome to the link:https://netbeans.apache.org/platform/[*NetBeans Platform*]!
The key benefit of the NetBeans Platform is its modular architecture. Secondary benefits are the NetBeans Platform's reliance on the Swing UI toolkit, which is the official toolkit for creating user interfaces in Java, in combination with the NetBeans IDE's award winning "Matisse" GUI Builder.
In this quick start, you are introduced to the benefit and usage of modularity via a very simple example, contributed by Thomas WΓΌrthinger, who is currently 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 link:https://netbeans.apache.org/kb/docs/platform.html[NetBeans Platform Learning Trail], providing a very rich variety of tutorials for many different scenarios relating to the NetBeans Platform.
If you are new to the NetBeans Platform, it is highly recommended to watch the screencast series link:https://netbeans.apache.org/tutorials/nbm-10-top-apis.html[Top 10 NetBeans APIs].
NOTE: 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.
== A Single Module NetBeans Platform Application
We start by simply creating a new NetBeans Platform application, containing a single module.
[start=1]
1. Choose File | New Project and then choose NetBeans Modules. Select "NetBeans Platform Application". You should see this:
image::images/quickstart-platform_wordapp01.png[]
Click Next.
[start=2]
1. Call your new application "WordApp" and make it the IDE's main project:
image::images/quickstart-platform_wordapp02.png[]
The IDE's main project is started when the global project command "Run Project" is invoked.
Click Finish.
[start=3]
1. Expand the new application in the Projects window, right-click the Modules node, and choose "Add New". Call the new module "WordEngine":
image::images/quickstart-platform_wordapp03.png[]
Click Next.
[start=4]
1. Specify a unique name for the module, which is its code name base, as well as a name that will be displayed in the Projects window:
image::images/quickstart-platform_wordapp04.png[]
Click Finish. The new module is created and its structure is shown in the Projects window.
[start=5]
1. Right-click the "WordEngine" module and choose New | Other. In the Module Development category, select "Window Component":
image::images/quickstart-platform_wordapp05.png[]
Click Next. You should now see this:
image::images/quickstart-platform_wordapp06.png[]
Click Next.
[start=6]
1. Set the class name prefix to "Text" and the package to "org.demo.wordengine":
image::images/quickstart-platform_wordapp07.png[]
Click Finish. The new window is added to the source structure of your module, together with some ancillary XML files.
[start=7]
1. 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:
image::images/quickstart-platform_wordapp15.png[]
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. Set the text of the button to "Filter!"
[start=8]
1. 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.
[source,java]
----
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
String s = text.getText();
s = s.toUpperCase();
text.setText(s);
}
----
[start=9]
1. Right-click the application and choose Run. Doing so will start up your new NetBeans Platform application and install your module. Your new application should look as follows:
image::images/quickstart-platform_wordapp08.png[]
[start=10]
1. Choose Window | Text, enter a text in the text area, and click "Filter!". You should see that the text is now shown in uppercase:
image::images/quickstart-platform_wordapp09.png[]
You have learned how to create a new NetBeans Platform application and how to add new modules to it.
== A Modular Application Using Lookup
We create two additional modules. The first new module defines a service, which the second module provides to the module we defined in the previous section.
[start=1]
1. Expand the new application in the Projects window, right-click the Modules node, and choose "Add New". Call the new module "TextFilter", with code name base "org.demo.textfilter", complete the wizard, which adds the module to your previously created application, as you did in the previous section.
[start=2]
1. Right-click the "TextFilter" module and choose New | Java Interface. Name the Java interface "TextFilter", in the package "org.demo.textfilter", and use the editor to define it as follows:
[source,java]
----
package org.demo.textfilter;
public interface TextFilter {
String process(String s);
}
----
[start=3]
1. Right-click the "TextFilter" module, choose Properties, and use the "API Versioning" tab so specify that the package containing the interface should be available throughout the application:
image::images/quickstart-platform_wordapp10.png[]
[start=4]
1. Create a third module in your application, name it "MyFilter", with "org.demo.myfilter" as the code name base.
[start=5]
1. Add a dependency in the Project Properties dialog of the newly created "MyFilter" module to the "TextFilter" module:
image::images/quickstart-platform_wordapp11.png[]
[start=6]
1. Because of the dependency you defined above, you can now implement the interface defined in the second module:
[source,java]
----
package org.demo.myfilter;
import org.demo.textfilter.TextFilter;
@ServiceProvider(service=TextFilter.class)
public class UpperCaseFilter implements TextFilter {
public String process(String s) {
return s.toUpperCase();
}
}
----
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. You need to set a dependency on the Utilities API module, which provides the ServiceProvider annotation.
[start=7]
1. The code that handles a click on the filter button now needs to be changed, so that an implementor of the interface "TextFilter" is located and loaded. When such an implementor is found, it is invoked to filter the text.
Before we can do this, we need to add a dependency in the Project Properties dialog of the "WordEngine" module to the "TextFilter" module:
image::images/quickstart-platform_wordapp12.png[]
Now, you can load implementations of the "TextFilter" class, as shown below:
[source,java]
----
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
String s = text.getText();
*TextFilter filter = Lookup.getDefault().lookup(TextFilter.class)*;
if (filter != null) {
s = filter.process(s);
}
text.setText(s);
}
----
The above could be done 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.
Now you are ready to run the code and check that everything works just as before. While the functionality is the same, the new modular design offers a clear separation between the graphical user interface and the implementation of the filter. The new application can also be extended quite easily, simply by adding new service providers to the application's classpath.
As an exercise, you could change the code, so that ALL found text filters (use the method "lookupAll") are applied consecutively on the text. For example, add a text filter implementation that removes all whitespaces and then test the resulting application.
== LookupListener and InstanceContent
We create a fourth module, which receives texts dynamically whenever we click the "Filter!" button in our first module.
[start=1]
1. In the first module, change the constructor of the "TextTopComponent" as follows:*private InstanceContent content;*
[source,java]
----
private TextTopComponent() {
initComponents();
setName(NbBundle.getMessage(TextTopComponent.class, "CTL_TextTopComponent"));
setToolTipText(NbBundle.getMessage(TextTopComponent.class, "HINT_TextTopComponent"));
// setIcon(Utilities.loadImage(ICON_PATH, true));
*content = new InstanceContent();
associateLookup(new AbstractLookup(content));*
}
----
[start=2]
1. Change the code of the filter button so that the old value is added to the ``InstanceContent`` object when the button is clicked.
[source,java]
----
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
String s = text.getText();
TextFilter filter = Lookup.getDefault().lookup(TextFilter.class);
if (filter != null) {
*content.add(s);*
s = filter.process(s);
}
text.setText(s);
}
----
[start=3]
1. Create a new module called "History" with code name base "com.demo.history".
[start=4]
1. In the History module, create a new window component with prefix "History", in the "com.demo.history" package. Specify that it should appear in the "editor" position. Once you have created the window, add a text area to it. Change the variable name of the text area to "historyText".
[start=5]
1. Add code to the constructor of the HistoryTopComponent class so that it listens to the lookup of the ``String`` class of the current active window. It displays all retrieved ``String`` objects in the text area:*private Lookup.Result result;*
[source,java]
----
private HistoryTopComponent() {
...
*result = org.openide.util.Utilities.actionsGlobalContext().lookupResult(String.class);
result.addLookupListener(new LookupListener() {
public void resultChanged(LookupEvent e) {
historyText.setText(result.allInstances().toString());
}
});*
}
----
[start=6]
1. Then you can start the application and experiment with it. The result should look similar to the one shown in the screenshot below:
image::images/quickstart-platform_wordapp13.png[]
As an exercise, you can change the type of the lookup result from ``String`` to ``Object`` and see what happens when you select different windows.
Congratulations! At this stage, with very little coding, you have created a small example of a modular application:
image::images/quickstart-platform_wordapp14.png[]
The application consists of 4 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.
Secondly, the ``Lookup`` 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 "WordEngine" module is able to display the service provided by the implementor. Loose coupling is provided to NetBeans Platform applications in this way.
To continue learning about modularity and the NetBeans Platform, head on to the 4-part "NetBeans Platform Selection Management" series, link:https://netbeans.apache.org/tutorials/nbm-selection-1.html[which starts here]. After that, get started with the link:https://netbeans.apache.org/kb/docs/platform.html[NetBeans Platform Learning Trail], 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 link:https://mail-archives.apache.org/mod_mbox/netbeans-dev/[is here].
Have fun with the NetBeans Platform and see you on the mailing list!