31.7.10

Project-Based Customer Application

Monday December 14, 2009

Project-Based Customer Application
A very interesting idea came up in the comments to my recent article How to Create a Swing CRUD Application on NetBeans Platform 6.8. Someone, essentially, suggested the idea of creating a tutorial that lets the user create new projects based on customers, with this type of structure:

Customers
- Geertjan
---Orders
-----+Raw-12092009
-----+Raw-11091009
---Balance
---Reports

So, today, on my disk I created a folder structure like that:

And now I'm able to open those structures into a new NetBeans Platform application:

Just about the best thing about the screenshot above is that each node (i.e., "Balance", "Orders", "Reports") can be provided externally, by a plugin:

So, if your folder/file structure for your customers changes... you can simply exclude the plugin for a folder that is removed or include a new one, custom made for a new folder in your folder/file structure. Now that's really powerful. Here, for example, you see that I've disabled the plugin that provides support for the Reports folder and, as a result, my projects do not have a Reports folder anymore:

Project templates will be added for creating these customer projects (since, right now, I'm simply opening the projects from the Favorites window):

...as well as some simple editors for visualizing the XML in the files you see above, probably using the XML MultiView API.

Then I'll create a new tutorial entitled "How to Create a Project-Based Customer Application on NetBeans Platform 6.8".

And here's the related source code: http://kenai.com/projects/customercreator

In other news. Wondering if Groovy is for you? As a NetBeans Platform developer, the answer can only be "Yes, definitely!" and you can even continue coding in Java. I.e., for a hybrid Java/Groovy application (where Groovy is simply an implementation detail of Java), see How to Create a Web Service Client with Groovy and NetBeans Platform 6.8.

 

http://blogs.sun.com/geertjan/entry/project_based_customer_application

30.7.10

NetBeans Platform: Combobox in property editor

http://emilian-bold.blogspot.com/2007/01/netbeans-platform-combobox-in-property.html

Properties Window – NetBeans Platform

Nodes API

  • The Nodes API is the third and uppermost layer in the NetBeans Resource Management System.
  • The role of the Nodes API
    • is visual representation of data
  • Connection
    • Closely connected to this API is the Explorer & Property Sheet API
      • Which is the container and manager of nodes.
  • Usage
    • To present data to the user interface of an application
    • to give the user actions, functionality, and properties for interacting with underlying data.
  • Other usage
    • Need not merely present data
      • It can be used for many other things as well.
        • Examples:
          • An action hiding beneath a node could be invoked when the node is double-click.
  • Nodes and Business Logic
    • A node is not typically concerned with business logic
    • Bug focuses on providing a presentation layer, delegating user interaction to action classes and, where applicable, to its related DataObject.
  • Hierarchy of Node subclasses

image

  • AbstractNode
    • Provides the simplest form of the Node class.
    • Use this class to instance a new Node directly, without needing to implement or extend any of the Node classes.
  • FilterNode
    • Creates a proxy Node that delegates its method calls to the original Node.
    • This kind of Node is used when data needs to be displayed in different ways.
  • BeanNode
    • is used to represent a JavaBean.
  • IndexedNode
    • lets its children be organized based on a given index.
  • DataNode
    • Is most commonly used when representing data from files.
    • Is the Node type representing DataObject such as those you learned about in the previous sections.

Node Container

  • Each Node object has its own Children object, providing a container for child nodes, which are the nodes'’s subnodes.
  • The Children object is responsible for the creation, addition, and structuring of child notes.
  • Each node within the Children object has the Node that owns the Children object as its parent.
  • For nodes that do not have their own children, such as our DataNode for MP3 files, we pass in Children.LEAF as an empty container.

Different Children container class variations and their uses

  • Children.Array
    • Superclass for all other Children classes.
    • You should not derive from this class directly.
    • This container class messages its nodes in an Array.
    • The nodes wil be appended at the end of the array and will be delivered in the same order.
  • Children.Keys<T>
    • Typical superclass for your implementation.
    • Nodes are connected with a key.
    • These keys are also used for ordering.
  • Children.Map<T>
    • The nodes are stored in a Map.
    • The nodes are associated with a key.
      • which is also used for deleting nodes
  • Children.SortedArray
    • Extends the Children.Array class with a Comparator
  • Children.SortedMap<T>
    • Extends the Children.Map<T> class with a Comparator.

Actions

  • A node makes a context menu available to the user,
    • allowing context-sensitive actions.
  • A DataNode obtains its context menu’s actions from the DataLoader of the DataObject it represents.
  • A DataLoader defines a MIME-specific folder in the layer file via the method actionContex(),
    • where action are registered.
  • These are read and added automatically to the context menu.
  • For nodes that do not represent DataObject(s)
    • The getActions() method in the Node is used to define the actions in the context menu.
    • Override this method in your class to add

Event Handling

  • To react to Node events, use a PropertyChangeListener, as well as a NodeListener.
    • PropertyChangeListener
      • to be informed of changes to Node properties provided via the getPropertySet() method.
    • NodeListener
      • Can listen to internal node changes, such as changes to the name, the parent node, and the child nodes.
      • The Node class makes a range of property keys available, such as PROP_NAME and PROG_LEAF
  • NodeListener
    • childrenAdded(NodeMemberEvent evt)
    • childrenRemoved(NodeMemberEvent evt)
    • childrenReordered(NodeMemberEvent evt)
    • nodeDestroyed(NodeEvent evt)
    • propertyChange(PropertyChangeEvent evt)
  • NodeAdapter

Implementing Nodes and Children

Collections.list()

Shame on myself!

29.7.10

Open a Windows Folder on Windows Using Java

try {
String[] cmd = new String[5];

String url = "D:\\Program Files\\download";
cmd[0] = "cmd";
cmd[1] = "/c";
cmd[2] = "start";
cmd[3] = " ";
cmd[4] = url;

Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}

28.7.10

Double-click on JTree

Excerpted from JTree API document

If you are interested in detecting either double-click events or when a user clicks on a node, regardless of whether or not it was selected, we recommend you do the following:

 final JTree tree = ...;

MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int selRow = tree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if(selRow != -1) {
if(e.getClickCount() == 1) {
mySingleClick(selRow, selPath);
}
else if(e.getClickCount() == 2) {
myDoubleClick(selRow, selPath);
}
}
}
};
tree.addMouseListener(ml);

27.7.10

Using Lookup & ServiceProvider Mechanism in NetBeans Platform

http://platform.netbeans.org/tutorials/nbm-quick-start.html

Part of the tutorial, only focus on Lookup & serviceProvider

 


  1. Create a third module in your application, name it "MyFilter", with "org.demo.myfilter" as the code name base.
  2. Add a dependency in the Project Properties dialog of the newly created "MyFilter" module to the "TextFilter" module:

  3. In the same way as shown in the previous step, set a dependency on the Lookup API module, which provides the ServiceProvider annotation that you will use in the next step.
  4. Because of the dependency you defined above, you can now implement the interface defined in the second module:
    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.


  5. 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:


    Now, you can load implementations of the "TextFilter" class, as shown below:

    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.

Creating Extensible Applications With the Java Platform

By John O'Conner, September 2007

An extensible application is one that you can extend easily without modifying its original code base. You can enhance its functionality with new plug-ins or modules. Developers, software vendors, and even customers can add new functionality or application programming interfaces (APIs) by simply adding a new Java Archive (JAR) file onto the application classpath or into an application-specific extension directory.

This article describes two ways to create applications with extensible services, which allow you or others to provide service implementations that require no modifications to the original application. By designing an extensible application, you provide an easy way to upgrade or enhance specific parts of a product without changing the core application.

One example of an extensible application is a word processor that allows the end user to add a new dictionary or spelling checker. In this example, the word processor provides a dictionary or spelling feature that other developers, or even customers, can extend by providing their own implementation of the feature. Another example is the NetBeans IDE, which in many cases allows users to add editors and other modules without restarting the application.

Definitions

A service is a set of programming interfaces and classes that provide access to some specific application functionality or feature. The service may simply define the interfaces for the functionality and a way to retrieve an implementation. In the word-processor example, a dictionary service can define a way to retrieve a dictionary and the definition of a word, but it does not implement the underlying feature set. Instead, it relies on a service provider to implement that functionality.

A service provider interface (SPI) is the set of public interfaces and abstract classes that a service defines. The SPI defines the classes and methods available to your application.

A service provider implements the SPI. An application with extensible services will allow you, vendors, and perhaps even customers to add service providers without modifying the original application.

Dictionary Service Example

Consider how you might design a dictionary service in a word processor or editor. One way would be to define a DictionaryService class and a Dictionary interface. TheDictionaryService provides a singleton DictionaryService object that can retrieve definitions of words from Dictionary providers. Dictionary service clients -- your application code -- will retrieve an instance of this service, and the service will search, instantiate, and use Dictionary service providers. The underlined attributes and methods in Figure 1 are static.

Class diagram for the Dictionary

Figure 1. Class diagram for the Dictionary service.
Click here for a larger image

All Dictionary providers must register their presence with the service. Otherwise, the service will not know how to find them. Developers can register interfaces in a variety of ways, but one of the most common ways is to simply use your application's classpath. Services can examine the classpath to find interface implementations. In this case, the DictionaryServicecan examine the application's classpath to find one or more Dictionary interface providers.

Although the word-processor developer would most likely provide a basic, general dictionary with the original product, the customer might require a specialized dictionary, perhaps containing legal or technical terms. Ideally, the customer would be able to create or purchase new dictionaries and add them to the existing application.

The ServiceLoader Class

