You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by fm...@apache.org on 2007/04/11 20:12:35 UTC
svn commit: r527592 [1/2] - in /incubator/felix/trunk/configadmin: ./ src/
src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/
src/main/java/org/apache/felix/ src/main/java/org/apache/felix/cm/
src/main/java/org/apache/felix/cm/file/ ...
Author: fmeschbe
Date: Wed Apr 11 11:12:33 2007
New Revision: 527592
URL: http://svn.apache.org/viewvc?view=rev&rev=527592
Log:
FELIX-11 Implement Configuration Admin (Initial Checkin)
Added:
incubator/felix/trunk/configadmin/ (with props)
incubator/felix/trunk/configadmin/pom.xml (with props)
incubator/felix/trunk/configadmin/src/
incubator/felix/trunk/configadmin/src/main/
incubator/felix/trunk/configadmin/src/main/java/
incubator/felix/trunk/configadmin/src/main/java/org/
incubator/felix/trunk/configadmin/src/main/java/org/apache/
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/PersistenceManager.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/FilePersistenceManager.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminFactory.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/Factory.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/PersistenceManagerProxy.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/RankingComparator.java (with props)
incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/UpdateThread.java (with props)
incubator/felix/trunk/configadmin/src/test/
incubator/felix/trunk/configadmin/src/test/java/
incubator/felix/trunk/configadmin/src/test/java/org/
incubator/felix/trunk/configadmin/src/test/java/org/apache/
incubator/felix/trunk/configadmin/src/test/java/org/apache/felix/
incubator/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/
incubator/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/MockBundleContext.java (with props)
incubator/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/file/
incubator/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/file/FileNameTest.java (with props)
incubator/felix/trunk/configadmin/src/test/java/org/apache/felix/cm/file/FilePersistenceManagerTest.java (with props)
Propchange: incubator/felix/trunk/configadmin/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Wed Apr 11 11:12:33 2007
@@ -0,0 +1,4 @@
+.classpath
+.project
+.settings
+target
Added: incubator/felix/trunk/configadmin/pom.xml
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/configadmin/pom.xml?view=auto&rev=527592
==============================================================================
--- incubator/felix/trunk/configadmin/pom.xml (added)
+++ incubator/felix/trunk/configadmin/pom.xml Wed Apr 11 11:12:33 2007
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix</artifactId>
+ <version>0.9.0-incubator-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>org.apache.felix.cm</artifactId>
+ <packaging>bundle</packaging>
+
+ <name>Apache Felix Configuration Admin Service</name>
+ <description>
+ Implementation of the OSGi Configuration Admin Service Specification 1.2
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>0.9.0-incubator-SNAPSHOT</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Category>osgi</Bundle-Category>
+ <Export-Package>
+ org.apache.felix.cm,
+ org.apache.felix.cm.file,
+ org.osgi.service.cm;version=1.2
+ </Export-Package>
+ <Private-Package>
+ org.apache.felix.cm.*
+ </Private-Package>
+ <Bundle-Activator>
+ org.apache.felix.cm.impl.ConfigurationManager
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
Propchange: incubator/felix/trunk/configadmin/pom.xml
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/PersistenceManager.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/PersistenceManager.java?view=auto&rev=527592
==============================================================================
--- incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/PersistenceManager.java (added)
+++ incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/PersistenceManager.java Wed Apr 11 11:12:33 2007
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+package org.apache.felix.cm;
+
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+
+/**
+ * The <code>PersistenceManager</code> interface defines the API to be
+ * implemented to support persisting configuration data. This interface may
+ * be implemented by bundles, which support storing configuration data in
+ * different locations.
+ * <p>
+ * The Apache Felix Configuration Admin Service bundles provides an
+ * implementation of this interface using the platform filesystem to store
+ * configuration data.
+ * <p>
+ * Implementations of this interface must support loading and storing
+ * <code>java.util.Dictionary</code> objects as defined in section 104.4.2,
+ * Configuration Properties, of the Configuration Admin Service Specification
+ * Version 1.2.
+ * <p>
+ * To make implementations of this interface available to the Configuration
+ * Admin Service they must be registered as service for this interface. The
+ * Configuration Admin Service will consider all registered services plus the
+ * default platform file system based implementation to load configuration data.
+ * To store new configuration data, the persistence manager service with the
+ * highest rank value - the <code>service.ranking</code> service property - is
+ * used. If no pesistence manager service has been registered, the platfrom
+ * file system based implementation is used.
+ *
+ * @author fmeschbe
+ */
+public interface PersistenceManager
+{
+
+ /**
+ * Returns <code>true</code> if a persisted <code>Dictionary</code> exists
+ * for the given <code>pid</code>.
+ *
+ * @param pid The identifier for the dictionary to test.
+ */
+ boolean exists( String pid );
+
+
+ /**
+ * Returns the <code>Dictionary</code> for the given <code>pid</code>.
+ *
+ * @param pid The identifier for the dictionary to load.
+ *
+ * @return The dictionary for the identifier. This must not be
+ * <code>null</code> but may be empty.
+ *
+ * @throws IOException If an error occurrs loading the dictionary. An
+ * <code>IOException</code> must also be thrown if no dictionary
+ * exists for the given identifier.
+ */
+ Dictionary load( String pid ) throws IOException;
+
+
+ /**
+ * Returns an enumeration of all <code>Dictionary</code> objects known to
+ * this persistence manager.
+ * <p>
+ * Implementations of this method are allowed to return lazy enumerations.
+ * That is, it is allowable for the enumeration to not return a dictionary
+ * if loading it results in an error.
+ *
+ * @return A possibly empty Enumeration of all dictionaries.
+ *
+ * @throws IOException If an error occurrs getting the dictionaries.
+ */
+ Enumeration getDictionaries() throws IOException;
+
+
+ /**
+ * Stores the <code>Dictionary</code> under the given <code>pid</code>.
+ *
+ * @param pid The identifier of the dictionary.
+ * @param properties The <code>Dictionary</code> to store.
+ *
+ * @throws IOException If an error occurrs storing the dictionary. If this
+ * exception is thrown, it is expected, that
+ * {@link #exists(String) exists(pid} returns <code>false</code>.
+ */
+ void store( String pid, Dictionary properties ) throws IOException;
+
+
+ /**
+ * Removes the <code>Dictionary</code> for the given <code>pid</code>. If
+ * such a dictionary does not exist, this method has no effect.
+ *
+ * @param pid The identifier of the dictionary to delet.
+ *
+ * @throws IOException If an error occurrs deleting the dictionary. This
+ * exception must not be thrown if no dictionary with the given
+ * identifier exists.
+ */
+ void delete( String pid ) throws IOException;
+
+}
\ No newline at end of file
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/PersistenceManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/PersistenceManager.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java?view=auto&rev=527592
==============================================================================
--- incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java (added)
+++ incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java Wed Apr 11 11:12:33 2007
@@ -0,0 +1,796 @@
+/*
+ * 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.
+ */
+package org.apache.felix.cm.file;
+
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PushbackReader;
+import java.io.Writer;
+import java.lang.reflect.Array;
+import java.util.BitSet;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+
+
+/**
+ * The <code>ConfigurationHandler</code> class implements configuration reading
+ * form a <code>java.io.InputStream</code> and writing to a
+ * <code>java.io.OutputStream</code> on behalf of the
+ * {@link FilePersistenceManager} class.
+ *
+ * <pre>
+ cfg = prop "=" value .
+ prop = symbolic-name . // 1.4.2 of OSGi Core Specification
+ symbolic-name = token { "." token } .
+ token = { [ 0..9 ] | [ a..z ] | [ A..Z ] | '_' | '-' } .
+ value = [ type ] ( "[" values "]" | "(" values ")" | simple ) .
+ values = simple { "," simple } .
+ simple = """ stringsimple """ .
+ type = // 1-char type code .
+ stringsimple = // quoted string representation of the value .
+ * </pre>
+ *
+ * @author fmeschbe
+ */
+public class ConfigurationHandler
+{
+ protected static final String ENCODING = "UTF-8";
+
+ protected static final int TOKEN_NAME = 'N';
+ protected static final int TOKEN_EQ = '=';
+ protected static final int TOKEN_ARR_OPEN = '[';
+ protected static final int TOKEN_ARR_CLOS = ']';
+ protected static final int TOKEN_VEC_OPEN = '(';
+ protected static final int TOKEN_VEC_CLOS = ')';
+ protected static final int TOKEN_COMMA = ',';
+ protected static final int TOKEN_VAL_OPEN = '"'; // '{';
+ protected static final int TOKEN_VAL_CLOS = '"'; // '}';
+
+ // simple types (string & primitive wrappers)
+ protected static final int TOKEN_SIMPLE_STRING = 'T';
+ protected static final int TOKEN_SIMPLE_INTEGER = 'I';
+ protected static final int TOKEN_SIMPLE_LONG = 'L';
+ protected static final int TOKEN_SIMPLE_FLOAT = 'F';
+ protected static final int TOKEN_SIMPLE_DOUBLE = 'D';
+ protected static final int TOKEN_SIMPLE_BYTE = 'X';
+ protected static final int TOKEN_SIMPLE_SHORT = 'S';
+ protected static final int TOKEN_SIMPLE_CHARACTER = 'C';
+ protected static final int TOKEN_SIMPLE_BOOLEAN = 'B';
+
+ // primitives
+ protected static final int TOKEN_PRIMITIVE_INT = 'i';
+ protected static final int TOKEN_PRIMITIVE_LONG = 'l';
+ protected static final int TOKEN_PRIMITIVE_FLOAT = 'f';
+ protected static final int TOKEN_PRIMITIVE_DOUBLE = 'd';
+ protected static final int TOKEN_PRIMITIVE_BYTE = 'x';
+ protected static final int TOKEN_PRIMITIVE_SHORT = 's';
+ protected static final int TOKEN_PRIMITIVE_CHAR = 'c';
+ protected static final int TOKEN_PRIMITIVE_BOOLEAN = 'b';
+
+ protected static final String CRLF = "\r\n";
+
+ protected static final Map type2Code;
+ protected static final Map code2Type;
+
+ // set of valid characters for "symblic-name"
+ private static final BitSet NAME_CHARS;
+ private static final BitSet TOKEN_CHARS;
+
+ static
+ {
+ type2Code = new HashMap();
+
+ // simple (exclusive String whose type code is not written)
+ type2Code.put( Integer.class, new Integer( TOKEN_SIMPLE_INTEGER ) );
+ type2Code.put( Long.class, new Integer( TOKEN_SIMPLE_LONG ) );
+ type2Code.put( Float.class, new Integer( TOKEN_SIMPLE_FLOAT ) );
+ type2Code.put( Double.class, new Integer( TOKEN_SIMPLE_DOUBLE ) );
+ type2Code.put( Byte.class, new Integer( TOKEN_SIMPLE_BYTE ) );
+ type2Code.put( Short.class, new Integer( TOKEN_SIMPLE_SHORT ) );
+ type2Code.put( Character.class, new Integer( TOKEN_SIMPLE_CHARACTER ) );
+ type2Code.put( Boolean.class, new Integer( TOKEN_SIMPLE_BOOLEAN ) );
+
+ // primitives
+ type2Code.put( Integer.TYPE, new Integer( TOKEN_PRIMITIVE_INT ) );
+ type2Code.put( Long.TYPE, new Integer( TOKEN_PRIMITIVE_LONG ) );
+ type2Code.put( Float.TYPE, new Integer( TOKEN_PRIMITIVE_FLOAT ) );
+ type2Code.put( Double.TYPE, new Integer( TOKEN_PRIMITIVE_DOUBLE ) );
+ type2Code.put( Byte.TYPE, new Integer( TOKEN_PRIMITIVE_BYTE ) );
+ type2Code.put( Short.TYPE, new Integer( TOKEN_PRIMITIVE_SHORT ) );
+ type2Code.put( Character.TYPE, new Integer( TOKEN_PRIMITIVE_CHAR ) );
+ type2Code.put( Boolean.TYPE, new Integer( TOKEN_PRIMITIVE_BOOLEAN ) );
+
+ // reverse map to map type codes to classes, string class mapping
+ // to be added manually, as the string type code is not written and
+ // hence not included in the type2Code map
+ code2Type = new HashMap();
+ for ( Iterator ti = type2Code.entrySet().iterator(); ti.hasNext(); )
+ {
+ Map.Entry entry = ( Map.Entry ) ti.next();
+ code2Type.put( entry.getValue(), entry.getKey() );
+ }
+ code2Type.put( new Integer( TOKEN_SIMPLE_STRING ), String.class );
+
+ NAME_CHARS = new BitSet();
+ for ( int i = '0'; i <= '9'; i++ )
+ NAME_CHARS.set( i );
+ for ( int i = 'a'; i <= 'z'; i++ )
+ NAME_CHARS.set( i );
+ for ( int i = 'A'; i <= 'Z'; i++ )
+ NAME_CHARS.set( i );
+ NAME_CHARS.set( '_' );
+ NAME_CHARS.set( '-' );
+ NAME_CHARS.set( '.' );
+
+ TOKEN_CHARS = new BitSet();
+ TOKEN_CHARS.set( TOKEN_EQ );
+ TOKEN_CHARS.set( TOKEN_ARR_OPEN );
+ TOKEN_CHARS.set( TOKEN_ARR_CLOS );
+ TOKEN_CHARS.set( TOKEN_VEC_OPEN );
+ TOKEN_CHARS.set( TOKEN_VEC_CLOS );
+ TOKEN_CHARS.set( TOKEN_COMMA );
+ TOKEN_CHARS.set( TOKEN_VAL_OPEN );
+ TOKEN_CHARS.set( TOKEN_VAL_CLOS );
+ TOKEN_CHARS.set( TOKEN_SIMPLE_STRING );
+ TOKEN_CHARS.set( TOKEN_SIMPLE_INTEGER );
+ TOKEN_CHARS.set( TOKEN_SIMPLE_LONG );
+ TOKEN_CHARS.set( TOKEN_SIMPLE_FLOAT );
+ TOKEN_CHARS.set( TOKEN_SIMPLE_DOUBLE );
+ TOKEN_CHARS.set( TOKEN_SIMPLE_BYTE );
+ TOKEN_CHARS.set( TOKEN_SIMPLE_SHORT );
+ TOKEN_CHARS.set( TOKEN_SIMPLE_CHARACTER );
+ TOKEN_CHARS.set( TOKEN_SIMPLE_BOOLEAN );
+
+ // primitives
+ TOKEN_CHARS.set( TOKEN_PRIMITIVE_INT );
+ TOKEN_CHARS.set( TOKEN_PRIMITIVE_LONG );
+ TOKEN_CHARS.set( TOKEN_PRIMITIVE_FLOAT );
+ TOKEN_CHARS.set( TOKEN_PRIMITIVE_DOUBLE );
+ TOKEN_CHARS.set( TOKEN_PRIMITIVE_BYTE );
+ TOKEN_CHARS.set( TOKEN_PRIMITIVE_SHORT );
+ TOKEN_CHARS.set( TOKEN_PRIMITIVE_CHAR );
+ TOKEN_CHARS.set( TOKEN_PRIMITIVE_BOOLEAN );
+ }
+
+
+ /**
+ * Writes the configuration data from the <code>Dictionary</code> to the
+ * given <code>OutputStream</code>.
+ * <p>
+ * This method writes at the current location in the stream and does not
+ * close the outputstream.
+ *
+ * @param out The <code>OutputStream</code> to write the configurtion data
+ * to.
+ * @param properties The <code>Dictionary</code> to write.
+ *
+ * @throws IOException If an error occurrs writing to the output stream.
+ */
+ public static void write( OutputStream out, Dictionary properties ) throws IOException
+ {
+ BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( out, ENCODING ) );
+
+ for ( Enumeration ce = properties.keys(); ce.hasMoreElements(); )
+ {
+ String key = ( String ) ce.nextElement();
+
+ // cfg = prop "=" value "." .
+ writeQuoted( bw, key );
+ bw.write( TOKEN_EQ );
+ writeValue( bw, properties.get( key ) );
+ bw.write( CRLF );
+ }
+
+ bw.flush();
+ }
+
+
+ /**
+ * Reads configuration data from the given <code>InputStream</code> and
+ * returns a new <code>Dictionary</code> object containing the data.
+ * <p>
+ * This method reads from the current location in the stream upto the end
+ * of the stream but does not close the stream at the end.
+ *
+ * @param ins The <code>InputStream</code> from which to read the
+ * configuration data.
+ *
+ * @return A <code>Dictionary</code> object containing the configuration
+ * data. This object may be empty if the stream contains no
+ * configuration data.
+ *
+ * @throws IOException If an error occurrs reading from the stream. This
+ * exception is also thrown if a syntax error is encountered.
+ */
+ public static Dictionary read( InputStream ins ) throws IOException
+ {
+ return new ConfigurationHandler().readInternal( ins );
+ }
+
+
+ // private constructor, this class is not to be instantiated from the outside
+ private ConfigurationHandler()
+ {
+ }
+
+ //---------- Configuration Input Implementation ---------------------------
+
+ private int token;
+ private String tokenValue;
+ private int line;
+ private int pos;
+
+
+ private Dictionary readInternal( InputStream ins ) throws IOException
+ {
+ BufferedReader br = new BufferedReader( new InputStreamReader( ins, ENCODING ) );
+ PushbackReader pr = new PushbackReader( br, 1 );
+
+ token = 0;
+ tokenValue = null;
+ line = 0;
+ pos = 0;
+
+ Hashtable configuration = new Hashtable();
+ token = 0;
+ while ( nextToken( pr ) == TOKEN_NAME )
+ {
+ String key = tokenValue;
+
+ // expect equal sign
+ if ( nextToken( pr ) != TOKEN_EQ )
+ {
+ throw readFailure( token, TOKEN_EQ );
+ }
+
+ // expect the token value
+ Object value = readValue( pr );
+ if ( value != null )
+ {
+ configuration.put( key, value );
+ }
+ }
+
+ return configuration;
+ }
+
+
+ /**
+ value = type ( "[" values "]" | "(" values ")" | simple ) .
+ values = value { "," value } .
+ simple = "{" stringsimple "}" .
+ type = // 1-char type code .
+ stringsimple = // quoted string representation of the value .
+ *
+ * @param pr
+ * @return
+ * @throws IOException
+ */
+ private Object readValue( PushbackReader pr ) throws IOException
+ {
+ // read (optional) type code
+ int type = read( pr );
+
+ // read value kind code if type code is not a value kinde code
+ int code;
+ if ( code2Type.containsKey( new Integer( type ) ) )
+ {
+ code = read( pr );
+ }
+ else
+ {
+ code = type;
+ type = TOKEN_SIMPLE_STRING;
+ }
+
+ switch ( code )
+ {
+ case TOKEN_ARR_OPEN:
+ return readArray( type, pr );
+
+ case TOKEN_VEC_OPEN:
+ return readVector( type, pr );
+
+ case TOKEN_VAL_OPEN:
+ return readSimple( type, pr );
+
+ default:
+ return null;
+ }
+ }
+
+
+ private Object readArray( int typeCode, PushbackReader pr ) throws IOException
+ {
+ Vector vector = new Vector();
+ for ( ;; )
+ {
+ if ( !checkNext( pr, TOKEN_VAL_OPEN ) )
+ {
+ return null;
+ }
+
+ Object value = readSimple( typeCode, pr );
+ if ( value == null )
+ {
+ // abort due to error
+ return null;
+ }
+
+ vector.add( value );
+
+ int c = read( pr );
+ if ( c == TOKEN_ARR_CLOS )
+ {
+ Class type = ( Class ) code2Type.get( new Integer( typeCode ) );
+ Object array = Array.newInstance( type, vector.size() );
+ for ( int i = 0; i < vector.size(); i++ )
+ {
+ Array.set( array, i, vector.get( i ) );
+ }
+ return array;
+ }
+ else if ( c < 0 )
+ {
+ return null;
+ }
+ else if ( c != TOKEN_COMMA )
+ {
+ return null;
+ }
+ }
+ }
+
+
+ private Vector readVector( int typeCode, PushbackReader pr ) throws IOException
+ {
+ Vector vector = new Vector();
+ for ( ;; )
+ {
+ if ( !checkNext( pr, TOKEN_VAL_OPEN ) )
+ {
+ return null;
+ }
+
+ Object value = readSimple( typeCode, pr );
+ if ( value == null )
+ {
+ // abort due to error
+ return null;
+ }
+
+ vector.add( value );
+
+ int c = read( pr );
+ if ( c == TOKEN_VEC_CLOS )
+ {
+ return vector;
+ }
+ else if ( c < 0 )
+ {
+ return null;
+ }
+ else if ( c != TOKEN_COMMA )
+ {
+ return null;
+ }
+ }
+ }
+
+
+ private Object readSimple( int code, PushbackReader pr ) throws IOException
+ {
+ switch ( code )
+ {
+ case -1:
+ return null;
+
+ case TOKEN_SIMPLE_STRING:
+ return readQuoted( pr );
+
+ // Simple/Primitive, only use wrapper classes
+ case TOKEN_SIMPLE_INTEGER:
+ case TOKEN_PRIMITIVE_INT:
+ return Integer.valueOf( readQuoted( pr ) );
+
+ case TOKEN_SIMPLE_LONG:
+ case TOKEN_PRIMITIVE_LONG:
+ return Long.valueOf( readQuoted( pr ) );
+
+ case TOKEN_SIMPLE_FLOAT:
+ case TOKEN_PRIMITIVE_FLOAT:
+ int fBits = Integer.parseInt( readQuoted( pr ) );
+ return new Float( Float.intBitsToFloat( fBits ) );
+
+ case TOKEN_SIMPLE_DOUBLE:
+ case TOKEN_PRIMITIVE_DOUBLE:
+ long dBits = Long.parseLong( readQuoted( pr ) );
+ return new Double( Double.longBitsToDouble( dBits ) );
+
+ case TOKEN_SIMPLE_BYTE:
+ case TOKEN_PRIMITIVE_BYTE:
+ return Byte.valueOf( readQuoted( pr ) );
+
+ case TOKEN_SIMPLE_SHORT:
+ case TOKEN_PRIMITIVE_SHORT:
+ return Short.valueOf( readQuoted( pr ) );
+
+ case TOKEN_SIMPLE_CHARACTER:
+ case TOKEN_PRIMITIVE_CHAR:
+ String cString = readQuoted( pr );
+ if ( cString != null && cString.length() > 0 )
+ {
+ return new Character( cString.charAt( 0 ) );
+ }
+ return null;
+
+ case TOKEN_SIMPLE_BOOLEAN:
+ case TOKEN_PRIMITIVE_BOOLEAN:
+ return Boolean.valueOf( readQuoted( pr ) );
+
+ // unknown type code
+ default:
+ return null;
+ }
+ }
+
+
+ private boolean checkNext( PushbackReader pr, int expected ) throws IOException
+ {
+ int next = read( pr );
+ if ( next < 0 )
+ {
+ return false;
+ }
+
+ if ( next == expected )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ private String readQuoted( PushbackReader pr ) throws IOException
+ {
+ StringBuffer buf = new StringBuffer();
+ for ( ;; )
+ {
+ int c = read( pr );
+ switch ( c )
+ {
+ // escaped character
+ case '\\':
+ c = read( pr );
+ switch ( c )
+ {
+ // well known escapes
+ case 'b':
+ buf.append( '\b' );
+ break;
+ case 't':
+ buf.append( '\t' );
+ break;
+ case 'n':
+ buf.append( '\n' );
+ break;
+ case 'f':
+ buf.append( '\f' );
+ break;
+ case 'r':
+ buf.append( '\r' );
+ break;
+ case 'u':// need 4 characters !
+ char[] cbuf = new char[4];
+ if ( read( pr, cbuf ) == 4 )
+ {
+ c = Integer.parseInt( new String( cbuf ), 16 );
+ buf.append( ( char ) c );
+ }
+ break;
+
+ // just an escaped character, unescape
+ default:
+ buf.append( ( char ) c );
+ }
+ break;
+
+ // eof
+ case -1: // fall through
+
+ // separator token
+ case TOKEN_VAL_CLOS:
+ return buf.toString();
+
+ // no escaping
+ default:
+ buf.append( ( char ) c );
+ }
+ }
+ }
+
+
+ private int nextToken( PushbackReader pr ) throws IOException
+ {
+ int c = ignorableWhiteSpace( pr );
+
+ // immediately return EOF
+ if ( c < 0 )
+ {
+ return ( token = c );
+ }
+
+ // check whether there is a name
+ if ( NAME_CHARS.get( c ) )
+ {
+ // read the property name
+ tokenValue = readName( pr, ( char ) c );
+ return ( token = TOKEN_NAME );
+ }
+
+ // check another token
+ if ( TOKEN_CHARS.get( c ) )
+ {
+ return ( token = c );
+ }
+
+ // unexpected character -> so what ??
+ return ( token = -1 );
+ }
+
+
+ private int ignorableWhiteSpace( PushbackReader pr ) throws IOException
+ {
+ int c = read( pr );
+ while ( c >= 0 && Character.isWhitespace( ( char ) c ) )
+ {
+ c = read( pr );
+ }
+ return c;
+ }
+
+
+ private String readName( PushbackReader pr, char firstChar ) throws IOException
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( firstChar );
+
+ int c = read( pr );
+ while ( c >= 0 && NAME_CHARS.get( c ) )
+ {
+ buf.append( ( char ) c );
+ c = read( pr );
+ }
+ pr.unread( c );
+
+ if ( buf.charAt( 0 ) == '.' || buf.charAt( buf.length() - 1 ) == '.' )
+ {
+ throw new IOException( "Name (" + buf + ") must not start or end with a dot" );
+ }
+
+ return buf.toString();
+ }
+
+
+ private int read( PushbackReader pr ) throws IOException
+ {
+ int c = pr.read();
+ if ( c == '\r' )
+ {
+ int c1 = pr.read();
+ if ( c1 != '\n' )
+ {
+ pr.unread( c1 );
+ }
+ c = '\n';
+ }
+
+ if ( c == '\n' )
+ {
+ line++;
+ pos = 0;
+ }
+ else
+ {
+ pos++;
+ }
+
+ return c;
+ }
+
+
+ private int read( PushbackReader pr, char[] buf ) throws IOException
+ {
+ for ( int i = 0; i < buf.length; i++ )
+ {
+ int c = read( pr );
+ if ( c >= 0 )
+ {
+ buf[i] = ( char ) c;
+ }
+ else
+ {
+ return i;
+ }
+ }
+
+ return buf.length;
+ }
+
+
+ private IOException readFailure( int current, int expected )
+ {
+ return new IOException( "Unexpected token " + current + "; expected: " + expected + " (line=" + line + ", pos="
+ + pos + ")" );
+ }
+
+
+ //---------- Configuration Output Implementation --------------------------
+
+ private static void writeValue( Writer out, Object value ) throws IOException
+ {
+ Class clazz = value.getClass();
+ if ( clazz.isArray() )
+ {
+ writeArray( out, value );
+ }
+ else if ( clazz == Vector.class )
+ {
+ writeVector( out, ( Vector ) value );
+ }
+ else
+ {
+ writeType( out, clazz );
+ writeSimple( out, value );
+ }
+ }
+
+
+ private static void writeArray( Writer out, Object arrayValue ) throws IOException
+ {
+ int size = Array.getLength( arrayValue );
+ if ( size == 0 )
+ {
+ return;
+ }
+
+ writeType( out, arrayValue.getClass().getComponentType() );
+ out.write( TOKEN_ARR_OPEN );
+ for ( int i = 0; i < size; i++ )
+ {
+ if ( i > 0 )
+ out.write( TOKEN_COMMA );
+ writeSimple( out, Array.get( arrayValue, i ) );
+ }
+ out.write( TOKEN_ARR_CLOS );
+ }
+
+
+ private static void writeVector( Writer out, Vector vector ) throws IOException
+ {
+ if ( vector.isEmpty() )
+ {
+ return;
+ }
+
+ writeType( out, vector.get( 0 ).getClass() );
+ out.write( TOKEN_VEC_OPEN );
+ for ( int i = 0; i < vector.size(); i++ )
+ {
+ if ( i > 0 )
+ out.write( TOKEN_COMMA );
+ writeSimple( out, vector.get( i ) );
+ }
+ out.write( TOKEN_VEC_CLOS );
+ }
+
+
+ private static void writeType( Writer out, Class valueType ) throws IOException
+ {
+ Integer code = ( Integer ) type2Code.get( valueType );
+ if ( code != null )
+ {
+ out.write( ( char ) code.intValue() );
+ }
+ }
+
+
+ private static void writeSimple( Writer out, Object value ) throws IOException
+ {
+ if ( value instanceof Double )
+ {
+ double dVal = ( ( Double ) value ).doubleValue();
+ value = new Long( Double.doubleToRawLongBits( dVal ) );
+ }
+ else if ( value instanceof Float )
+ {
+ float fVal = ( ( Float ) value ).floatValue();
+ value = new Integer( Float.floatToRawIntBits( fVal ) );
+ }
+
+ out.write( TOKEN_VAL_OPEN );
+ writeQuoted( out, String.valueOf( value ) );
+ out.write( TOKEN_VAL_CLOS );
+ }
+
+
+ private static void writeQuoted( Writer out, String simple ) throws IOException
+ {
+ if ( simple == null || simple.length() == 0 )
+ {
+ return;
+ }
+
+ char c = 0;
+ int len = simple.length();
+ for ( int i = 0; i < len; i++ )
+ {
+ c = simple.charAt( i );
+ switch ( c )
+ {
+ case '\\':
+ case TOKEN_VAL_CLOS:
+ out.write( '\\' );
+ out.write( c );
+ break;
+
+ // well known escapes
+ case '\b':
+ out.write( "\\b" );
+ break;
+ case '\t':
+ out.write( "\\t" );
+ break;
+ case '\n':
+ out.write( "\\n" );
+ break;
+ case '\f':
+ out.write( "\\f" );
+ break;
+ case '\r':
+ out.write( "\\r" );
+ break;
+
+ // other escaping
+ default:
+ if ( c < ' ' )
+ {
+ String t = "000" + Integer.toHexString( c );
+ out.write( "\\u" + t.substring( t.length() - 4 ) );
+ }
+ else
+ {
+ out.write( c );
+ }
+ }
+ }
+ }
+}
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/ConfigurationHandler.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/FilePersistenceManager.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/FilePersistenceManager.java?view=auto&rev=527592
==============================================================================
--- incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/FilePersistenceManager.java (added)
+++ incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/FilePersistenceManager.java Wed Apr 11 11:12:33 2007
@@ -0,0 +1,436 @@
+/*
+ * 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.
+ */
+package org.apache.felix.cm.file;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.BitSet;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+
+import org.apache.felix.cm.PersistenceManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+
+/**
+ * The <code>FilePersistenceManager</code> class stores configuration data in
+ * properties-like files inside a given directory. All configuration files are
+ * located in the same directory.
+ * <p>
+ * The configuration directory may be set by using the
+ * {@link #FilePersistenceManager(String)} naming the path to the directry. When
+ * this persistence manager is used by the Configuration Admin Service, the
+ * location may be configured using the {@link #CM_CONFIG_DIR} bundle context
+ * property.
+ * <p>
+ * If the location is not set, the <code>config</code> directory in the current
+ * working directory (as set in the <code>user.dir</code> system property) is
+ * used. If the the location is set but, no such directory exists, the directory
+ * and any missing parent directories are created. If a file exists at the given
+ * location, the constructor fails.
+ * <p>
+ * Configuration files are created in the configuration directory by appending
+ * the extension <code>.config</code> to the PID of the configuration. The PID
+ * is converted into a relative path name by replacing enclosed dots to slashes.
+ * Non-<code>symbolic-name</code> characters in the PID are encoded with their
+ * Unicode character code in hexadecimal.
+ * <p>
+ * <table border="0" cellspacing="3" cellpadding="0">
+ * <tr><td colspan="2"><b>Examples of PID to name conversion:</td></tr>
+ * <tr><th>PID</th><th>Configuration File Name</th></tr>
+ * <tr><td><code>sample</code><td><code>sample.config</code></tr>
+ * <tr><td><code>org.apache.felix.log.LogService</code><td><code>org/apache/felix/log/LogService.config</code></tr>
+ * <tr><td><code>sample.flèche</code><td><code>sample/fl%00e8che.config</code></tr>
+ * </table>
+ *
+ * @author fmeschbe
+ */
+public class FilePersistenceManager implements PersistenceManager
+{
+
+ /**
+ * The extension of the configuration files.
+ */
+ private static final String FILE_EXT = ".config";
+
+ private static final BitSet VALID_PATH_CHARS;
+
+ /**
+ * The abstract path name of the configuration files.
+ */
+ private final File location;
+
+ // sets up this class defining the set of valid characters in path
+ // set getFile(String) for details.
+ static
+ {
+ VALID_PATH_CHARS = new BitSet();
+
+ for ( int i = 'a'; i <= 'z'; i++ )
+ {
+ VALID_PATH_CHARS.set( i );
+ }
+ for ( int i = 'A'; i <= 'Z'; i++ )
+ {
+ VALID_PATH_CHARS.set( i );
+ }
+ for ( int i = '0'; i <= '9'; i++ )
+ {
+ VALID_PATH_CHARS.set( i );
+ }
+ VALID_PATH_CHARS.set( File.separatorChar );
+ VALID_PATH_CHARS.set( ' ' );
+ VALID_PATH_CHARS.set( '-' );
+ VALID_PATH_CHARS.set( '_' );
+ }
+
+
+ /**
+ * Creates an instance of this persistence manager using the given location
+ * as the directory to store and retrieve the configuration files.
+ *
+ * @param location The configuration file location. If this is
+ * <code>null</code> the <code>config</code> directory below the current
+ * working directory is used.
+ *
+ * @throws IllegalArgumentException If the location exists but is not a
+ * directory or does not exist and cannot be created.
+ */
+ public FilePersistenceManager( String location )
+ {
+ if ( location == null )
+ {
+ location = System.getProperty( "user.dir" ) + "/config";
+ }
+
+ // check the location
+ File locationFile = new File( location );
+ if ( !locationFile.isDirectory() )
+ {
+ if ( locationFile.exists() )
+ {
+ throw new IllegalArgumentException( location + " is not a directory" );
+ }
+
+ if ( !locationFile.mkdirs() )
+ {
+ throw new IllegalArgumentException( "Cannot create directory " + location );
+ }
+ }
+
+ this.location = locationFile;
+ }
+
+
+ /**
+ * Loads configuration data from the configuration location and returns
+ * it as <code>Dictionary</code> objects.
+ * <p>
+ * This method is a lazy implementation, which is just one configuration
+ * file ahead of the current enumeration location.
+ *
+ * @return an enumeration of configuration data returned as instances of
+ * the <code>Dictionary</code> class.
+ */
+ public Enumeration getDictionaries()
+ {
+ return new DictionaryEnumeration();
+ }
+
+
+ /**
+ * Deletes the file for the given identifier.
+ *
+ * @param pid The identifier of the configuration file to delete.
+ */
+ public void delete( String pid )
+ {
+ getFile( pid ).delete();
+ }
+
+
+ /**
+ * Returns <code>true</code> if a (configuration) file exists for the given
+ * identifier.
+ *
+ * @param pid The identifier of the configuration file to check.
+ *
+ * @return <code>true</code> if the file exists
+ */
+ public boolean exists( String pid )
+ {
+ return getFile( pid ).isFile();
+ }
+
+
+ /**
+ * Reads the (configuration) for the given identifier into a
+ * <code>Dictionary</code> object.
+ *
+ * @param pid The identifier of the configuration file to delete.
+ *
+ * @return The configuration read from the file. This <code>Dictionary</code>
+ * may be empty if the file contains no configuration information
+ * or is not properly formatted.
+ */
+ public Dictionary load( String pid ) throws IOException
+ {
+ return load( getFile( pid ) );
+ }
+
+
+ /**
+ * Stores the contents of the <code>Dictionary</code> in a file denoted
+ * by the given identifier.
+ *
+ * @param pid The identifier of the configuration file to which to write
+ * the configuration contents.
+ * @param props The configuration data to write.
+ *
+ * @throws IOException If an error occurrs writing the configuration data.
+ */
+ public void store( String pid, Dictionary props ) throws IOException
+ {
+ OutputStream out = null;
+ try
+ {
+ File cfgFile = getFile( pid );
+
+ // ensure parent path
+ cfgFile.getParentFile().mkdirs();
+
+
+ out = new FileOutputStream( cfgFile );
+ ConfigurationHandler.write( out, props );
+ }
+ finally
+ {
+ if ( out != null )
+ {
+ try
+ {
+ out.close();
+ }
+ catch ( IOException ioe )
+ {
+ // ignore
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Loads the contents of the <code>cfgFile</code> into a new
+ * <code>Dictionary</code> object.
+ *
+ * @param cfgFile The file from which to load the data.
+ *
+ * @return A new <code>Dictionary</code> object providing the file contents.
+ *
+ * @throws java.io.FileNotFoundException If the given file does not exist.
+ * @throws IOException If an error occurrs reading the configuration file.
+ */
+ private Dictionary load( File cfgFile ) throws IOException
+ {
+ InputStream ins = null;
+ try
+ {
+ ins = new FileInputStream( cfgFile );
+ return ConfigurationHandler.read( ins );
+ }
+ finally
+ {
+ if ( ins != null )
+ {
+ try
+ {
+ ins.close();
+ }
+ catch ( IOException ioe )
+ {
+ // ignore
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Creates an abstract path name for the <code>pid</code> encoding it as
+ * follows:
+ * <ul>
+ * <li>Dots (<code>.</code>) are replaced by <code>File.separatorChar</code>
+ * <li>Characters not matching [a-zA-Z0-9 _-] are encoded with a percent
+ * character (<code>%</code>) and a 4-place hexadecimal unicode value.
+ * </ul>
+ * Before returning the path name, the parent directory and any ancestors
+ * are created.
+ *
+ * @param pid The identifier for which to create the abstract file name.
+ *
+ * @return The abstract path name, which the parent directory path created.
+ */
+ private File getFile( String pid )
+ {
+ // replace dots by File.separatorChar
+ pid = pid.replace( '.', File.separatorChar );
+
+ // replace slash by File.separatorChar if different
+ if ( File.separatorChar != '/' )
+ {
+ pid = pid.replace( '/', File.separatorChar );
+ }
+
+ // scan for first non-valid character (if any)
+ int first = 0;
+ while ( first < pid.length() && VALID_PATH_CHARS.get( pid.charAt( first ) ) )
+ {
+ first++;
+ }
+
+ // check whether we exhausted
+ if ( first < pid.length() )
+ {
+ StringBuffer buf = new StringBuffer( pid.substring( 0, first ) );
+
+ for ( int i = first; i < pid.length(); i++ )
+ {
+ char c = pid.charAt( i );
+ if ( VALID_PATH_CHARS.get( c ) )
+ {
+ buf.append( c );
+ }
+ else
+ {
+ String val = "000" + Integer.toHexString( c );
+ buf.append( '%' );
+ buf.append( val.substring( val.length() - 4 ) );
+ }
+ }
+
+ pid = buf.toString();
+ }
+
+ return new File( location, pid + FILE_EXT );
+ }
+
+ /**
+ * The <code>DictionaryEnumeration</code> class implements the
+ * <code>Enumeration</code> returning configuration <code>Dictionary</code>
+ * objects on behalf of the {@link FilePersistenceManager#getDictionaries()}
+ * method.
+ * <p>
+ * This enumeration loads configuration lazily with a look ahead of one
+ * dictionary.
+ *
+ * @author fmeschbe
+ */
+ private class DictionaryEnumeration implements Enumeration
+ {
+ private Stack dirStack;
+ private File[] fileList;
+ private int idx;
+ private Dictionary next;
+
+
+ DictionaryEnumeration()
+ {
+ dirStack = new Stack();
+ fileList = null;
+ idx = 0;
+
+ dirStack.push( location );
+ next = seek();
+ }
+
+
+ public boolean hasMoreElements()
+ {
+ return next != null;
+ }
+
+
+ public Object nextElement()
+ {
+ if ( next == null )
+ {
+ throw new NoSuchElementException();
+ }
+
+ Dictionary toReturn = next;
+ next = seek();
+ return toReturn;
+ }
+
+
+ private Dictionary seek()
+ {
+ while ( ( fileList != null && idx < fileList.length ) || !dirStack.isEmpty() )
+ {
+ if ( fileList == null || idx >= fileList.length )
+ {
+ File dir = ( File ) dirStack.pop();
+ fileList = dir.listFiles();
+ idx = 0;
+ }
+ else
+ {
+
+ File cfgFile = fileList[idx++];
+ if ( cfgFile.isFile() )
+ {
+ try
+ {
+ Dictionary dict = load( cfgFile );
+
+ // use the dictionary if it has a PID and the PID
+ // derived file name matches the source file name
+ if ( dict.get( Constants.SERVICE_PID ) != null
+ && cfgFile.equals( getFile( ( String ) dict.get( Constants.SERVICE_PID ) ) ) )
+ {
+ return dict;
+ }
+ }
+ catch ( IOException ioe )
+ {
+ // ignore, check next file
+ }
+ }
+ else if ( cfgFile.isDirectory() )
+ {
+ dirStack.push( cfgFile );
+ }
+ }
+ }
+
+ // exhausted
+ return null;
+ }
+ }
+}
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/FilePersistenceManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/file/FilePersistenceManager.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java?view=auto&rev=527592
==============================================================================
--- incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java (added)
+++ incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java Wed Apr 11 11:12:33 2007
@@ -0,0 +1,266 @@
+/*
+ * 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.
+ */
+package org.apache.felix.cm.impl;
+
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+
+/**
+ * The <code>CaseInsensitiveDictionary</code> is a
+ * <code>java.util.Dictionary</code> which conforms to the requirements laid
+ * out by the Configuration Admin Service Specification requiring the property
+ * names to keep case but to ignore case when accessing the properties.
+ *
+ * @author fmeschbe
+ */
+class CaseInsensitiveDictionary extends Dictionary
+{
+
+ /**
+ * The backend dictionary with lower case keys.
+ */
+ private Hashtable internalMap;
+
+ /**
+ * Mapping of lower case keys to original case keys as last used to set
+ * a property value.
+ */
+ private Hashtable originalKeys;
+
+
+ CaseInsensitiveDictionary()
+ {
+ internalMap = new Hashtable();
+ originalKeys = new Hashtable();
+ }
+
+
+ CaseInsensitiveDictionary( Dictionary props )
+ {
+ this();
+
+ Enumeration keys = props.keys();
+ while ( keys.hasMoreElements() )
+ {
+ Object key = keys.nextElement();
+ if ( !( key instanceof String ) )
+ {
+ throw new IllegalArgumentException( "Key [" + key + "] must be a String" );
+ }
+
+ // check uniqueness of key
+ String lowerCase = ( ( String ) key ).toLowerCase();
+ if ( internalMap.containsKey( lowerCase ) )
+ {
+ throw new IllegalArgumentException( "Key [" + key + "] already present in different case" );
+ }
+
+ // check the value
+ Object value = props.get( key );
+ checkValue( value );
+
+ // add the key/value pair
+ internalMap.put( lowerCase, value );
+ originalKeys.put( lowerCase, key );
+ }
+ }
+
+
+ CaseInsensitiveDictionary( CaseInsensitiveDictionary props )
+ {
+ internalMap = new Hashtable( props.internalMap );
+ originalKeys = new Hashtable( props.originalKeys );
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.util.Dictionary#elements()
+ */
+ public Enumeration elements()
+ {
+ return Collections.enumeration( internalMap.values() );
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.util.Dictionary#get(java.lang.Object)
+ */
+ public Object get( Object key )
+ {
+ if ( key == null )
+ {
+ throw new NullPointerException( "key" );
+ }
+
+ String stringKey = String.valueOf( key ).toLowerCase();
+ return internalMap.get( stringKey );
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.util.Dictionary#isEmpty()
+ */
+ public boolean isEmpty()
+ {
+ return internalMap.isEmpty();
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.util.Dictionary#keys()
+ */
+ public Enumeration keys()
+ {
+ return Collections.enumeration( originalKeys.values() );
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.util.Dictionary#put(java.lang.Object, java.lang.Object)
+ */
+ public Object put( Object key, Object value )
+ {
+ if ( key == null || value == null )
+ {
+ throw new NullPointerException( "key or value" );
+ }
+
+ if ( !( key instanceof String ) )
+ {
+ throw new IllegalArgumentException( "Key [" + key + "] must be a String" );
+ }
+
+ checkValue( value );
+
+ String lowerCase = String.valueOf( key ).toLowerCase();
+ originalKeys.put( lowerCase, key );
+ return internalMap.put( lowerCase, value );
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.util.Dictionary#remove(java.lang.Object)
+ */
+ public Object remove( Object key )
+ {
+ if ( key == null )
+ {
+ throw new NullPointerException( "key" );
+ }
+
+ String lowerCase = String.valueOf( key ).toLowerCase();
+ originalKeys.remove( lowerCase );
+ return internalMap.remove( lowerCase );
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.util.Dictionary#size()
+ */
+ public int size()
+ {
+ return internalMap.size();
+ }
+
+
+ //---------- internal -----------------------------------------------------
+
+ static void checkValue( Object value )
+ {
+ Class type;
+ if ( value instanceof Object[] )
+ {
+ // check simple or primitive
+ type = value.getClass().getComponentType();
+
+ // check for primitive type
+ if ( type == Integer.TYPE || type == Long.TYPE || type == Float.TYPE || type == Double.TYPE
+ || type == Byte.TYPE || type == Short.TYPE || type == Character.TYPE || type == Boolean.TYPE )
+ {
+ return;
+ }
+
+ }
+ else if ( value instanceof Vector )
+ {
+ // check simple
+ Vector vector = ( Vector ) value;
+ if ( vector.isEmpty() )
+ {
+ throw new IllegalArgumentException( "Vector must not be empty" );
+ }
+
+ // ensure all elements have the same type
+ type = null;
+ for ( int i = 0; i < vector.size(); i++ )
+ {
+ Object el = vector.get( i );
+ if ( el == null )
+ {
+ throw new IllegalArgumentException( "Vector must not contain null elements" );
+ }
+ if ( type == null )
+ {
+ type = el.getClass();
+ }
+ else if ( type != el.getClass() )
+ {
+ throw new IllegalArgumentException( "Vector element types must not be mixed" );
+ }
+ }
+
+ }
+ else if ( value != null )
+ {
+ // get the type to check (must be simple)
+ type = value.getClass();
+
+ }
+ else
+ {
+ // null is illegal
+ throw new IllegalArgumentException( "Value must not be null" );
+ }
+
+ // check for simple type
+ if ( type == String.class || type == Integer.class || type == Long.class || type == Float.class
+ || type == Double.class || type == Byte.class || type == Short.class || type == Character.class
+ || type == Boolean.class )
+ {
+ return;
+ }
+
+ // not a valid type
+ throw new IllegalArgumentException( "Value [" + value + "] has unsupported (base-) type " + type );
+ }
+
+
+ //---------- Object Overwrites --------------------------------------------
+
+ public String toString()
+ {
+ return internalMap.toString();
+ }
+
+}
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/CaseInsensitiveDictionary.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java?view=auto&rev=527592
==============================================================================
--- incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java (added)
+++ incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java Wed Apr 11 11:12:33 2007
@@ -0,0 +1,176 @@
+/*
+ * $Url: $
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.felix.cm.impl;
+
+
+import java.io.IOException;
+import java.util.Dictionary;
+
+import org.osgi.service.cm.Configuration;
+
+
+/**
+ * The <code>ConfigurationAdapter</code> TODO
+ *
+ * @author fmeschbe
+ * @version $Rev:$, $Date:$
+ */
+public class ConfigurationAdapter implements Configuration
+{
+
+ private ConfigurationAdminImpl configurationAdmin;
+ private ConfigurationImpl delegatee;
+
+
+ ConfigurationAdapter( ConfigurationAdminImpl configurationAdmin, ConfigurationImpl delegatee )
+ {
+ this.configurationAdmin = configurationAdmin;
+ this.delegatee = delegatee;
+ }
+
+
+ /**
+ * @return
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#getPid()
+ */
+ public String getPid()
+ {
+ checkDeleted();
+ return delegatee.getPid();
+ }
+
+
+ /**
+ * @return
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#getFactoryPid()
+ */
+ public String getFactoryPid()
+ {
+ checkDeleted();
+ return delegatee.getFactoryPid();
+ }
+
+
+ /**
+ * @return
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#getBundleLocation()
+ */
+ public String getBundleLocation()
+ {
+ configurationAdmin.checkPermission();
+ checkDeleted();
+ return delegatee.getBundleLocation();
+ }
+
+
+ /**
+ * @param bundleLocation
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#setBundleLocation(java.lang.String)
+ */
+ public void setBundleLocation( String bundleLocation )
+ {
+ configurationAdmin.checkPermission();
+ checkDeleted();
+ delegatee.setBundleLocation( bundleLocation );
+ }
+
+
+ /**
+ * @throws IOException
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#update()
+ */
+ public void update() throws IOException
+ {
+ checkDeleted();
+ delegatee.update();
+ }
+
+
+ /**
+ * @param properties
+ * @throws IOException
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#update(java.util.Dictionary)
+ */
+ public void update( Dictionary properties ) throws IOException
+ {
+ checkDeleted();
+ delegatee.update( properties );
+ }
+
+
+ /**
+ * @return
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#getProperties()
+ */
+ public Dictionary getProperties()
+ {
+ checkDeleted();
+ return delegatee.getProperties();
+ }
+
+
+ /**
+ * @throws IOException
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#delete()
+ */
+ public void delete() throws IOException
+ {
+ checkDeleted();
+ delegatee.delete();
+ }
+
+
+ /**
+ * @return
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#hashCode()
+ */
+ public int hashCode()
+ {
+ return delegatee.hashCode();
+ }
+
+
+ /**
+ * @param obj
+ * @return
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#equals(java.lang.Object)
+ */
+ public boolean equals( Object obj )
+ {
+ return delegatee.equals( obj );
+ }
+
+
+ /**
+ * @return
+ * @see org.apache.felix.cm.impl.ConfigurationImpl#toString()
+ */
+ public String toString()
+ {
+ return delegatee.toString();
+ }
+
+ /**
+ * Checks whether this configuration object has already been deleted.
+ *
+ * @throws IllegalStateException If this configuration object has been
+ * deleted.
+ */
+ private void checkDeleted() {
+ if (delegatee.isDeleted()) {
+ throw new IllegalStateException( "Configuration " + delegatee.getPid() + " deleted" );
+ }
+ }
+}
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdapter.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminFactory.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminFactory.java?view=auto&rev=527592
==============================================================================
--- incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminFactory.java (added)
+++ incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminFactory.java Wed Apr 11 11:12:33 2007
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package org.apache.felix.cm.impl;
+
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+
+
+/**
+ * The <code>ConfigurationAdminFactory</code> is the <code>ServiceFactory</code>
+ * registered as the <code>ConfigurationAdmin</code> service responsible to
+ * create the real <code>ConfiguratAdmin</code> instances returend to client
+ * bundles. Each bundle gets a separate instance.
+ *
+ * @author fmeschbe
+ */
+class ConfigurationAdminFactory implements ServiceFactory
+{
+
+ // The configuration manager to which the configuration admin instances
+ // delegate most of their work
+ private ConfigurationManager configurationManager;
+
+
+ ConfigurationAdminFactory( ConfigurationManager configurationManager )
+ {
+ this.configurationManager = configurationManager;
+ }
+
+
+ /**
+ * Returns a new instance of the {@link ConfigurationAdminImpl} class for
+ * the given bundle.
+ */
+ public Object getService( Bundle bundle, ServiceRegistration registration )
+ {
+ return new ConfigurationAdminImpl( configurationManager, bundle );
+ }
+
+
+ /**
+ * Disposes off the given {@link ConfigurationAdminImpl} instance as the
+ * given bundle has no use of it any more.
+ */
+ public void ungetService( Bundle bundle, ServiceRegistration registration, Object service )
+ {
+ ( ( ConfigurationAdminImpl ) service ).dispose();
+ }
+
+}
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminFactory.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java?view=auto&rev=527592
==============================================================================
--- incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java (added)
+++ incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java Wed Apr 11 11:12:33 2007
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+package org.apache.felix.cm.impl;
+
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationPermission;
+
+
+/**
+ * The <code>ConfigurationAdminImpl</code> is the per-bundle frontend to the
+ * configuration manager. Instances of this class are created on-demand for
+ * each bundle trying to get hold of the <code>ConfigurationAdmin</code>
+ * service.
+ *
+ * @author fmeschbe
+ */
+public class ConfigurationAdminImpl implements ConfigurationAdmin
+{
+
+ // The configuration manager to which most of the tasks are delegated
+ private ConfigurationManager configurationManager;
+
+ // The bundle for which this instance has been created
+ private Bundle bundle;
+
+
+ ConfigurationAdminImpl( ConfigurationManager configurationManager, Bundle bundle )
+ {
+ this.configurationManager = configurationManager;
+ this.bundle = bundle;
+ }
+
+
+ void dispose()
+ {
+ this.bundle = null;
+ this.configurationManager = null;
+ }
+
+
+ Bundle getBundle()
+ {
+ return bundle;
+ }
+
+
+ //---------- ConfigurationAdmin interface ---------------------------------
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.ConfigurationAdmin#createFactoryConfiguration(java.lang.String)
+ */
+ public Configuration createFactoryConfiguration( String factoryPid ) throws IOException
+ {
+ return wrap( configurationManager.createFactoryConfiguration( this, factoryPid ) );
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.ConfigurationAdmin#createFactoryConfiguration(java.lang.String, java.lang.String)
+ */
+ public Configuration createFactoryConfiguration( String factoryPid, String location ) throws IOException
+ {
+ checkPermission();
+
+ return wrap( configurationManager.createFactoryConfiguration( factoryPid, location ) );
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.ConfigurationAdmin#getConfiguration(java.lang.String)
+ */
+ public Configuration getConfiguration( String pid ) throws IOException
+ {
+ ConfigurationImpl config = configurationManager.getConfiguration( pid );
+
+ if ( config.getBundleLocation() == null )
+ {
+ config.setBundleLocation( getBundle().getLocation() );
+ }
+ else if ( !config.getBundleLocation().equals( getBundle().getLocation() ) )
+ {
+ checkPermission();
+ }
+
+ return wrap( config );
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.ConfigurationAdmin#getConfiguration(java.lang.String, java.lang.String)
+ */
+ public Configuration getConfiguration( String pid, String location ) throws IOException
+ {
+ checkPermission();
+
+ return wrap( configurationManager.getConfiguration( pid, location ) );
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.ConfigurationAdmin#listConfigurations(java.lang.String)
+ */
+ public Configuration[] listConfigurations( String filter ) throws IOException, InvalidSyntaxException
+ {
+ ConfigurationImpl ci[] = configurationManager.listConfigurations( this, filter );
+ if ( ci == null )
+ {
+ return null;
+ }
+
+ Configuration[] cfgs = new Configuration[ci.length];
+ for ( int i = 0; i < cfgs.length; i++ )
+ {
+ cfgs[i] = wrap( ci[i] );
+ }
+
+ return cfgs;
+ }
+
+
+ //---------- Security checks ----------------------------------------------
+
+ private Configuration wrap( ConfigurationImpl configuration )
+ {
+ return new ConfigurationAdapter( this, configuration );
+ }
+
+
+ /**
+ * Checks whether the bundle to which this instance has been given has the
+ * <code>CONFIGURE</code> permission and returns <code>true</code> in this
+ * case.
+ */
+ boolean hasPermission()
+ {
+ return bundle.hasPermission( new ConfigurationPermission( "*", ConfigurationPermission.CONFIGURE ) );
+ }
+
+
+ /**
+ * Checks whether the bundle to which this instance has been given has the
+ * <code>CONFIGURE</code> permission and throws a <code>SecurityException</code>
+ * if this is not the case.
+ *
+ * @throws SecurityException if the bundle to which this instance belongs
+ * does not have the <code>CONFIGURE</code> permission.
+ */
+ void checkPermission()
+ {
+ if ( !hasPermission() )
+ {
+ throw new SecurityException( "Bundle " + bundle.getSymbolicName()
+ + " not permitted for Configuration Tasks" );
+ }
+ }
+
+}
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationAdminImpl.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java?view=auto&rev=527592
==============================================================================
--- incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java (added)
+++ incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java Wed Apr 11 11:12:33 2007
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ */
+package org.apache.felix.cm.impl;
+
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.cm.PersistenceManager;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.log.LogService;
+
+
+/**
+ * The <code>ConfigurationImpl</code> is the implementation of the Configuration
+ * Admin Service Specification <i>Configuration object</i> (section 104.4).
+ *
+ * @author fmeschbe
+ */
+class ConfigurationImpl
+{
+
+ /**
+ * The {@link ConfigurationManager configuration manager} instance which
+ * caused this configuration object to be created.
+ */
+ private ConfigurationManager configurationManager;
+
+ /**
+ * The {@link PersistenceManager persistence manager} which loaded this
+ * configuration instance and which is used to store and delete configuration
+ * data.
+ */
+ private PersistenceManager persistenceManager;
+
+ /**
+ * The serviceReference PID of this configuration.
+ */
+ private String pid;
+
+ /**
+ * The factory serviceReference PID of this configuration or <code>null</code> if this
+ * is not a factory configuration.
+ */
+ private String factoryPID;
+
+ /**
+ * The location of the bundle to which this configuration instance is bound.
+ * This is not necessarily the same as the bundle of the
+ * {@link #configurationAdmin}. If this configuration is not bound to a
+ * bundle, this field is <code>null</code>.
+ */
+ private String bundleLocation;
+
+ /**
+ * The <code>ServiceReference</code> of the serviceReference which first asked for
+ * this configuration. This field is <code>null</code> if the configuration
+ * has not been handed to a serviceReference by way of the <code>ManagedService.update(Dictionary)</code>
+ * or <code>ManagedServiceFactory.updated(String, Dictionary)</code>
+ * method.
+ */
+ private ServiceReference serviceReference;
+
+ /**
+ * The configuration data of this configuration instance. This is a private
+ * copy of the properties of which a copy is made when the
+ * {@link #getProperties()} method is called. This field is <code>null</code> if
+ * the configuration has been created and never stored to persistence.
+ */
+ private CaseInsensitiveDictionary properties;
+
+
+ ConfigurationImpl( ConfigurationManager configurationManager, PersistenceManager persistenceManager,
+ Dictionary properties )
+ {
+ this.configurationManager = configurationManager;
+ this.persistenceManager = persistenceManager;
+
+ this.pid = ( String ) properties.remove( Constants.SERVICE_PID );
+ this.factoryPID = ( String ) properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
+ this.bundleLocation = ( String ) properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
+
+ configure( properties );
+ }
+
+
+ ConfigurationImpl( ConfigurationManager configurationManager, PersistenceManager persistenceManager, String pid,
+ String factoryPid )
+ {
+ this.configurationManager = configurationManager;
+ this.persistenceManager = persistenceManager;
+ this.pid = pid;
+ this.factoryPID = factoryPid;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.Configuration#delete()
+ */
+ public void delete() throws IOException
+ {
+ if ( !isDeleted() )
+ {
+ persistenceManager.delete( pid );
+ persistenceManager = null;
+
+ configurationManager.deleted( this );
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.Configuration#getPid()
+ */
+ public String getPid()
+ {
+ return pid;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.Configuration#getFactoryPid()
+ */
+ public String getFactoryPid()
+ {
+ return factoryPID;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.Configuration#getBundleLocation()
+ */
+ public String getBundleLocation()
+ {
+ return bundleLocation;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.Configuration#getProperties()
+ */
+ public Dictionary getProperties()
+ {
+ // no properties yet
+ if ( properties == null )
+ {
+ return null;
+ }
+
+ CaseInsensitiveDictionary props = new CaseInsensitiveDictionary( properties );
+
+ // fix special properties (pid, factory PID, bundle location)
+ setAutoProperties( props, false );
+
+ return props;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.Configuration#setBundleLocation(java.lang.String)
+ */
+ public void setBundleLocation( String bundleLocation )
+ {
+ if ( !isDeleted() )
+ {
+ this.bundleLocation = bundleLocation;
+
+ // 104.15.2.8 The bundle location will be set persistently
+ try
+ {
+ store();
+ }
+ catch ( IOException ioe )
+ {
+ configurationManager.log( LogService.LOG_ERROR, "Persisting new bundle location failed", ioe );
+ }
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.Configuration#update()
+ */
+ public void update() throws IOException
+ {
+ if ( !isDeleted() )
+ {
+ // read configuration from persistence (again)
+ Dictionary properties = persistenceManager.load( pid );
+
+ // ensure serviceReference pid
+ String servicePid = ( String ) properties.get( Constants.SERVICE_PID );
+ if ( servicePid != null && !pid.equals( servicePid ) )
+ {
+ throw new IOException( "PID of configuration file does match requested PID; expected " + pid + ", got "
+ + servicePid );
+ }
+
+ configure( properties );
+
+ configurationManager.updated( this );
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.osgi.service.cm.Configuration#update(java.util.Dictionary)
+ */
+ public void update( Dictionary properties ) throws IOException
+ {
+ if ( !isDeleted() )
+ {
+ CaseInsensitiveDictionary newProperties = new CaseInsensitiveDictionary( properties );
+
+ setAutoProperties( newProperties, true );
+
+ persistenceManager.store( pid, newProperties );
+
+ configure( newProperties );
+
+ configurationManager.updated( this );
+ }
+ }
+
+
+ //---------- Object overwrites --------------------------------------------
+
+ public boolean equals( Object obj )
+ {
+ if ( obj == this )
+ {
+ return true;
+ }
+
+ if ( obj instanceof Configuration )
+ {
+ return pid.equals( ( ( Configuration ) obj ).getPid() );
+ }
+
+ return false;
+ }
+
+
+ public int hashCode()
+ {
+ return pid.hashCode();
+ }
+
+
+ public String toString()
+ {
+ return "Configuration PID=" + pid + ", factoryPID=" + factoryPID + ", bundleLocation=" + bundleLocation;
+ }
+
+
+ //---------- private helper -----------------------------------------------
+
+ void setServiceReference( ServiceReference serviceReference )
+ {
+ this.serviceReference = serviceReference;
+ }
+
+
+ ServiceReference getServiceReference()
+ {
+ return serviceReference;
+ }
+
+
+ void store() throws IOException
+ {
+ Dictionary props = getProperties();
+
+ // if this is a new configuration, we just use an empty Dictionary
+ if ( props == null )
+ {
+ props = new Hashtable();
+
+ // add automatic properties including the bundle location (if set)
+ setAutoProperties( props, true );
+ }
+ else if ( getBundleLocation() != null )
+ {
+ props.put( ConfigurationAdmin.SERVICE_BUNDLELOCATION, getBundleLocation() );
+ }
+
+ // only store now, if this is not a new configuration
+ persistenceManager.store( pid, props );
+ }
+
+
+ boolean isDeleted()
+ {
+ if ( persistenceManager != null )
+ {
+ if ( properties == null || persistenceManager.exists( pid ) )
+ {
+ return false;
+ }
+
+ persistenceManager = null;
+ }
+
+ return true;
+ }
+
+
+ private void configure( Dictionary properties )
+ {
+ // remove predefined properties
+ clearAutoProperties( properties );
+
+ // ensure CaseInsensitiveDictionary
+ if ( properties instanceof CaseInsensitiveDictionary )
+ {
+ this.properties = ( CaseInsensitiveDictionary ) properties;
+ }
+ else
+ {
+ this.properties = new CaseInsensitiveDictionary( properties );
+ }
+ }
+
+
+ void setAutoProperties( Dictionary properties, boolean withBundleLocation )
+ {
+ // set pid and factory pid in the properties
+ replaceProperty( properties, Constants.SERVICE_PID, pid );
+ replaceProperty( properties, ConfigurationAdmin.SERVICE_FACTORYPID, factoryPID );
+
+ // bundle location is not set here
+ if ( withBundleLocation )
+ {
+ replaceProperty( properties, ConfigurationAdmin.SERVICE_BUNDLELOCATION, getBundleLocation() );
+ }
+ else
+ {
+ properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
+ }
+ }
+
+
+ void clearAutoProperties( Dictionary properties )
+ {
+ properties.remove( Constants.SERVICE_PID );
+ properties.remove( ConfigurationAdmin.SERVICE_FACTORYPID );
+ properties.remove( ConfigurationAdmin.SERVICE_BUNDLELOCATION );
+ }
+
+
+ private void replaceProperty( Dictionary properties, String key, String value )
+ {
+ if ( value == null )
+ {
+ properties.remove( key );
+ }
+ else
+ {
+ properties.put( key, value );
+ }
+ }
+}
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/felix/trunk/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationImpl.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url