| <!-- |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| |
| --> |
| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> |
| <html> |
| <head> |
| <title>Preferences API in NetBeans</title> |
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
| </head> |
| <body> |
| <h1 align="left">Preferences API in NetBeans</h1> |
| NetBeans adopts <a href="http://java.sun.com/j2se/1.5.0/docs/guide/preferences/">Java Preferences API</a> |
| standard to be used in NetBeans to |
| store preference and configuration data to be able to adapt to the needs |
| of different users. NetBeans keeps this standard and enhances it slightly |
| by providing its own preference tree in addition to default system and user |
| roots. The purpose for this enhancement is to |
| unify the way how persistent preference and configuration data are stored in NetBeans |
| whereas all changes go into userdir which is the central place where also |
| other configuration data are kept.This concept is convenient for developers |
| or power users for running more instances of NetBeans, for debugging, |
| for testing, for analysing structure or content of such files whenever any |
| problems occures whereas running NetBeans with various userdirs means |
| actually running NetBeans with different preferences. Running NetBeans with |
| fresh userdir means actually running NetBeans with factory preset |
| defaults.<P> |
| The decision whether to use standard or enhanced NetBeans preference tree is left |
| up to the developer. |
| <h3 align="left">Usage Notes</h3> |
| <h4 align="left">How to obtain NetBeans Preferences?</h4> |
| To get preference node there are two <A |
| HREF="@org-openide-util@/org/openide/util/NbPreferences.html">factory |
| methods in Utilities API (org.openide.util)</A>: |
| <ul> |
| <li><code>NbPreferences.root()</code> returns root preference node. </li> |
| <li><code>NbPreferences.forModule(class cls)</code> returns |
| preference node which <a |
| href="@JDK@/java/util/prefs/Preferences.html#absolutePath%28%29">absolute path</a> |
| depends whether class provided as a parameter was loaded as a |
| part of any module or not. If so, then absolute path corresponds to |
| slashified code name base of module. If not, then absolute path |
| corresponds to class's package </li> |
| </ul> |
| <h4 align="left">How to verify that preferences work properly?</h4> |
| Layout of files on disk:<br> |
| <p> </p> |
| <pre> |
| config/Preferences/ |
| `-- org |
| |-- apache |
| | `-- tools |
| | `-- ant |
| | `-- module.properties |
| `-- netbeans |
| `-- modules |
| `-- derby.properties |
| </pre> |
| |
| <p> prefs.properties is regular properties file. See: </p> |
| <p> </p> |
| <pre>#Thu Sep 28 18:15:22 CEST 2006<br>location=/tmp/location<br>systemHome=/tmp/systemHome<br> </pre> |
| <H4 ALIGN=LEFT>How to migrate from SystemOptions into Preferences API?</H4> |
| <OL> |
| <LI>Remove registration from layer: |
| <PRE> |
| - <folder name="Services"/> |
| - <file name="org-netbeans-modules-derby-DerbyOptions.settings" url="DerbyOptions.settings"/> |
| - </folder> |
| </PRE> |
| <LI>Add module dependency on Utilities API (org.openide.util with specification |
| version >= 7.4) if necessary |
| <LI>Remove module dependency on Settings Options API (org.openide.options) |
| <LI>Remove registration of Node which was used to visualize original |
| SystemOptions in Tools/Options |
| <PRE> |
| - <folder name="Services"> |
| - <folder name="IDEConfiguration"> |
| - <folder name="ServerAndExternalToolSettings"> |
| - <file name="org-netbeans-modules-derby-DerbyOptions.shadow"> |
| - <attr name="originalFile" stringvalue="Services/org-netbeans-modules-derby-DerbyOptions.settings"/> |
| - </file> |
| - </folder> |
| - </folder> |
| - </folder> |
| |
| </PRE> |
| <LI>Rewrite the code not to extend SystemOption anymore |
| <LI>Ensure visualization in Tools/Options if still needed. |
| <LI>Ensure import from previous version into preferences storage |
| <LI>Delete associated BeanInfo if it isn't necessary anymore |
| <LI>Document usage of Preferences API like any other API in arch document |
| <LI>Run tests to verify |
| that code after migration is working |
| </OL> |
| <H4 ALIGN=LEFT>Hints how to replace SystemOption </H4> |
| It is recommended to delete the whole settings class extending SystemOption |
| and call directly Preferences API. This approach isn't convenient when |
| except reading, writing from storage is necessary additional logic. Then |
| following steps can be used as a hint: |
| <OL> |
| <LI>Change your setting class not to extend SystemOption anymore |
| <LI>Either use static methods for getters and setters or replace factory method SharedClassObject.findObject by common singleton |
| pattern: |
| <PRE> |
| -public class DerbyOptions extends SystemOption { |
| - |
| - private static final long serialVersionUID = 1101894610105398924L; |
| +public class DerbyOptions { |
| + private static DerbyOptions INSTANCE = new DerbyOptions(); |
| |
| public static DerbyOptions getDefault() { |
| - return (DerbyOptions)SharedClassObject.findObject(DerbyOptions.class, true); |
| + return INSTANCE; |
| } |
| </PRE> |
| <LI>Replace all other SystemOption and SharedClassObject calls. E.g.: |
| <PRE> |
| + protected final String putProperty(String key, String value, boolean notify) { |
| + String retval = getPreferences().get(key, null); |
| + if (value != null) { |
| + NbPreferences.forModule(DerbyOptions.class).put(key, value); |
| + } else { |
| + NbPreferences.forModule(DerbyOptions.class).remove(key); |
| + } |
| + return retval; |
| + } |
| |
| + protected final String getProperty(String key) { |
| + return NbPreferences.forModule(DerbyOptions.class).get(key, null); |
| + } |
| </PRE> |
| </OL> |
| <H4 ALIGN=LEFT>How to ensure visualization in Tools/Options?</H4> |
| If you need keep UI backward compatibility then the way is very easy: |
| provide a node representing your settings class. See: |
| <PRE> |
| |
| + public static BeanNode createViewNode() throws IntrospectionException { |
| + return new BeanNode(DerbyOptions.getDefault()); |
| + } |
| |
| - <file name="org-netbeans-modules-derby-DerbyOptions.shadow"> |
| - <attr name="originalFile" stringvalue="Services/org-netbeans-modules-derby-DerbyOptions.settings"/> |
| + <file name="DerbyOptionsNode.instance"> |
| + <attr name="instanceCreate" methodvalue="org.netbeans.modules.derby.DerbyOptions.createViewNode"/> |
| |
| </PRE> |
| The other way means to go a little more farther and provide and install custom |
| options panels/categories to Options Dialog according to the |
| <A HREF="@org-netbeans-modules-options-api@/overview-summary.html">Options Dialog API</A>. |
| The module development support in NetBeans can help by generating boilerplate code and |
| registering the option into your layer. |
| <H4 ALIGN=LEFT>How to document usage of Preferences API?</H4> |
| Answer properly arch questions like any other <A |
| HREF="http://openide.netbeans.org/tutorial/api-design.html">APIs</A>. There was added new arch question related to |
| preferences (resources-preferences). See example: |
| <PRE> |
| <answer id="resources-preferences"> |
| <api group="preferences" name="org.netbeans.modules.derby" type="export" category="private" url=""> |
| <table> |
| <tbody> |
| <tr> |
| <th>key</th> |
| <th>description</th> |
| <th>read</th> |
| <th>write</th> |
| </tr> |
| <tr> |
| <td>location</td> |
| <td>Derby location or an empty string if the Derby location is not set</td> |
| <td>x</td> |
| <td>x</td> |
| </tr> |
| <tr> |
| <td>systemHome</td> |
| <td>Derby system home or an empty string if the system home is not set</td> |
| <td>x</td> |
| <td>x</td> |
| </tr> |
| </tbody> |
| </table> |
| </api> |
| </answer> |
| </PRE> |
| <H4 ALIGN=LEFT>How to ensure import from previous version?</H4> |
| There is support for importing old serialized instances of SystemOptions into |
| preferences storage in ide/launcher/upgrade module in package |
| org.netbeans.upgrade.systemoptions. This infrastructure reads configuration file |
| containing enumeration of settings files that should be imported. In simple |
| cases there is actually no neeed to code anything, but this is rare case which |
| may happen just when all the properties are primitive data types or String and |
| if method writeExternal isn't overridden.<P> |
| How to start? The best is to start with tests. But first you must have settings |
| file that will be parsed and tested. Put this file among tests: |
| <PRE> |
| test/unit/src/org/netbeans/upgrade/systemoptions |
| |-- BasicTestForImport.java |
| |-- DerbyOptionsTest.java |
| |-- org-netbeans-modules-derby-DerbyOptions.settings |
| </PRE> |
| Then write simple test that should contain assertions what actually will be |
| parsed and imported. See: |
| <PRE> |
| public class DerbyOptionsTest extends BasicTestForImport { |
| public DerbyOptionsTest(String testName) { |
| super(testName, "org-netbeans-modules-derby-DerbyOptions.settings"); |
| } |
| public void testPreferencesNodePath() throws Exception { |
| assertPreferencesNodePath("/org/netbeans/modules/derby"); |
| } |
| public void testPropertyNames() throws Exception { |
| assertPropertyNames(new String[] { |
| "systemHome", |
| "location" |
| }); |
| } |
| public void testSystemHome() throws Exception { |
| assertPropertyType("systemHome","java.lang.String"); |
| assertProperty("systemHome","/tmp/systemHome"); |
| } |
| public void testLocation() throws Exception { |
| assertPropertyType("location","java.lang.String"); |
| assertProperty("location","/tmp/location"); |
| } |
| } |
| </PRE> |
| If the tests didn't pass then probably there is more complicated object graph |
| serialized then you must subclass <CODE>PropertyProcessor</CODE> and put your |
| own code in. |
| (See as an example: <A HREF="http://www.netbeans.org/source/browse/ide/launcher/upgrade/src/org/netbeans/upgrade/systemoptions/TaskTagsProcessor.java?view=markup">TaskTagsProcessor</A> |
| and here is a <A |
| HREF="http://www.netbeans.org/source/browse/ide/launcher/upgrade/test/unit/src/org/netbeans/upgrade/systemoptions/ExampleSettingsTest.java?view=markup">test</A>). |
| <H4 ALIGN=LEFT>How to write tests that needs Preferences API?</H4> |
| Not persistent implementation of <code>java.util.prefs.Preferences</code> |
| is installed in place of the platform-specific default implementation for |
| running tests which is in spirit of unit testing because individual tests |
| shouldn't interact. But if this behaviour isn't suitable then there is possible |
| to reinstall platform-specific default implementation again: |
| <PRE> |
| + public void run(final TestResult result) { |
| + //just initialize Preferences before code NbTestCase |
| + Preferences.userRoot(); |
| + super.run(result); |
| + } |
| </PRE> |
| </body> |
| </html> |