The Java SE 6 platform provides a new API that helps you find, load, and use service providers. The java.util.ServiceLoader class has been quietly performing its job in the Java platform since the 1.3 version, but it has become a public API in Java SE 6.

The ServiceLoader class searches for service providers on your application's classpath or in your runtime environment's extensions directory. It loads them and allows your application to use the provider's APIs. If you add new providers to the classpath or runtime extension directory, the ServiceLoader class will find them. If your application knows the provider interface, it can find and use different implementations of that interface. You can use the first loadable instance of the interface or even iterate through all the available interfaces.

The ServiceLoader class is final, which means that you cannot subclass or override its loading algorithms. You cannot, for example, change its algorithm to search for services from a different location.

From the perspective of the ServiceLoader class, all services have a single type, which is usually a single interface or abstract class. The provider itself contains one or more concrete classes that extend the service type with an implementation specific to its purpose. The ServiceLoader class requires that the single exposed provider type has a default constructor, which requires no arguments. This allows the ServiceLoader class to easily instantiate the service providers that it finds.

Define a service provider by implementing the service provider API. Usually, you will create a JAR file to hold your provider. To register your provider, you must create a provider configuration file in the JAR file's META-INF/services directory. The configuration file name should be the fully qualified binary name of the service's type. The binary name is simply the fully qualified class name in which each component of the name is separated by a . character, and nested classes are separated by a $ character.

For example, if you implement the com.example.dictionary.spi.Dictionary service type, you should create a META-INF/services/com.example.dictionary.spi.Dictionary file. On separate lines within the file, list the fully qualified binary names of your concrete implementations. The file must be UTF-8 encoded. Additionally, you can include comments in the file by beginning the comment line with the # character.

A service loader will ignore duplicate provider class names in either the same configuration file or other configuration files. Although you will most likely put the configuration file within the same JAR file as the provider class itself, this is not strictly necessary. However, the provider must be accessible from the same class loader that was initially queried to locate the configuration file.

Providers are located and instantiated on demand. A service loader maintains a cache of the providers that have been loaded. Each invocation of the loader's iterator method returns an iterator that first yields all of the elements of the cache, in instantiation order. It then locates and instantiates any new providers, adding each one to the cache in turn. You can clear the provider cache with the reload method.

To create a loader for a specific class, provide the class itself to the load or loadInstalled method. You can use default class loaders or provide your own ClassLoader subclass.

The loadInstalled method searches the runtime environment's extension directory of installed runtime providers. The default extension location is your runtime environment'sjre/lib/ext directory. You should use the extension location only for well-known, trusted providers because this location becomes part of the classpath for all applications. In this article, providers will not use the extension directory but will instead depend on an application-specific classpath.

Dictionary Provider Implementation

This section describes how to implement the DictionaryService and Dictionary provider classes described earlier in this article. Providers are not always implemented by the original application vendor. In fact, anyone can create a service provider if they have the SPI specification, which tells them what interface to implement. The example word-processor application provides a DictionaryService and defines a Dictionary SPI. The published SPI defines a single Dictionary interface with one method. The entire interface is shown here:

package com.example.dictionary.spi;

public interface Dictionary {
String getDefinition(String word);
}

To provide this service, you must create a Dictionary implementation. To keep things simple for now, start by creating a general dictionary that defines just a few words. You can implement the dictionary with a database, a set of property files, or any other technology. The easiest way to demonstrate the provider pattern is to include all the words and definitions within a single file.

The following code shows a possible implementation of this SPI. Notice that it provides a no-argument constructor and implements the getDefinition method defined by the SPI.

package com.example.dictionary;
import com.example.dictionary.spi.Dictionary;
import java.util.SortedMap;
import java.util.TreeMap;

public class GeneralDictionary implements Dictionary {

private SortedMap<String, String> map;

/** Creates a new instance of GeneralDictionary */
public GeneralDictionary() {
map = new TreeMap<String, String>();
map.put("book", "a set of written or printed pages, usually bound with " +
"a protective cover");
map.put("editor", "a person who edits");
}

public String getDefinition(String word) {
return map.get(word);
}
}

Before you compile and create this provider's JAR file, only one task remains. You must comply with the provider registration requirement to create a configuration file in your project and JAR file's META-INF/services directory. Because this example implements the com.example.dictionary.spi.Dictionary interface, you create a file of the same name within the directory. The contents should contain a single line listing the concrete class name of the implementation. In this case, the file contents look like this:

com.example.dictionary.GeneralDictionary

The final JAR contents will contain files as shown in Figure 2.

The GeneralDictionary provider is packaged in the GeneralDictionary.jar file.

Figure 2. The GeneralDictionary provider is packaged in the GeneralDictionary.jar file.

The GeneralDictionary provider for this example defines just two words: book and editor. Obviously, a more usable dictionary would provide a more substantial list of generally used vocabulary.

To use the GeneralDictionary, you should place its deployment JAR file, GeneralDictionary.jar, into the application's classpath.

To demonstrate how multiple providers can implement the same SPI, the following code shows yet another possible provider. This provider is an extended dictionary containing technical terms familiar to most software developers.

package com.example.dictionary;
import com.example.dictionary.spi.Dictionary;
import java.util.SortedMap;
import java.util.TreeMap;

public class ExtendedDictionary implements Dictionary {
private SortedMap<String, String> map;

/**
* Creates a new instance of ExtendedDictionary
*/
public ExtendedDictionary() {
map = new TreeMap<String, String>();
map.put("XML",
"a document standard often used in web services, among other things");
map.put("REST",
"an architecture style for creating, reading, updating, " +
"and deleting data that attempts to use the common vocabulary" +
"of the HTTP protocol; Representational State Transfer");
}

public String getDefinition(String word) {
return map.get(word);
}
}

This additional ExtendedDictionary follows the same pattern as the GeneralDictionary: You must create a configuration file for it and place the JAR file in your application's classpath. The configuration file should again be named using the SPI class name of com.example.dictionary.spi.Dictionary. This time, however, the file contents will be different from the GeneralDictionary implementation. For the ExtendedDictionary provider, the file contains the following single line that declares the concrete class implementation of the SPI:

com.example.dictionary.ExtendedDictionary

The files and structure for this additional Dictionary implementation are shown in Figure 3.

The ExtendedDictionary provider is packaged in the ExtendedDictionary.jar file

Figure 3. The ExtendedDictionary provider is packaged in the ExtendedDictionary.jar file.

It is easy to imagine customers using a complete set of Dictionary providers for their own special needs. The service loader API allows them to add new dictionaries to their application as their needs or preferences change. Moreover, because the underlying word-processor application is extensible, no additional coding is required for customers to use the new providers.

Dictionary User Demo

Because developing a full word-processor application would be a significant undertaking, the author will provide a more simple application that defines and uses the DictionaryServiceand Dictionary SPI. The Dictionary User application allows a user to type in a word and retrieve its definition from any Dictionary providers on the classpath.

The DictionaryService class itself will sit in front of all Dictionary implementations. The application will access the DictionaryService to retrieve definitions. TheDictionaryService instance will load and access available Dictionary providers on behalf of the application. The DictionaryService class source code is here:

package com.example.dictionary;
import com.example.dictionary.spi.Dictionary;
import java.util.Iterator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;

public class DictionaryService {

private static DictionaryService service;
private ServiceLoader<Dictionary> loader;

/**
* Creates a new instance of DictionaryService
*/
private DictionaryService() {
loader = ServiceLoader.load(Dictionary.class);
}

/**
* Retrieve the singleton static instance of DictionaryService.
*/
public static synchronized DictionaryService getInstance() {
if (service == null) {
service = new DictionaryService();
}
return service;
}

/**
* Retrieve definitions from the first provider
* that contains the word.
*/
public String getDefinition(String word) {
String definition = null;

try {
Iterator<Dictionary> dictionaries = loader.iterator();
while (definition == null && dictionaries.hasNext()) {
Dictionary d = dictionaries.next();
definition = d.getDefinition(word);
}
} catch (ServiceConfigurationError serviceError) {
definition = null;
serviceError.printStackTrace();

}
return definition;
}
}

The DictionaryService instance is the application's entry point to using any installed Dictionary. Use the getInstance method to retrieve the singleton service entry point. Then the application can call the getDefinition method, which iterates through available Dictionary providers until it finds the targeted word. The getDefinition method returns null if no Dictionary instance contains the specified definition of the word.

The dictionary service uses the ServiceLoader.load method to find the target class. The SPI is defined by the interface com.example.dictionary.spi.Dictionary, so the example uses this class as the load method's argument. The default load method searches the application classpath with the default class loader.

However, an overloaded version of this method allows you to specify custom class loaders if you wish. That would allow you to do more sophisticated class searches. A particularly enthusiastic programmer might, for example, create a ClassLoader instance that could search in an application-specific subdirectory that contains provider JARs added during runtime. The result would be an application that would not require a restart to access new provider classes.

Once a loader for this class exists, you can use its iterator method to access and use each provider that it finds. The getDefinition method uses a Dictionary iterator to loop through the providers until it finds a definition for the specified word. The iterator method caches Dictionary instances, so successive calls require little additional processing time. If new providers have been placed into service since the last invocation, the iterator method adds them to the list.

The DictionaryUser class uses this service. To use the service, the application simply creates a DictionaryService and calls the getDefinition method when the user types a searchable word. If a definition is available, the application displays it. If a definition is not available, the application displays a message stating that no available dictionary carries the word.

The following code listing shows most of the DictionaryUser implementation. Some of the user interface layout code has been removed to make the listing easier to read. The primary point of interest is the txtWordActionPerformed method. This method runs when the user presses the Enter key within the application's text field. The method then requests a definition of the target word from the DictionaryService object, which in turn passes the request to its known Dictionary providers.

