Java Web Start Persistence and JList Striping: Core Java Technologies Tech Tips In This Issue Welcome to the Core Java Technologies Tech Tips for November 2006. Core Java Technologies Tech Tips provide tips and hints for using core Java technologies and APIs provided in the Java 2 Platform, Standard Edition (Java SE). This issue provides tips for the following: * Java Web Start Persistence * JList Striping These tips were developed using the Java 2 Platform, Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 from the Java SE Downloads page: http://java.sun.com/javase/downloads/index.jsp See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. Java Web Start Persistence by Joshua Marinacci, NetBeans Engineer Java Web Start is a great technology for deploying lightweight client applications. It even supports automatic updates. One of Web Start's less understood features is the PersistenceService API. This tip shows how to use the PersistenceService class create a click through license screen. First a quick recap: What is Java Web Start? Web Start is a technology built into the Java platform that lets you deploy lightweight applications. First the user downloads an XML file that describes the application, lists the required jars, and optionally asks for full desktop permissions. Once everything is downloaded, the application will start. If your application asks for permissions, the user will see a warning dialog. If your app does not request permissions, it will run in a secure sandbox, much like an applet. Running in the sandbox is the proper, safe, and secure way to write a Java Web Start application. If your application is inside a secure sandbox, how can it do unsafe things like save data to disk? That's were Java Web Start's Service APIs come in. This tip describes one service provided by the PersistenceService class. The Web Start environment provides the PersistenceService class that allows your application to safely store data without having full access to your hard-drive. The service limits the data size, and only your application can read or write the data. This means that other applications on your hard-drive will not be able to access information stored by your application. This security is the key to making Java Web Start a safe application platform. This example code shows a license approval dialog. If the user accepts the license, the code saves a note about it so that the application won't show the license again. All services in the Web Start environment can be obtained using the ServiceManager class, as you can see in the example below: import javax.jnlp.*; //.... public void showDialog() { // show the dialog. after the user accepts do the following try { PersistenceService service = (PersistenceService) ServiceManager.lookup("javax.jnlp.PersistenceService"); service.create(new URL("http://www.myserver.com/CoolApp/ApprovedLicense"),100); print("created the license"); } catch (Throwable thr) { print(thr); } } public static void print(Object o) { System.out.println(o); if(o instanceof Throwable) { ((Throwable)o).printStackTrace(); } } Obtain a PersistenceService instance by providing the service's class name, "javax.jnlp.PersistenceService", to the ServiceManager.lookup() method. Once you have a reference to the service, you can get create a storage entry using the create() method, which takes two arguments. The first is a reference URL. This URL should match the URL your application will be loaded from. The second argument indicates how much size you want in bytes. Since this sample code doesn't actually need to store any data, the data size is a low number: 100. Now that you have created a storage entry, how do you use it? The code above will create an empty entry. Since the entry's contents are irrelevant, you can simply check that it exists by calling the get method of the PersistenceService instance. If the data entry doesn't exist, the service will throw a FileNotFoundException object, which means either the user didn't accept the license or this is the first time the application has run. In either case we should show the license. If you want to load data from the storage entry, you can use the FileContents object that returns from the service.get() method. public void checkLicenseApproved() { try { PersistenceService service = (PersistenceService) ServiceManager.lookup("javax.jnlp.PersistenceService"); FileContents contents = service.get(new URL("http://www.myserver.com/CoolApp/ApprovedLicense")); print("The user has already approved the license"); } catch (FileNotFoundException fnfe) { print("need to show the license"); showDialog(); } catch (Throwable ex) { print(ex); } } That's all you need to conditionally show a license panel. Notice that you must use the same URL for both storing and loading data from the persistence store. If you use the wrong URL, a MalformedURLException object will be thrown. The Web Start environment uses this URL to enforce security among applications, ensuring that only your application can access your data. The PersistenceService class is one of many services provided by the Java Web Start deployment system. For more information on Java Web Start please read the following: Java Web Start documentation: http://java.sun.com/j2se/1.5.0/docs/guide/javaws/index.html Java Web Start services: http://java.sun.com/j2se/1.5.0/docs/guide/javaws/jnlp/index.html JList Striping by Joshua Marinacci, NetBeans Engineer One of the great things about Swing is that you can customize your components to look like whatever you want. It has become quite common to add a slightly custom look to standard desktop applications. In iTunes, for example, song lists look mostly normal but have some very subtle changes that make the interface look nicer. This tip will show you how to style a standard Swing list with an alternating stripe background and a gradient shaded selection. The JList class, like JTable and JTree, can be customized using a cell renderer. It is essentially a rubber stamp that the list calls over and over to draw each item in the list. Primarily this is used by operating system specific Look and Feels to make JLists match their native counterparts. However, you can write your own cell renderer to style a JList any way you want. All you have to do is subclass the DefaultListCellRenderer class and override the getListCellRendererComponent() method. In order to stripe the list cells, you must change the background color for every other row. Here's a simple cell renderer which will do just that. import java.awt.*; import javax.swing.*; class StripeRenderer extends DefaultListCellRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if(index%2 == 0) { label.setBackground(new Color(230,230,255)); } return label; } } This class simply calls its parent's version of the getListCellRendererComponent() method, then sets the background color to light blue if the index is an even number, and then returns the label. The index value represents the row number of the item in the list. The cast to JLabel is safe because this is a subclass of DefaultListCellRenderer which is guaranteed to always return a JLabel subclass. You can set this class as the renderer for your JList like this: list.setCellRenderer(new StripeRenderer()); Figure 1 shows the finished renderer. http://java.sun.com/mailers/techtips/corejava/2006/img/tech-tip.even-odd-renderer.png You can also create background gradients for selected list cells. Since you cannot create a gradient by simply setting the background color of the label, you must implement the list cell drawing yourself. The DefaultListCellRenderer class is itself a JLabel, and when you call getListCellRendererComponent() it returns a reference to itself, after doing some setup work. This means that you can customize the drawing by subclassing DefaultListCellRenderer and overriding the paintComponent(Graphics) method. Here's what the code looks like: class GradientSelectionRenderer extends DefaultListCellRenderer { private boolean drawGradient = false; public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); drawGradient = false; label.setOpaque(true); if(isSelected) { drawGradient = true; label.setForeground(Color.WHITE); label.setBackground(Color.BLUE); label.setOpaque(false); } return label; } protected void paintComponent(Graphics g) { if(drawGradient) { Graphics2D gfx = (Graphics2D)g.create(); GradientPaint gradient = new GradientPaint(0,0, Color.BLUE, 0,10,Color.BLACK); gfx.setPaint(gradient); gfx.fillRect(0,0,getWidth(),getHeight()); gfx.dispose(); } super.paintComponent(g); } } Notice the drawGradient boolean field. If the current row is selected (indicated by the isSelected argument to getListCellRendererComponent()), then it will set the drawGradient boolean to true so that the painting code knows to draw the gradient. The gradient is created using a simple GradientPaint object that draws blue to black going from top to bottom. Notice the call to label.setOpaque(false). This tells the label to turn off its background. Without this the normal background would be drawn, hiding the custom one. If you set this class as your list cell renderer then it will look like Figure 2: http://java.sun.com/mailers/techtips/corejava/2006/img/tech-tip.gradient-selection-renderer.png Figure 2. JList with GradientSelectionRenderer These two renderers look pretty cool, but wouldn't it be great if you could have both effects at the same time? You could combine the two into a single class, but there's an easier way. You can make the StripeRenderer class return an instance of the GradientSelectionRenderer only if the current item is selected and then return the StripeRenderer for any other item. You just need to store the GradientSelectionRenderer as a field inside the StripeRenderer and then use it when the current item is selected. class StripeRenderer extends DefaultListCellRenderer { GradientSelectionRenderer gradient = new GradientSelectionRenderer(); public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if(isSelected) { return gradient.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); } // else, do the usual stripe renderer stuff ... } } ... list.setCellRenderer(new StripeRenderer()); http://java.sun.com/mailers/techtips/corejava/2006/img/tech-tip.combined-renderer.png Figure 3. JList with GradientSelectionRenderer That's it. The two renderers are now combined into one. Cell renderers are a great way to spice up any normal application without requiring significant changes. They can really make your program pop! For more on lists and list cell renderers see the following documents: How to Use Lists: http://java.sun.com/docs/books/tutorial/uiswing/components/list.html The Java Tutorial http://java.sun.com/docs/books/tutorial/index.html ---- DEVELOPER ASSISTANCE Need programming advice on Java SE? Try Developer Expert Assistance (http://developers.sun.com/services/expertassistance/) If you have your own Tech Tip that you would like to share with others, you're encouraged to post it in an appropriate Sun Developer Network forum (http://forum.java.sun.com/index.jspa). IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://www.sun.com/privacy/ http://developers.sun.com/dispatcher.jsp?uid=6910008 FEEDBACK Comments? Please enter your feedback on the Tech Tips at: http://developers.sun.com/contact/feedback.jsp?category=newslet SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: * Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE). * Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME). To subscribe to these and other JDC publications: Go to the Sun Developer Network - Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to and click "Submit". To unsubscribe, go to the Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click "Submit". ARCHIVES You'll find the Core Java Technologies Tech Tips archives at: http://java.sun.com/developer/TechTips/index.html COPYRIGHT Copyright 2006 Sun Microsystems, Inc. All rights reserved. 4150 Network Circle, Santa Clara, California 95054 USA. This document is protected by copyright. For more information, see: http://java.sun.com/developer/copyright.html Core Java Technologies Tech Tips September 2006 Trademark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.