package com.example.demo;

import com.example.dictionary.DictionaryService;
import javax.swing.JOptionPane;

public class DictionaryUser extends javax.swing.JFrame {

/** Creates new form DictionaryUser */
public DictionaryUser() {
dictionary = DictionaryService.getInstance();
initComponents();
}

/** This method is called from within the constructor to
* initialize the form.
*/
private void initComponents() {
// ...
txtWord.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
txtWordActionPerformed(evt);
}
});
// ...
}

private void txtWordActionPerformed(java.awt.event.ActionEvent evt) {
String searchText = txtWord.getText();
String definition = dictionary.getDefinition(searchText);
txtDefinition.setText(definition);
if (definition == null) {
JOptionPane.showMessageDialog(this,
"Word not found in dictionary set",
"Oops", JOptionPane.WARNING_MESSAGE);
}
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new DictionaryUser().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JLabel lblDefinition;
private javax.swing.JLabel lblSearch;
private javax.swing.JTextArea txtDefinition;
private javax.swing.JTextField txtWord;
// End of variables declaration
private DictionaryService dictionary;
}

Figure 4 shows the warning message pane that the application displays when the target word book is not available. The GeneralDictionary class defines the book term, but this class is not in the application classpath.

Without a dictionary provider, the application cannot find definitions.

Figure 4. Without a dictionary provider, the application cannot find definitions.

You can put the GeneralDictionary class on the classpath by adding it to the command-line classpath argument of the runtime environment. The following command line adds the dictionary to a Microsoft Windows runtime classpath:

java -classpath DictionaryUser.jar;GeneralDictionary.jar
com.example.demo.DictionaryUser

Notice that this command line references two JAR files: DictionaryUser, and GeneralDictionary. The author divided the application and API so that the DictionaryUser.jarfile contains the DictionaryService class, Dictionary interface, and the Dictionary User application itself. The GeneralDictionary.jar file contains the provider implementation.

Using the newly available provider, the Dictionary User application now finds the word. Figure 5 shows the result.

The application finds definitions in providers found on the classpath.

Figure 5. The application finds definitions in providers found on the classpath.

Add providers to the classpath by appending the provider's JAR file to the command line classpath argument. The new provider in this example is ExtendedDictionary. The following command line would add it to the application:

java -classpath DictionaryUser.jar;GeneralDictionary.jar;ExtendedDictionary.jar
com.example.demo.DictionaryUser

Now some technical terms are defined in the Dictionary User application. Figure 6 shows the results of a search for the term REST after the user has added theExtendedDictionary.jar provider:

 New terms are available from additional dictionary providers.

Figure 6. New terms are available from additional dictionary providers.

Limitations of the ServiceLoader API

The ServiceLoader API is useful, but it has limitations. For example, it is impossible to subclass ServiceLoader, so you cannot modify its behavior. You can use customClassLoader subclasses to change how classes are found, but ServiceLoader itself can't be extended. Also, the current ServiceLoader class can't tell your application when new providers are available at runtime. Additionally, you cannot add change-listeners to the loader to find out whether a new provider has been placed into an application-specific extension directory.

The public ServiceLoader API is available in Java SE 6. Although the loader service existed as early as JDK 1.3, the API was private and only available to internal Java runtime code.

NetBeans Platform Support

An alternate way to provide extensible services for an application is to use the NetBeans platform. Most developers know the NetBeans integrated development environment (IDE), but many are unaware that the IDE itself is an extensible application built upon modular, general platform.

The NetBeans platform provides a complete application framework for creating modular, extensible applications. Modules for user interface, printing, intermodule communication, and many other services already exist in the platform. Using these existing, well-tested APIs can save you time developing a larger application.

Although the entire platform is beyond this article's scope, it does have a subset of pertinent facilities for registering, discovering, and using service providers. Most of the APIs you need for registering, finding, and using providers are available from the org.openide.util.Lookup class. This class provides applications with the ability to find services and is a significant improvement over the simple ServiceLoader class.

You don't have to adopt the entire NetBeans platform to get enhanced lookup functionality. You can get provider lookup services by using just a single module of the platform. If you have the NetBeans IDE, you also have the NetBeans platform. Getting the platform from the IDE distribution is probably the easiest way for most people to acquire the platform. By including theorg-openide-util.jar file from the <NETBEANS_HOME>\platform6\lib subdirectory, you get some of the following benefits over the standard Java SE 6 implementation of theServiceLoader class:


  • The Lookup API is available even if you use earlier versions of the Java SE Development Kit (JDK).
  • The Lookup API can be subclassed, allowing you to customize its functionality.
  • The Lookup API allows you to listen and respond to changes in service providers.

The exact location of the JAR file may be different depending on your NetBeans IDE version. Instead of <NETBEANS_HOME>\platform6\lib found in NetBeans 5.5, the file may be in aplatform7\lib or different subdirectory if you use NetBeans 6.0 or later. To use org-openide-util.jar, you should add it to your compile and runtime classpath. Although this JAR file contains many utilities, this article will use only the utilities for the Lookup and related APIs.

The Lookup Class

The org.openide.util.Lookup class has all the functionality of ServiceLoader and more. It also has an interface that allows any class to become a Lookup type, which simply means that the class will provide a getLookup method itself. The Lookup class provides a default Lookup instance that searches the classpath. The examples in this article use the default. However, it would be relatively easy for a programmer to create a customized Lookup subclass that is able to monitor a changeable classpath during application runtime, allowing for truly dynamic service provider installations.

The systemwide Lookup instance default is available from the static getDefault method:

Lookup myLookup = Lookup.getDefault();

In the most basic case, you can use Lookup to return the first provider instance it finds on the classpath. Use the Lookup instance's lookup method for that purpose. Provide the targeted class as the method argument. The following code will find and return an instance of the first Dictionary provider it finds:

Dictionary dictionary = myLookup.lookup(Dictionary.class);

Using version 5.5 of the NetBeans platform, you must use a template class to find and return multiple provider instances. Create a Lookup.Template and provide the template to thelookup method. The result contains all the matching providers. The following code shows how to use Template and Result classes to find and return all provider instances of theDictionary class.

This new DictionaryService2 class provides the same functionality as the original DictionaryService class. The difference is that the new implementation uses the NetBeans Platform APIs, which work on earlier versions of the JDK and provide the benefits described earlier.

/*
* DictionaryService.java
*/

package com.example.dictionary;
import com.example.dictionary.spi.Dictionary;
import java.util.Collection;
import org.openide.util.Lookup;
import org.openide.util.Lookup.Result;
import org.openide.util.Lookup.Template;

public class DictionaryService2 {

private static DictionaryService2 service;
private Lookup dictionaryLookup;
private Collection<Dictionary> dictionaries;
private Template dictionaryTemplate;
private Result dictionaryResults;

/**
* Creates a new instance of DictionaryService
*/
private DictionaryService2() {
dictionaryLookup = Lookup.getDefault();
dictionaryTemplate = new Template(Dictionary.class);
dictionaryResults = dictionaryLookup.lookup(dictionaryTemplate);
dictionaries = dictionaryResults.allInstances();
}

public static synchronized DictionaryService2 getInstance() {
if (service == null) {
service = new DictionaryService2();
}
return service;
}

public String getDefinition(String word) {
String definition = null;
for(Dictionary d: dictionaries) {
definition = d.getDefinition(word);
if (d != null) break;
}
return definition;
}
}

In particular, notice the way to get multiple provider instances. That code is shown in the private DictionaryService2 constructor:

    private DictionaryService2() {
dictionaryLookup = Lookup.getDefault();
dictionaryTemplate = new Template(Dictionary.class);
dictionaryResults = dictionaryLookup.lookup(dictionaryTemplate);
dictionaries = dictionaryResults.allInstances();
}

The template lookup method returns a Result instance that contains multiple providers, if they exist. You can retrieve the entire collection of providers by calling the Result instance'sallInstances method. This allows you to iterate over the collection of Dictionary instances like this:

    for(Dictionary d: dictionaries) {
definition = d.getDefinition(word);
if (d != null) break;
}

Summary

Extensible applications provide service points that can be extended by service providers. The easiest way to create an extensible application is to use the ServiceLoader class available in the Java SE 6 platform. Using this class, you can add provider implementations to the application classpath to make new functionality available.

The ServiceLoader class is available only in Java SE 6, so you may need to consider other options for earlier runtime environments. Also, the ServiceLoader class is final, so you cannot modify its abilities. One alternative class is in the NetBeans platform, which provides access to extensible services with its Lookup API. The Lookup class provides all the functionality of ServiceLoader, but it has the added benefit of being subclassable.

More Information


http://java.sun.com/developer/technicalArticles/javase/extensible/

Displaying Only Directories in a File Chooser Dialog

 

JFileChooser fileChooser = new JFileChooser(file); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

 

Related examples

Adding a Filter to a File Chooser Dialog

Copying the Filename Path from a JFileChooser Dialog to the Clipboard

Creating a JFileChooser Dialog

Determining If the Approve or Cancel Button Was Clicked in a JFileChooser Dialog

Getting and Setting the Current Directory of a JFileChooser Dialog

Getting and Setting the Selected File of a JFileChooser Dialog

Getting the File-Type Name of a File

http://www.exampledepot.com/egs/javax.swing.filechooser/DirOnly.html

How to Programmatically Open the New File Dialog in NetBeans

The question of the day comes from Robin, who is from bstek.com in China. He sends the screenshot shown below and wonders how to enable the "New File" dialog to be opened when a button he created is clicked:

There was a discussion on the dev@openide.netbeans.org mailing list sometime ago (click here to jump into it), answering this question. And here's code that I have tried a few minutes ago (in a 6.7 build) that opens the New File dialog. However, note that the New File dialog can only open in the context of a project in NetBeans IDE. In other words, a project must be open and the assumption is that the new file will be created within that project.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Action;
import org.openide.ErrorManager;
import org.openide.cookies.InstanceCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;

public final class SomeAction implements ActionListener {

public void actionPerformed(ActionEvent e) {

Action a = findAction("Actions/Project/org-netbeans-modules-project-ui-NewFile.instance");
a.actionPerformed(e);

}

public Action findAction(String key) {
FileObject fo = FileUtil.getConfigFile(key);
if (fo != null && fo.isValid()) {
try {
DataObject dob = DataObject.find(fo);
InstanceCookie ic = dob.getLookup().lookup(InstanceCookie.class);
if (ic != null) {
Object instance = ic.instanceCreate();
if (instance instanceof Action) {
Action a = (Action) instance;
return a;
}
}
} catch (Exception e) {
ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
return null;
}
}
return null;
}

}

The code above comes straight from the discussion on the mailing list. Alternative approaces are also described there. So, here's hoping this answers the question, Robin! And good luck with your plugin, looks pretty interesting so far.

May 25 2009, 06:26:38 PM PDT Permalink

3 Comments

Trackback URL: http://blogs.sun.com/geertjan/entry/how_to_programmatically_open_the


Comments:

http://blogs.sun.com/geertjan/entry/how_to_programmatically_open_the

File Access and Display

  • To manage, manipulate, and represent data

image

Solutions

  • File System
  • Data System
  • Nodes APIs

Four Layers

  • Presentation Layer
  • Logical Layer
  • Abstraction Layer
  • Physical Layer

File System API

image

  • The NetBeans Platform gives transparent access to files and folders by providing the File System API.
  • … as if the data is stored in the form of a virtual XML file system, such as that used by the System Filesystem, a JAR file, or in a normal folder.
  • Operations
    • Obtaining
    • Creating
    • Renaming
    • Deleting
    • Moving
    • Reading and Writing Files
    • Monitoring Changes
      • Only internal events are monitored

Data System API

  • Provides a logical layer on top of the File Systems API
    • While a FileObject manages its data regardless of type, a DataObject is a wrapper for a FileObject of a quite specific type.
    • A DataObject extends a FileObject with type-specific features and functionalities.
    • These are specified through interfaces or abstract classes: so-called cookies.

image

  • DataObject
    • Implementing and Adding Cookies
    • Using Cookies
    • Providing Cookies Dynamically
    • Creating a DataObject Manually
  • DataObject Factory
  • DataLoader
    • Implementation
    • Registration

Nodes API

image

  • Node Container
    • Actions
    • Event Handling
  • Implementing Nodes and Children

Explorer & Property Sheet API

image

Window System

  • The window system is a framework provided by the NetBeans Platform.
  • It is responsible for the administration and display of all application windows.
  • It allows the user to customize the layout of the user interface.

Introduction

  • The window system is document based.
    • The central section – that is, the editor section – is all about the display of several files in tabs.
    • View sections are placed around the editor section.
    • Components are arranged within the view sections.
  • The window system is comprised of modes.
    • A mode is a NetBeans Platform class that provides a container for window, displayed like a tab.
    • The windows must be subclasses of TopComponent.
    • Evey displayed window is managed by the WindowManager.
    • Windows can be grouped as well.
      • The assembly of the window system is described in the layer file.

Customization

  • You can disable several features to prevent undesired changes by the end user, such as the undocking or closing of windows.
  • Go to the Properties dialog of your NetBeans Platform Application (or Module Suite) and open the category Build –> Window System.

Window: TopComponent

  • Creating a TopComponent
    • Definition and mapping of a TopComponent in the layer file
    • Settings file to declaratively add a TopComponent
    • TopComponent Reference file mapping a TopComponent to a mode
    • Definition of an action for opening a TopComponent
    • Docking a TopComponent into a specific mode programmatically
  • States
    • Different states of a TopComponent
      • opened
      • closed
      • visible
      • invisible
      • active
      • inactive
    • Methods for the different states
      • opened: protected void componentOpened()
      • closed: protected void componentClosed()
      • visible protected void componentShowing()
      • invisible: protected void componentHidden()
      • active: protected void componentActivated()
  • Context Menu
  • Persistence
  • Registry

Docking Container: Mode

  • Creating a mode

image

  • Modifying a Mode

Groups of Windows: TopComponentGroup

  • Creating a TopComponentGroup

Administration: WindowManager

  • findMode(String name)
  • findMode(TopComponent t)
  • findTopComponent(String id)
  • findTopComponentID(TopComponent t)
  • findTopComponentGroup(String name)

Window System Architecture

image

26.7.10

Menu Bar

  • The menu bar of an application based on the NetBeans Platform is created by the NetBeans Platform via the System Filesystem.

 

  • Creating and Adding Menus and Menu Entries
    • Creating an action class for a menu entry
    • Adding a menu entry in the layer file
    • Creating a submenu
  • Inserting Separators
  • Hiding Existing Menu Entries
    • Simply use the layer tree
      • Creating a Custom Menu Bar
      • Important files –> XML Layer –> <this layer in context>
    • Use the NetBeans APIs
      • The Data Systems API
        • Provides the MenuBar class, which is a subclass of JMenuBar, and has the ability to create its content from a DataFolder object.
          • Create a DataFolder
          • Access the System Filesystem via method getDefaultFileSystem()
          • findFolder()

Structure of the NetBeans Application Window

image

Utilities.keyToString() & Utilities.stringToKey()

It can be helpful to look in the Javadoc for the functions Utilities.keyToString() and Utilities.stringToKey(). These are used for encoding shortcuts.

Action Types

  • Always Enabled Actions

image

image

    • Category:
      • The categories represent semantic groupings of the actions.
      • Either select a preexisting category or create a new one.
  • CallableSystemAction
  • CallbackSystemAction
    • Is a subclass of CallableSystemAction
    • Can delegate to another action, which is typically a context sensitive action.
    • Contains no action logic, but delegates to an action performer.
    • Are used especially by global actions – that is, actions that provide different behavior depending on their context.
      • searches
      • copying
      • pasting
      • such global actions are offered out of the box by the NetBeans Actions API.
    • The action performer:
      • Is made available by a Java ActionMap
        • is made available via a Lookup.
      • Is delivered via the getActionMapKey()
      • All classes that derive from JComponent can make use of an ActionMap.
        • That means the NetBeans superclass TopComponent, which creates windows that integrate into NetBeans Platform applications, also has an ActionMap.
  • CookieAction
    • NodeAction
      • Are dependent on nodes
        • A node is the visual representation of a particular piece of data.
        • Each TopComponent (and therefore each window within the NetBeans Platform) can make use of one or many activated nodes.
    • It is precisely these activated nodes that form the context of a CookieAction.
    • Context-sensitivity is constructed from interfaces, which are called cookies.

image

    •  
  • General Context-Sensitive Action Classes
    • Yet another approach:
      • Providing generic context-sensitive action classes with the help of the Lookup class
    • You will not be dependent upon a Node class, nor any other NetBeans superclass, since you will boserve how any class or interface can be used to determine the applicable context of an action.
public abstract class ContextAction<T> extends AbstractAction 
implements LookupListener, ContextAwareAction {
private Lookup context = null;
private Lookup.Result result = null;

public ContextAction(Lookup context) {
init(context);
}
private void init(Lookup context) {
this.context = context;
result = context.lookupResult(contextClass());
result.addLookupListener(this);
resultChanged(null);
}
public void resultChanged(LookupEvent ev) {
setEnabled(result.allItems().size() != 0);
}
public void actionPerformed(ActionEvent e) {
performAction(result.allInstances().iterator().next());
}
public abstract Class contextClass();
public abstract void performAction(T context);
}

NetBeans Platform Actions

  • The NetBeans Platform bases its actions on the Swing Action Framework.
    • Ultimately, every action rests upon Swing’s Action interface.
    • Simplest case:
      • An action class implements the Action interface
    • Straightforward:
      • Extends the AbstractAction class.
  • The NetBeans’ Own Base Classes for Actions

Hierarchy-of-the-NetBeans-Platform-Action-Base-Classes

  • Registry
    • Actions are registered centrally in the layer file of your module, within the Actions folder.
  • Instance:
    • Actions can be used multiple times in different places
    • Only one instance is created.
  • The possibility of toolbar customization.

Using Libraries

  • Library Wrapper Module
    • The whole application is based on modules.
    • It is desirable to integrate the external JAR file in the form of a module.
    • Enhances the consistency of the application as a whole.
    • You can also bundle multiple JAR files into a single module, after which you need no longer put the physical JAR files on the application classpath, as is normally done when developing applications.
  • Two very important things:
    • It marked all packages in the third-party library with the attribute OpenIDE-Module-Public-Packages, making all these packages publicly accessible.
    • Marked the library it found in the ext/ folder with the Class-Path attribute, putting it on the module classpath.
  • It is advisable to always use a library wrapper module when integrating a third-party library into an application.

Module Registry

Collection modules = 
Lookup.getDefault().lookupAll(ModuleInfo.class);


  • ModuleInfo class

    • The module system provides a ModuleInfo class for each module, where all information about modules is stored.
    • The ModuleInfo objects are available centrally via the Lookup, and can be obtained there as above.

  • Information List / getAttribute():

    • name
    • version
    • dependencies
    • current status (activated or deactivated)
    • the existence of service implementations for the current module

  • Being informed of changes / PropertyChangeListener
  • LookupListener

    • Informs you of the installation and uninstallation of modules
Lookup.Result res = Lookup.getDefault().lookupResult(ModuleInfo.class);
result.addLookupListener(new LookupListener() {
public void resultChanged(LookupEvent lookupEvent) {
Collection c = res.allInstances();
System.out.println("Available modules: " + c.size());
}
});
res.allItems(); //initialize the listener

DialogDisplayer & NotifyDescriptor

NotifyDescriptor d = new NotifyDescriptor.Confirmation(
"Do you really want to exit the application?",
"Exit",
NotifyDescriptor.YES_NO_OPTION);
if(DialogDisplayer.getDefault().notify(d) == NotifyDescriptor.YES_OPTION) {
return true;
}else{
return false;
}


  • Dialog API

Lifecycle of a NetBeans Module

  • To influence the lifecycle of a module, implement a module installer.
    • Module System API
    • ModuleInstal class
  • validate():
    • This method is called before a module is installed or loaded.
    • Load sequences, such as the verification of a module license, are set here. Should the sequence not succeed and the module not be loaded, throw an IllegalStateException method. This exception prevents loading or installing the module
  • restored():
    • This method is called when an installed module is loaded. Here, actions required to initialize the module are called.
  • uninstalled():
    • When the module is removed from the application, this method is called.
  • closing():
    • Before a module is ended, this method is called. Here, you also test whether the module is ready to be removed. Only once this is true for all the modules in the application can the application itself shut down. You can, for example, show the user a dialog to confirm whether the application should readlly be closed.
  • close():
    • If all modules are ready to end, this method is called. Here, you call the actions for the successful verification of the module in question.

Module Layer

  • Layer file
    • This is the central configuration file, in which virtually everything is defined that a module adds to the NetBeans Platform. Partly, it can be seen as the interface between the module and the NetBeans Platform, describing declaratively the integration of the module into the NetBeans Platform.
  • Declare:
    • manifest file
      • OpenIDE-Module-Layer: com/galileo/netbeans/module/layer.xml

netbeans-system-filesystem

  • Order of Entries
    • The order in which the entries of the layer file are read (and hence shown in the menu bar) is defined by a position attribute.
  • Instance Files
    • Files of the type .instance in the System filesystem describe objects of which instances can be created. The filename typically describes the full class name of a Java object.
  • Shadow Files
    • .shadow files are a kind of link or reference to an .instance file. They are used mainly when singleton instances of objects, as with actions, are used.
  • Settings Files
    • .settings files are an extracted version of .instance files in the layer file. Information on the type of class and how an instance is created from this class – i.e., information on what these attributes can determine in an .instance file – is defined in a separated XML file.
  • Creating and Using Your Own Contents
    • Your module may use folders, and attributes from the layer file in order to provide extension points to other modules.

Module Manifest

  • Attributes
    • OpenIDE-Module
      • This attribute defines a unique name for the module used for recognition by the module system. The specification of this attribute is mandatory.
    • OpenIDE-Module-Name
      • This defines a displayable name of the module, also displayed in the Plugin Manager.
    • OpenIDE-Module-Short-Description
      • This represents a short functionality description provided by the module.
    • OpenIDE-Module-Long-Description
      • The attribute defines a long description of the module-provided functionality.
    • OpenIDE-Module-Display-Category
      • Modules are summarized into a virtual group with this attribute and thus presented to the user as a functional unit.
    • OpenIDE-Module-Install
      • To run actions at certain times in the modue lifecycle, this attribute sets a module installer class.
    • OpenIDE-Module-Layer
      • This is one of the most important attributes. With it, the path is specified to the layer file describing the module integration into the platform.
    • OpenIDE-Module-Public-Packages
      • To support encapsulation, access to classes from another module is denied by default. This attribute is used to set visible public packages and allow other modules to use these classes. It is especially essential with libraries.
    • OpenIDE-Module-Friends
      • If only certain modules are allowed access to these packages, which are declared as public, then these may be stated here.
    • OpenIDE-Modue-Loalizing-Bundle
      • Here, a properties file is defined, which is used as a localizing bundle.
  • Versioning and Dependencies
    • OpenIDE-Module-Module-Dependencies
    • OpenIDE-Module-Package-Dependencies
    • OpenIDE-Module-Java-Dependencies
    • OpenIDE-Module-Specification-Version
    • OpenIDE-Module-Implementation-Version
    • OpenIDE-Module-Build-Version
    • OpenIDE-Module-Module-Dependencies-Message
    • OpenIDE-Module-Deprecated
    • OpenIDE-Module-Deprecation-Message
  • Services and Interfaces
    • OpenIDE-Module-Provides
    • OpenIDE-Module-Requires
    • OpenIDE-Module-Needs
    • OpenIDE-Module-Recommends
    • OpenIDE-Module-Requires-Message
  • Visibility
    • AutoUpdate-Show-In-Client
    • AutoUpdate-Essential-Module

25.7.10

Module Type

 

 

	<module name="com.galileo.netbeans.module">
<param name="autoload">false</param>
<param name="eager">false</param>
<param name="enabled">true</param>
<param name="jar">modules/com-galileo-netbeans-module.jar</param>
<param name="specversion">1.0</param>
</module>


  • Regular

    • This is the common type of application modul They are oaded on application start. The application loading time is extended by the time of module initialization. Therefore, it is recommended to keep the module initialization very short. Normally it is not necessary to run anything during module loading, as many tasks can be defined declaratively.

  • Autoload


    • These modules are loaded only when another module requires them. Autoload modules correcspond to the priciple of lazy-loading. This mode is usually used for those modules acting as libraries.

  • Eager


    • Eager modules are only loaded when all dependencies are met. This is another option to minimize starting time. For example, if module X depends on the modules A and B, which are not available, it makes no sense to load module X.

Module Structure

A module is a simiple JAR file, normally consisting of the following parts

  • Manifest file (manifes.mf)
  • Layer file (layer.xml)
  • Class files
  • Resources like icons, properties bundles, helpsets, etc.

An XML config file (e.g., com-gaileo-netbeans-module.xm) is needed by each module, located outside the JAR file. This is the first file read – i.e., it announces the module to the platform.

NetBeans ClassLoader System

netbeans-classloader-system

The NetBeans classloader system is an essential part of the NetBeans runtime container and a continuation of the encapsulation of modules and structuring of a modulear architecture.is system consists of three different types of classloaders. These are the module classloader, the system loader, and the original classloader. Most classes are loaded by the module classloader. Only in certain cases, such as when resources must be accessed outside a module, is the system class

  • Module Classloader
    • For every module registered in the Module System, an instance of the module classloader is made available by means of which every module obtains its own namespace.
  • System Classloader
    • ClassLoader cl = Lookup.getDefault().lookup(ClassLoader.class);
    • ClassLoader cl = Thread.currentThread().getContextClassLoader();
  • Original Classloader
    • Is used by the launcher of the application.

NetBeans Runtime Container

netbeans-runtime-container

The runtime continaer is minimal subset of modules that NetBeans Platform applications require. Without other modules or settings being required, an application can be deployed contains five modules.

  • Bootstrap
    • This module is executed before any other. It carries out all registered command handlers and prepares a boot classloader, which loads the Startup module.
  • Startup
    • This module deploys the application, at which point it initializes the module system and the file system.
  • Module System
    • This module is responsible for the definition of modules, as well as their settings and dependencies.
  • File System
    • This module makes a virtual data system available, with platform-independent acfcess. Primarily it is used to load module resources into an application.
  • Utilities
    • This module provides basic components such as those required for intermodular communication.

NetBeans Platform Distribution

  • NetBeans runtime container
    • org-netbeans-bootstrap
    • org-netbeans-core-startup
    • org-openide-file-system
    • org-openide-modules
    • org-openide-util
  • Basic Underlying Functionality for Applicatioins Based on NetBeans Platform
    • org-netbeans-core
    • org-netbeans-core-execution
    • org-netbeans-core-ui
    • org-netbeans-core-windows
  • Predefined Application Module that Provides a Central Window for Displaying and Working with Application Output Messages
    • org-netbeans-core-output2
  • A Framework for Multiview Windows, such as Those Used by Matisse GUI Builder, and Makes an API available for similar views
    • org-netbeans-core-multiview
  • Windows System API
    • org-openide-windows
  • Update Functionality
    • org-netbeans-modules-autoupdate-services
    • org-netbeans-modules-autoupdate-ui
  • Providing a Window that Lets the User Select Folders and Files
    • org-netbeans-modules-favorites
  • A Range of Important System Actions
    • org-openide-actions
      • Copy
      • Paste
      • Print
  • A Very Powerful Module that Contains
    • org-openide-loaders
  • Nodes API
    • org-openide-nodes
  • The Display of Explore Views
    • org-openide-explorer
  • The Discovery of MIME-specific Settings, Services, and Other Objects, together with an SPI Enabling Creation of MIME-specific Date Providers.
    • org-netbeans-modules-editor-mimelookup
  • JavaHelp Runtime Library
    • org-netbeans-modules-javahelp
  • A Central Wrapper File System
    • org-netbeans-modules-masterfs
  • An Options Window
    • org-netbeans-modules-options-api
  • Controlling Long-running Tasks
    • org-netbeans-api-progress
    • org-netbeans-api-process-ui
  • General Queries API
    • org-netbeans-modules-queries
  • org-netbeans-modules-sendopts
  • org-netbeans-modules-settings
  • org-netbeans-awt
  • org-openide-dialogs
  • org-openide-execution
  • org-openide-io
  • org-openide-text
  • org-netbeans-swing-plaf
  • org-netbeans-swing-tabcontrol
  • org-jdesktop-layout
  • org-netbeans-api-visual
  • org-netbeans-spi-quicksearch

NetBeans Platform Architecture

netbeans-platform-architecture

The NetBeans Platform itself is formed from a group of core modules, which are needed for starting the application and for defining its user interface. To this end, the NetBeans Platform makes many APIs and service providers available, simplifying the development process considerably. Included in this group are, for example, the Actions API, which makes available the oft-needed action classes; the powerful Nodes API; and the Options SPI, with whose help your own options dialogs can easily be integrated into the application. Next to these, there are also complete reusable components in the NetBeans Platform, such as the Output window and the Favorites window.

22.7.10

Annotation usage with properties

Background

Groovy's property mechanism provides a short-hand which simplifies defining typical JavaBean-style classes. You simply define the property with no explicit visibility and it is replaced at the bytecode level by a private backing variable, a public setter and a public getter. To the Java world, all three parts are visible. In the Groovy world, conceptually it is OK to just think about the property - Groovy's property notation comes in to play so that using the setter or getter looks like using the property as if it was a field.

This works well but introduces a slight complication with annotations. When using the non-property approach, there are three places in the source code where an annotation may be added (the field, the setter and the getter). With the property approach (idiomatic Groovy) there is just one place available in the source to place an annotation.

Current Behaviour

Currently if an annotation is applied to a property, the compiler will attempt to apply the annotation to the field, the setter and the getter. If any of these fails (e.g. some annotations may apply only to methods or only to fields) the code won't compile.

PROPOSAL: Smarter annotation assignment

Use the java.lang.annotation.Target of any annotations to determine where the annotation will be included in the final class.

If the @Target of the used annotation marks the annotation as usable only on FIELD level, then the annotation will be attached to the generated field.

If the @Target of the used annotation marks the annotation as usable on METHOD level, then the annotation will be attached to the generated getter and setter.

If the @Target of the used annotation marks the annotation as usable on both FIELD and METHOD level, then the annotation will be attached to all the generated class elements.

Obviously, if multiple annotations are used, there is the possibility of the annotations being split across the generated class elements, e.g. I can use both a field and a method annotation. The field annotation will be attached to the field (and ignored for the getters and setters) while the method annotation will be attached just to the methods.

PROPOSAL: Additional annotation classification mechanism

Sometimes it is desirable to be more selective with annotation than is possible even with smarter annotation assignment, e.g. placing an annotation on just one of the setter or getter. Sometimes an annotation might be possible on both fields and methods but in a particular example we want to apply it to just one. To achieve this we need additional syntax to express how we want to classify an annotation.

One way to do this is to use a synthetic annotation as a prefix to the real annotation. A @Target annotation appears in Groovy code at the source level, but it is only synthetic. It has no real definition. It will be like a compiler flag only, and completely ignored in the output.

The @Target annotation allows the user to further refine where the compiler should attach an annotation. After it has been used by the compiler, it is completely discarded. The Groovy @Target annotation will further specify if an annotation will be attached to a FIELD, GETTER or SETTER, but will never contradict the Java @Target annotation. In case such a contradiction occurs the Groovy compiler must emit an error.

Last, but not least I should mention that there will be no naming conflict between the Java java.lang.annotation.Target annotation and the Groovy Target annotation (probably groovy.lang.annotation.Target), because they will be living in completely different context.

An example:

@Target([FIELD]) @AutoPopulate(defaultValue="bar")
@Target([GETTER]) @ManagedAttribute(currencyTimeLimit=15)
@Target([SETTER]) @ManagedAttribute(persistPolicy="OnUpdate")
int base = 10

As an alternative, we could come up with some other syntax to achieve the same result, e.g. here might be some other ways to write the above:

@[FIELD]AutoPopulate(defaultValue="bar")
@[GETTER]ManagedAttribute(currencyTimeLimit=15)
@[SETTER]ManagedAttribute(persistPolicy="OnUpdate")
int base = 10

Or this:

@AutoPopulate[FIELD](defaultValue="bar")
@ManagedAttribute[GETTER](currencyTimeLimit=15)
@ManagedAttribute[SETTER](persistPolicy="OnUpdate")
int base = 10

Or this (using a synthetic annotation parameter):

@AutoPopulate(groovyTarget='field', defaultValue="bar")
@ManagedAttribute(groovyTarget='getter', currencyTimeLimit=15)
@ManagedAttribute(groovyTarget='setter', persistPolicy="OnUpdate")
int base = 10
 
http://groovy.codehaus.org/Annotation+usage+with+properties

20.7.10

Annotations with Groovy

Introduction

Java 5 and above supports the use of annotations to include metadata within programs. Groovy 1.1 and above also supports such annotations.

Annotations are used to provide information to tools and libraries. They allow a declarative style of providing metadata information and allow it to be stored directly in the source code. Such information would need to otherwise be provided using non-declarative means or using external files. We won't discuss guidelines here for when it is appropriate to use annotations, just give you a quick run down of annotations in Groovy.

Annotations are defined much like Java class files but use the @interface keyword. As an example, here is how you could define aFeatureRequest Annotation in Java:

// Java
public @interface FeatureRequest {
String key();
String summary();
String assignee() default "[unassigned]";
String status() default "[open]";
String targetVersion() default "[unassigned]";
}

This annotation represents the kind of information you may have in an issue tracking tool. You could use this annotation in a Groovy file as follows:

@FeatureRequest(
key="GROOVY-9999",
summary="Support Graphical Annotations",
assignee="Pete",
status="Open",
targetVersion="5.0"
)
class SomeClassWhereFeatureCouldBeUsed {
// ...
}

Now if you had tools or libraries which understood this annotation, you could process this source file (or the resulting compiled class file) and perform operations based on this metadata.

As well as defining your own annotations, there are many existing tools, libraries and frameworks that make use of annotations. See some of the examples referred to at the end of this page. As just one example, here is how you could use annotations with Hibernate or JPA:

import javax.persistence.*

@Entity
@Table(name="staff")
class Staff implements Serializable {
@Id @GeneratedValue
Long id
String firstname
String lastname
String position
}

Example

As another example, consider this XStream example. XStream is a library for serializing Java (and Groovy) objects to XML (and back again if you want). Here is an example of how you could use it without annotations:

// require(groupId:'com.thoughtworks.xstream', artifactId:'xstream', version:'1.3')
import com.thoughtworks.xstream.*

class Staff {
String firstname, lastname, position
}

def xstream = new XStream()
xstream.classLoader = getClass().classLoader
def john = new Staff(firstname:'John',
lastname:'Connor',
position:'Resistance Leader')

println xstream.toXML(john)

This results in the following output:

<Staff>
<firstname>John</firstname>
<lastname>Connor</lastname>
<position>Resistance Leader</position>
</Staff>

Just as an aside, not related to annotations, here is how you could write the XML to a file:

new File("john.xml").withOutputStream { out ->
xstream.toXML(john, out)
}

And how you would read it back in:

// require(groupId:'com.thoughtworks.xstream', artifactId:'xstream', version:'1.3')
// require(groupId:'xpp3', artifactId:'xpp3_min', version:'1.1.4c')
import com.thoughtworks.xstream.*

class Staff {
String firstname, lastname, position
}

def xstream = new XStream()
def john
// now read back in
new File("john.xml").withInputStream { ins ->
john = xstream.fromXML(ins)
}

println john.dump()
// => <Staff@12d96f2 firstname=John lastname=Connor position=Resistance Leader>

Now, on to the annotations ...

XStream also allows you to have more control over the produced XML (in case you don't like its defaults). This can be done through API calls or with annotations. Here is how we can annotate our Groovy class with XStream annotations to alter the resulting XML:

// ...
import com.thoughtworks.xstream.annotations.*

@XStreamAlias("person")
class Associate {
@XStreamAsAttribute
@XStreamAlias('first-name')
String firstname

@XStreamAlias('surname')
String lastname

@XStreamOmitField
String position
}

msg = new Associate(firstname:'Sarah',
lastname:'Connor',
position:'Protector')
Annotations.configureAliases(stream, Associate)
println stream.toXML(msg)

When run, this produces the following output:

<person first-name="Sarah">
<surname>Connor</surname>
</person>

Differences to Java

Annotations may contain lists. When using such annotations with Groovy, remember to use the square bracket list notation supported by Groovy rather than the braces used by Java, i.e.:

// Java
@ManagedOperationParameters({
@ManagedOperationParameter(name="x", description="The first number"),
@ManagedOperationParameter(name="y", description="The second number")})

Would become:

// Groovy
@ManagedOperationParameters([
@ManagedOperationParameter(name="x", description="The first number"),
@ManagedOperationParameter(name="y", description="The second number")])

 


 


http://groovy.codehaus.org/Annotations+with+Groovy

17.7.10

How to Find the Embedded Image’s URL in PicasaWeb Java API?

PhotoEntry.getMediaContents().get(0).getUrl();

PicasaWeb Albums Data API: Java Developer’ Guide

Picasa Web Albums allows client applications to view and update albums, photos, and comments in the form of Google Data API feeds. Your client application can use the Picasa Web Albums Data API to create new albums, upload photos, add comments, edit or delete existing albums, photos, and comments, and query for items that match particular criteria.

In addition to providing some background on the capabilities of the Picasa Web Albums Data API, this guide provides examples for interacting with the API using the Java Google Data APIs Client Library. For help setting up the client library, see the Getting Started Guide.

If you're interested in understanding more about the underlying protocol used by the Java client library to interact with the Picasa Web Albums API, please see the protocol guide.

Contents

Audience

This document is intended for programmers who want to write client applications that can interact with Picasa Web Albums.

This document assumes that you understand the general ideas behind the Google Data APIs protocol. It also assumes that you know how to program in Java.

The code examples in this document use the Java client library; see the client library documentation for more information and more (non-Picasa Web Albums-specific) examples.

Getting Started

Picasa Web Albums uses Google Accounts for authentication, so if you have a Google account you are all set. Otherwise, you can create a new account.

For help setting up the client library, see the Getting Started Guide. To use the Java client library, you'll need the JDK 1.5+, and you should also be current on all patches.

To begin writing a Java application using the client library, you will need to add the following import statements to your Java source file:

 
import java.io.File;
import java.net.URL;

import com.google.gdata.client.*;
import com.google.gdata.client.photos.*;
import com.google.gdata.data.*;
import com.google.gdata.data.media.*;
import com.google.gdata.data.photos.*;

Authentication


The Java client library can be used to work with either public or private feeds. Public feeds are read-only, but do not require any authentication. Private feeds require that you authenticate to the Picasa Web Albums servers. This can be done via ClientLogin username/password authentication or AuthSub proxy authentication.


Single-user "installed" client authentication


To use ClientLogin (also called "Authentication for Installed Applications"), create a PicasawebService object, then invoke the setUserCredentials method to set the user ID and password,

 
PicasawebService myService = new PicasawebService("exampleCo-exampleApp-1");
myService.setUserCredentials("liz@gmail.com", "mypassword");

You can now use this object to make authenticated requests (like inserts, deletes, or updates).


For more information about the authentication systems of the Google Data APIs, see the Google Account Authentication documentation.


Multiple-user web application client authentication


The AuthSub login protocol allows your web-based application to have a user authenticate through Google's servers.


To authenticate a user using AuthSub, you have to follow these general steps:



  1. Construct the AuthSub URL.
  2. Send the user to a Google page to enter their AuthSub credentials.
  3. When the user returns, acquire a session token.
  4. Create a PicasawebService object.
  5. Tell the client library to use the given session token for interaction with the service, using the setAuthSubToken method.

Follow the instructions in the AuthSub and Google Data APIs client library documentation to acquire a session token, using the getRequestUrl, getTokenFromReply, exchangeForSessionToken, and setAuthSubToken methods. The scope string to use is:

http://picasaweb.google.com/data/

It'll look something like this:

 
String requestUrl =
AuthSubUtil.getRequestUrl("http://www.example.com/RetrieveToken",
"http://picasaweb.google.com/data/",
false,
true);
 
String sessionToken = AuthSubUtil.exchangeForSessionToken(onetimeUseToken, null);
 
PicasawebService.setAuthSubToken(sessionToken, null);

After you've called the PicasawebService object's setAuthSubToken method, the client library sends the token with every request, so you don't need to do anything further with the token.


Working with Albums


Albums are the way Picasa Web Albums groups photos into useful sets. These albums can be public or unlisted, and have their own properties such as a geographic location, a description, or a date.


You do not have to authenticate to retrieve data about public albums, but in order to create, update, or delete content, you must authenticate using one of the methods discussed in the authentication section.


Request a list of albums


To request the feed of albums belonging to user username, you can use the following Java code.

 
URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/username?kind=album");

UserFeed myUserFeed = myService.getFeed(feedUrl, UserFeed.class);

for (AlbumEntry myAlbum : myUserFeed.getAlbumEntries()) {
System.out.println(myAlbum.getTitle().getPlainText());
}

The getFeed method method handles all of the communication with Picasa Web Albums—it sends the HTTP GET request, it receives the redirect (if any), it sends the second GET request, it waits for the XML response to arrive, and it parses the XML and assigns the element contents and attribute values to appropriate parts of the given feed object.


If an error occurs during the getFeed call, the client library generates an appropriate exception, so your code should be prepared to handle exceptions.


The final line of the above code calls the feed's getTitle method, which returns a TextConstruct object. To transform the text construct into a String, we call the title's getPlainText method.


Add an album

 
AlbumEntry myAlbum = new AlbumEntry();

myAlbum.setTitle(new PlainTextConstruct("Trip to France"));
myAlbum.setDescription(new PlainTextConstruct("My recent trip to France was delightful!"));

AlbumEntry insertedEntry = myService.insert(postUrl, myAlbum);

The above code creates a new AlbumEntry, then sets various attributes for the album (title and description). It assumes that myService is an authenticated PicasawebService object. myService then calls the insert method to add the album and receive the response. If an error occurs during the insertion, then the client library generates an appropriate exception, so your code should be prepared to handle exceptions.


Modify the properties of an album


Once an entry is retrieved, it can be easily modified using the update method.

 
myAlbum.setDescription(new PlainTextConstruct("Updated album description"));
myAlbum.update();

Delete an album


Retrieved entries can also be deleted using the delete method.

 
myAlbum.delete();

Working with Photos


When uploading, modifying, or removing photos, you will have to authenticate a service object using one of the methods discussed in the Authentication section.


Request a list of photos


There are different ways to retrieve photos. The most common is to get a list of all of the photos in an album, but you can also retrieve recent photos from a user, or search photos from the public albums of other users.


List photos in album


This is the basic query to retrieve all of the photos belonging to username in the albumid album. The title of each photo is then printed out to the console.


The string "default" can be used in place of a real username, in which case the server will use the username of the user credentials used to authenticate the request.

 
URL feedUrl = "http://picasaweb.google.com/data/feed/api/user/username/albumid/albumid";

AlbumFeed feed = myService.getFeed(feedUrl, AlbumFeed.class);

for(PhotoEntry photo : feed.getPhotoEntries()) {
System.out.println(photo.getTitle().getPlainText());
}

List photos recently uploaded


It is also possible to retrieve the photos associated with a user, but without specifying any particular album. The snippet below will retrieve the latest photos uploaded by username and print their titles.

 
URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/username?kind=photo");

AlbumFeed feed = myService.getFeed(feedUrl, AlbumFeed.class);

for(PhotoEntry photo : feed.getPhotoEntries()) {
System.out.println(photo.getTitle().getPlainText());
}

List photos by community search


With the API, you can search photos uploaded by other users, as long as they are in a public album. The following code retrieves 10 photos matching a search for "puppy," (because who doesn't love puppies?) and prints out their titles. We'll illustrate this operation using a Query object. All the parameters previously specified are set — 10 results and "puppy" as a search term.

 
URL baseSearchUrl = new URL("http://picasaweb.google.com/data/feed/api/all");

Query myQuery = new Query(baseSearchUrl);
myQuery.setStringCustomParameter("kind", "photo");
myQuery.setMaxResults(10);
myQuery.setFullTextQuery("puppy");

AlbumFeed searchResultsFeed = myService.query(myQuery, AlbumFeed.class);

for (PhotoEntry photo : searchResultsFeed.getPhotoEntries()) {
System.out.println(photo.getTitle().getPlainText());
}

Accessing photo information


In previous examples, only the title of a photo was printed out. However, many different metadata fields can be retrieved about a photo— such as EXIF information, thumbnail URLs, the ID of the album it is a part of, and more.

 
System.out.println("Title: " + photo.getTitle().getPlainText());
System.out.println("Description: " + photo.getDescription().getPlainText());
System.out.println("ID: " + photo.getId());
System.out.println("Camera Model: " + photo.getExifTags().getCameraModel());
System.out.println("Geo Location: " + photo.getGeoLocation());
System.out.println("Media Thumbnail: " + photo.getMediaThumbnails().get(0).getUrl());

The above example prints out different pieces of information from a PhotoEntry. It also demonstrates how to retrieve elements from the gphotos namespace. The thumbnail and image URLs are taken from the MediaRSS elements inside of the entry. The camera data, if available, is found inside of the exif namespace.


Upload a new photo


Upload new photo with metadata


You can upload a photo to Picasa Web Albums using the insert method of the PicasawebService object. The following demonstrates uploading a new photo named "puppies.jpg," with the title "Puppies FTW," and the description "Puppies are the greatest." We assume that myService is an authenticated PicasawebService object. Note that "image/jpeg" is the valid MIME type of of our "puppies.jpg" file, but adjust for whatever photo file you are uploading. See the protocol guide for the types of files that are supported, along with their MIME type.

 
URL albumPostUrl = new URL("http://picasaweb.google.com/data/feed/api/user/username/albumid/albumid");

PhotoEntry myPhoto = new PhotoEntry();
myPhoto.setTitle(new PlainTextConstruct("Puppies FTW"));
myPhoto.setDescription(new PlainTextConstruct("Puppies are the greatest."));
myPhoto.setClient("myClientName");

MediaFileSource myMedia = new MediaFileSource(new File("/home/liz/puppies.jpg"), "image/jpeg");
myPhoto.setMediaSource(myMedia);

PhotoEntry returnedPhoto = myService.insert(albumPostUrl, myPhoto);

The MediaFileSource object specifies a source file on the local disk for the photo; you can instead use MediaSource to use an image from another kind of data stream. The insert method pulls the image data from the specified file or stream and sends it to Picasa Web Albums.


The setClient method sets the name of the client that's uploading the photo. You can use any name for your client that you want.


Upload new photo without metadata


To send a photo without its associated metadata, post to the following URL:

http://picasaweb.google.com/data/feed/api/user/username/albumid/albumid

This can be done using the following code:

 
MediaFileSource myMedia = new MediaFileSource(new File("/home/liz/puppies.jpg"), "image/jpeg");
PhotoEntry returnedPhoto = myService.insert(feedUrl, PhotoEntry.class, myMedia);

If you want to post a photo, but don't want the hassle of requiring the user of your app to choose an album, you can post the photo to the 'Drop Box.' This special album will automatically be created the first time it is used to store a photo. To post to the 'Drop Box,' use an albumid value of default.

Upload a video


Uploading a video can be done in the same way as uploading a photo with metadata:

 
URL albumPostUrl = new URL("http://picasaweb.google.com/data/feed/api/user/username/albumid/albumid");

PhotoEntry myVideo = new PhotoEntry();
myVideo.setTitle(new PlainTextConstruct("birthday.mov"));
myVideo.setDescription(new PlainTextConstruct("My grandpa's 80th birthday."));
myVideo.setClient("myClientName");

MediaFileSource myMedia = new MediaFileSource(new File("/home/liz/birthday.mov"), "video/quicktime");
myVideo.setMediaSource(myMedia);

PhotoEntry returnedVideo = myService.insert(albumPostUrl, myVideo);

// Fetching the video status (will always be "pending" right after posting a video).
String videoStatus = returnedVideo.getVideoStatus();

You can let the video processing service create a video thumbnail automatically or you can provide an image to be used instead. In the following sample, the returnedVideo variable from the above sample is used to update the thumbnail for the video:

 
MediaFileSource myThumb = new MediaFileSource(new File("/home/liz/birthday_title.jpg"), "image/jpeg");
returnedVideo.setMediaSource(myThumb);
PhotoEntry updatedEntry = returnedVideo.updateMedia(false);

Note: updating the entire video media data is currently not supported. You will have to create a new video entry and delete the existing one.


Update a photo


This example will update the photo's a title, tags, and geographic location. Notice that we are adding three tags (foo, bar, and baz) all at the same time using the <media:keywords> element.

 
myPhoto.setTitle(new PlainTextConstruct("New Title"));

MediaKeywords myTags = new MediaKeywords();
myTags.addKeyword("cute, puppy, dog");
myPhoto.setKeywords(tags);

myPhoto.setGeoLocation(37.7733, -122.4178);

myPhoto.update();

Delete a photo


Deleting a photo is done by using the delete method, after you have retrieved the photo object.

myPhoto.delete();

Working with Tags


Tags are a convenient way to label and organize your photos. By associating photos with descriptive strings, it makes searching through large quantities of photos easier.


Retrieving a list of tags


Your program can retrieve a list of tags that are used by a user, in a particular album, or that are associated with a particular photo.


List tags by user


The following code will print out all of the tags that username has used in photos in their albums.


The string "default" can be used in place of a real username, in which case the server will use the username of the user credentials used to authenticate the request.

 
URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/username?kind=tag");

AlbumFeed searchResultsFeed = myService.query(feedUrl, AlbumFeed.class);

for (TagEntry tag : searchResultsFeed.getTagEntries()) {
System.out.println(tag.getTitle().getPlainText());
}

List tags by album


The following code will print out all of the tags that username has tagged photos with in the albumid album.

 
URL feedUrl = new
URL("http://picasaweb.google.com/data/feed/api/user/username/albumid/albumid?kind=tag");

AlbumFeed searchResultsFeed = myService.query(feedUrl, AlbumFeed.class);

for (TagEntry tag : searchResultsFeed.getTagEntries()) {
System.out.println(tag.getTitle().getPlainText());
}

List tags by photo


The following code will print out all of the tags that username has tagged on the photo identified by photoid within the albumid album. The Accessing photo information section describes how to retrieve a valid value for photoid.

 
URL feedUrl = new
URL("http://picasaweb.google.com/data/feed/api/user/username/albumid/albumid/photoid/photoid?kind=tag");

AlbumFeed searchResultsFeed = myService.query(feedUrl, AlbumFeed.class);

for (TagEntry tag : searchResultsFeed.getTagEntries()) {
System.out.println(tag.getTitle().getPlainText());
}

Note that this same information is available inside of the <media:keywords> element of the photo itself in a comma separated format.


Search photos using tags


The following code uses the tag parameter to search for all photos belonging to username that are tagged with "puppy."


Here, we're creating a Query object and setting the tag to search for and the photo kind.

 
URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/username");

Query myQuery = new Query(feedUrl);
myQuery.setStringCustomParameter("kind", "photo");
myQuery.setStringCustomParameter("tag", "puppy");

AlbumFeed searchResultsFeed = myService.query(myQuery, AlbumFeed.class);

for (PhotoEntry photo : searchResultsFeed.getPhotoEntries()) {
System.out.println(photo.getTitle().getPlainText());
}

Note that you could search for photos in a particular album by adding the albumid in the URL.


Adding a tag to a photo


The following code adds the tag "terrier" to the photo identified by photoid, in the albumid album, which is owned by username.

 
URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/username/albumid/albumid/photoid/photoid");

TagEntry myTag = new TagEntry();
myTag.setTitle(new PlainTextConstruct("terrier"));

myService.insert(feedUrl, myTag);

Note that this can also be done in bulk as described in the Update a photo section using the <media:keywords> element.


Deleting a tag from a photo


Deleting a tag is done by using the delete method on the tag entry.

 
myTag.delete();

Working with Comments


Comments are short text snippets attached to photos by Picasa Web Albums users.


Retrieving a list of comments


The following example prints out the most recent comments on username's photos.


The string "default" can be used in place of a real username, in which case the server will use the username of the user credentials used to authenticate the request.

 
URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/username?kind=comment");

AlbumFeed myFeed = myService.query(myQuery, AlbumFeed.class);

for(PhotoEntry comment : feed.getPhotoEntries()) {
System.out.println(comment.getTitle().getPlainText());
}

List comments by photo


You can also retrieve all of the comments associated with a particular photo. The following example prints out all of the comments on the photo identified by photoid, inside of the albumid album, owned by the username user. The Accessing photo information section describes how to retrieve a valid value for photoid.

 
URL feedUrl = new
URL("http://picasaweb.google.com/data/feed/api/user/username/albumid/albumid/photoid/photoid?kind=comment");

PhotoFeed searchResultsFeed = myService.getFeed(feedUrl, PhotoFeed.class);

for (CommentEntry comment : searchResultsFeed.getCommentEntries()) {
System.out.println(comment.getPlainTextContent());
}

Add comments to a photo


The following code adds the comment "great photo!" to the photo identified by photoid in the albumid owned by username.

 
URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/username/albumid/albumid/photoid/photoid");

CommentEntry myComment = new CommentEntry();
myComment.setContent(new PlainTextConstruct("great photo!"));

myService.insert(feedUrl, myComment);

Delete a comment from a photo


Deleting a comment is done by using the delete method.

 
myComment.delete();

Improving performance


Some special features are available to help you optimize your Picasa Web Albums Data API requests.


Retrieving specific field (Experimental )


When you use the Java Client Library to work with the Picasa Web Albums Data API, you are actually making underlying requests that transfer XML data using the Google Data Protocol. By default, requests that return data transfer the full representation of the target resource. Yet receiving full responses to requests can consume a lot of network and CPU resources, especially when you're making many requests. To reduce resource use, you can request that the server return only certain fields for each returned event.


To request only parts of an album instead of the full album, use the Query.setFields method. For more information, including the field-specification syntax, see the partial-response documentation.


For example, the following code retrieves the title, ID, and location fields for each of the user's albums, using a partial request.

 
String albumsUrl = "http://picasaweb.google.com/data/feed/api/user/username";

// Selection criteria to fetch only title, gphoto:id, and gphoto:location fields for each album.
String fields = "entry(title,gphoto:id,gphoto:location)";

Query albumQuery = new Query(new URL(albumsUrl));
albumQuery.setFields(fields);

AlbumFeed partialFeed = service.query(albumQuery, AlbumFeed.class);
for (GphotoEntry partialEntry : partialFeed.getEntries()) {
if (partialEntry instanceof AlbumEntry) {
AlbumEntry partialAlbumEntry = (AlbumEntry) partialEntry;
System.out.println(String.format("%s: [title - %s, location - %s]", partialAlbumEntry.getGphotoId(), partialAlbumEntry.getTitle().getPlainText(), partialAlbumEntry.getLocation()));
}
}

Note: The fields you specify with the Query.setFields method are expressed in terms of the Google Data Protocol's AtomPub XML representation for the Picasa Web Albums Data API. For more information about these fields, please see the protocol and the element reference documentation.


Updating specific fields (Experimental )


When you change a field in a local copy of an album, you can send the server just the updated field, rather than sending the entire updated entry. For more information, see the partial-update documentation.


The following code updates an album's location using partial update. In this example we are updating the gphoto:location for one of the albums retrieved using partial response.

 
String albumEntryUrl = "http://picasaweb.google.com/data/feed/api/user/username/albumid";
// Retrieve only the ETag and location attributes for the album to be updated.
String fields = "@gd:etag,gphoto:location";
Query patchQuery = new Query(new URL(albumEntryUrl));
patchQuery.setFields(fields);
AlbumEntry partialEntry = service.getEntry(patchQuery.getUrl(), AlbumEntry.class);
System.out.println("Current location: " + partialEntry.getLocation());

// Update the location in the album entry.
partialEntry.setLocation(newLocation);

AlbumEntry updated = service.patch(new URL(albumEntryUrl), fields, partialEntry);
System.out.println("Location set to: " + updated.getLocation());

Conclusion


Hopefully, you now have a good idea of how to get started using the Picasa Web Albums API! If you have questions, comments, or noticed a typo in this document, let us know in the forum.


http://code.google.com/apis/picasaweb/docs/2.0/developers_guide_java.html