ConfigService based on HSQLDB (through JDBC)

cusax-fix 5024
Ingo Bauersachs 12 years ago
parent e7cd23f434
commit 09c0ef620b

@ -31,7 +31,7 @@
<classpathentry kind="lib" path="lib/installer-exclude/hexdump-0.2.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/httpclient-osgi-4.2.3.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/httpcore-osgi-4.2.3.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/ice4j.jar" sourcepath="../ice4j/src"/>
<classpathentry kind="lib" path="lib/installer-exclude/ice4j.jar" sourcepath="/ice4j/src"/>
<classpathentry kind="lib" path="lib/installer-exclude/jain-sdp.jar">
<attributes>
<attribute name="javadoc_location" value="https://hudson.jboss.org/jenkins/job/jain-sip/lastStableBuild/artifact/javadoc/"/>
@ -59,7 +59,7 @@
<attribute name="javadoc_location" value="http://dbus.freedesktop.org/doc/dbus-java/api/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="lib/installer-exclude/libjitsi.jar" sourcepath="../libjitsi/src"/>
<classpathentry kind="lib" path="lib/installer-exclude/libjitsi.jar" sourcepath="/libjitsi/src"/>
<classpathentry kind="lib" path="lib/bundle/log4j.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/mac_widgets-0.9.5.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/objenesis-1.2.jar"/>
@ -82,5 +82,6 @@
<classpathentry kind="lib" path="lib/installer-exclude/bccontrib-1.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/zrtp4j-light.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/jcalendar-1.4.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/hsqldb.jar"/>
<classpathentry kind="output" path="classes"/>
</classpath>

@ -1088,7 +1088,7 @@
bundle-demuxcontactsource, bundle-muc,
bundle-desktoputil,bundle-globaldisplaydetails,
bundle-plugin-propertieseditor,bundle-plugin-accountinfo,
bundle-guava"/>
bundle-guava,bundle-hsql"/>
<!--BUNDLE-SC-LAUNCHER-->
<target name="bundle-sc-launcher">
@ -1386,6 +1386,10 @@
<copy file="${lib.noinst}/guava-15.0.jar" tofile="${bundles.dest}/guava.jar"/>
</target>
<target name="bundle-hsql">
<copy file="${lib.noinst}/hsqldb.jar" tofile="${bundles.dest}/hsqldb.jar"/>
</target>
<!--BUNDLE-SERVICEBINDER -->
<target name="bundle-servicebinder">
<jar compress="true" destfile="lib/bundle/servicebinder.jar"

@ -42,7 +42,8 @@ felix.auto.start.10= \
felix.auto.start.15= \
reference:file:sc-bundles/libjitsi.jar \
reference:file:sc-bundles/dnsservice.jar
reference:file:sc-bundles/dnsservice.jar \
reference:file:sc-bundles/hsqldb.jar
felix.auto.start.20= \
reference:file:sc-bundles/util.jar \

@ -50,6 +50,7 @@ felix.auto.start.10= \
reference:file:lib/installer-exclude/cglib-nodep.osgi-2.1_3.jar \
reference:file:sc-bundles/bouncycastle.jar \
reference:file:sc-bundles/bccontrib.jar \
reference:file:sc-bundles/hsqldb.jar \
reference:file:sc-bundles/guava.jar
# file:lib/bundle/shell.jar \
# file:lib/bundle/bundlerepository.jar \

Binary file not shown.

@ -7,7 +7,11 @@
package net.java.sip.communicator.impl.configuration;
import com.sun.jna.*;
import net.java.sip.communicator.util.ServiceUtils;
import org.jitsi.service.configuration.*;
import org.jitsi.service.fileaccess.*;
import org.jitsi.service.libjitsi.*;
import org.jitsi.util.*;
import org.osgi.framework.*;
@ -29,10 +33,9 @@ public class ConfigurationActivator
= Logger.getLogger(ConfigurationActivator.class);
/**
* The <tt>BundleContext</tt> in which the configuration bundle has been
* started and has not been stopped yet.
* The currently registered {@link ConfigurationService} instance.
*/
private static BundleContext bundleContext;
private ConfigurationService cs;
/**
* Starts the configuration service
@ -44,20 +47,29 @@ public class ConfigurationActivator
public void start(BundleContext bundleContext)
throws Exception
{
ConfigurationActivator.bundleContext = bundleContext;
ConfigurationService configurationService
= LibJitsi.getConfigurationService();
if (configurationService != null)
FileAccessService fas
= ServiceUtils.getService(bundleContext, FileAccessService.class);
File useDatabaseConfig = fas.getPrivatePersistentFile(
".usedatabaseconfig",
FileCategory.PROFILE);
// BETA: if the marker file exists, use the database configuration
if (useDatabaseConfig.exists())
{
logger.info("Using database configuration store.");
this.cs = new JdbcConfigService(fas);
}
else
{
this.cs = LibJitsi.getConfigurationService();
}
bundleContext.registerService(
ConfigurationService.class.getName(),
configurationService,
this.cs,
null);
fixPermissions(configurationService);
}
fixPermissions(this.cs);
}
/**
@ -72,18 +84,8 @@ public void start(BundleContext bundleContext)
public void stop(BundleContext bundleContext)
throws Exception
{
}
/**
* Gets the <tt>BundleContext</tt> in which the configuration bundle has
* been started and has not been stopped yet.
*
* @return the <tt>BundleContext</tt> in which the configuration bundle has
* been started and has not been stopped yet
*/
public static BundleContext getBundleContext()
{
return bundleContext;
this.cs.storeConfiguration();
this.cs = null;
}
/**

@ -0,0 +1,910 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.configuration;
import java.beans.*;
import java.io.*;
import java.sql.*;
import java.sql.Statement;
import java.util.*;
import org.jitsi.service.configuration.*;
import org.jitsi.service.fileaccess.*;
import org.jitsi.util.*;
import com.google.common.collect.*;
/**
* Implementation of the {@link ConfigurationService} based on JDBC.
*
* @author Ingo Bauersachs
*/
public final class JdbcConfigService
implements ConfigurationService
{
/**
* The <tt>Logger</tt> used by this class.
*/
private final Logger logger
= Logger.getLogger(JdbcConfigService.class);
/**
* Name of the file containing default properties.
*/
private static final String DEFAULT_PROPS_FILE_NAME
= "jitsi-defaults.properties";
/**
* Name of the file containing overrides (possibly set by the distributor)
* for any of the default properties.
*/
private static final String DEFAULT_OVERRIDES_PROPS_FILE_NAME
= "jitsi-default-overrides.properties";
/**
* A set of immutable properties deployed with the application during
* install time. The properties in this file will be impossible to override
* and attempts to do so will simply be ignored.
* @see #defaultProperties
*/
private Map<String, String> immutableDefaultProperties
= new HashMap<String, String>();
/**
* A set of properties deployed with the application during install time.
* Contrary to the properties in {@link #immutableDefaultProperties} the
* ones in this map can be overridden with call to the
* <tt>setProperty()</tt> methods. Still, re-setting one of these properties
* to <tt>null</tt> would cause for its initial value to be restored.
*/
private Map<String, String> defaultProperties
= new HashMap<String, String>();
/**
* Registered property change listeners that may veto a change.
*/
private SetMultimap<String, ConfigVetoableChangeListener> vetoListeners
= HashMultimap.create();
/**
* Registered property change listeners.
*/
private SetMultimap<String, PropertyChangeListener> listeners
= HashMultimap.create();
/**
* Connection to the JDBC database.
*/
private Connection connection;
// SQL statements for queries against the database
private PreparedStatement selectExact;
private PreparedStatement selectLike;
private PreparedStatement selectAll;
private PreparedStatement insertOrUpdate;
private PreparedStatement delete;
/**
* Reference to the {@link FileAccessService}.
*/
private FileAccessService fas;
/**
* Creates a new instance of this class.
* @param fas Reference to the {@link FileAccessService}.
* @throws Exception
*/
public JdbcConfigService(FileAccessService fas) throws Exception
{
this.fas = fas;
File dataFile = fas.getPrivatePersistentFile(
"props.hsql.script",
FileCategory.PROFILE);
File oldProps = fas.getPrivatePersistentFile(
"sip-communicator.properties",
FileCategory.PROFILE);
// if the file for the current database does not exist yet but
// the previous properties-based file is there, migrate it
boolean migrate = false;
if (!dataFile.exists() && oldProps.exists())
{
migrate = true;
}
// open the connection
Class.forName("org.hsqldb.jdbc.JDBCDriver");
checkConnection();
// then do the actual migration
if (migrate)
{
Properties p = new Properties();
p.load(new FileInputStream(oldProps));
this.connection.setAutoCommit(false);
for (Map.Entry<Object, Object> e : p.entrySet())
{
this.setProperty(e.getKey().toString(), e.getValue(), false);
}
this.connection.commit();
this.connection.setAutoCommit(true);
}
// and finally load the (mandatory) system properties
loadDefaultProperties(DEFAULT_PROPS_FILE_NAME);
loadDefaultProperties(DEFAULT_OVERRIDES_PROPS_FILE_NAME);
}
/**
* Verifies that the connection to the database and all prepared statement
* are valid.
*
* @throws SQLException
*/
private void checkConnection() throws SQLException
{
if (this.connection != null && this.connection.isValid(1))
{
return;
}
String filename;
try
{
File f = fas.getPrivatePersistentFile(
"props.hsql",
FileCategory.PROFILE);
filename = f.getAbsolutePath();
}
catch (Exception e)
{
throw new SQLException(e);
}
this.connection = DriverManager.getConnection(
"jdbc:hsqldb:file:"
+ filename
+ ";shutdown=true;hsqldb.write_delay=false;"
+ "hsqldb.write_delay_millis=0");
Statement st = this.connection.createStatement();
st.executeUpdate(
"CREATE TABLE IF NOT EXISTS Props ("
+ "k LONGVARCHAR UNIQUE, v LONGVARCHAR"
+ ")");
this.selectExact = this.connection.prepareStatement(
"SELECT v FROM Props WHERE k=?");
this.selectLike = this.connection.prepareStatement(
"SELECT k, v FROM Props WHERE k LIKE ?");
this.selectAll = this.connection.prepareStatement(
"SELECT k, v FROM Props");
this.insertOrUpdate = this.connection.prepareStatement(
"MERGE INTO Props"
+ " USING (VALUES(?,?)) AS i(k,v) ON Props.k = i.k"
+ " WHEN MATCHED THEN UPDATE SET Props.v = i.v"
+ " WHEN NOT MATCHED THEN INSERT (k, v) VALUES (i.k, i.v)");
this.delete = this.connection.prepareStatement(
"DELETE FROM Props WHERE k=?");
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#setProperty(java
* .lang.String, java.lang.Object)
*/
@Override
public synchronized void setProperty(String propertyName, Object property)
{
this.setProperty(propertyName, property, false);
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#setProperty(java
* .lang.String, java.lang.Object, boolean)
*/
@Override
public synchronized void setProperty(String propertyName, Object property,
boolean isSystem)
{
// a property with the same name as an existing system property cannot
// be set, so mark it as a system property
if (!isSystem && System.getProperty(propertyName) != null)
{
isSystem = true;
}
if (isSystem)
{
if (property == null)
{
System.clearProperty(propertyName);
return;
}
System.setProperty(propertyName, property.toString());
}
else
{
if (immutableDefaultProperties.containsKey(propertyName))
{
return;
}
try
{
this.checkConnection();
Object oldValue = this.getProperty(propertyName);
this.fireVetoableChange(propertyName, oldValue, property);
if (property == null)
{
this.delete.setString(1, propertyName);
this.delete.execute();
}
else
{
this.insertOrUpdate.setString(1, propertyName);
this.insertOrUpdate.setString(2, property.toString());
this.insertOrUpdate.execute();
}
this.fireChange(propertyName, oldValue, property);
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
}
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#setProperties(java
* .util.Map)
*/
@Override
public synchronized void setProperties(Map<String, Object> properties)
{
try
{
checkConnection();
this.connection.setAutoCommit(false);
for (Map.Entry<String, Object> e : properties.entrySet())
{
this.setProperty(e.getKey(), e.getValue(), false);
}
this.connection.commit();
this.connection.setAutoCommit(true);
}
catch (SQLException e1)
{
throw new RuntimeException(e1);
}
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getProperty(java
* .lang.String)
*/
@Override
public synchronized Object getProperty(String propertyName)
{
Object value = immutableDefaultProperties.get(propertyName);
if (value != null)
{
return value;
}
try
{
this.checkConnection();
this.selectExact.setString(1, propertyName);
ResultSet q = this.selectExact.executeQuery();
if (q.next())
{
value = q.getString(1);
}
}
catch (SQLException e)
{
logger.error(e);
throw new RuntimeException(e);
}
if (value != null)
{
return value;
}
value = defaultProperties.get(propertyName);
if (value != null)
{
return value;
}
return System.getProperty(propertyName);
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#removeProperty(java
* .lang.String)
*/
@Override
public synchronized void removeProperty(String propertyName)
{
//remove all properties
for (String child : this.getPropertyNamesByPrefix(propertyName, false))
{
removeProperty(child);
}
this.setProperty(propertyName, null, false);
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getAllPropertyNames
* ()
*/
@Override
public List<String> getAllPropertyNames()
{
List<String> data = new ArrayList<String>(
immutableDefaultProperties.keySet());
data.addAll(defaultProperties.keySet());
try
{
this.checkConnection();
ResultSet q = this.selectAll.executeQuery();
while (q.next())
{
data.add(q.getString(1));
}
}
catch (SQLException e)
{
logger.error(e);
throw new RuntimeException(e);
}
return data;
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService
* #getPropertyNamesByPrefix(java.lang.String, boolean)
*/
@Override
public List<String> getPropertyNamesByPrefix(String prefix,
boolean exactPrefixMatch)
{
try
{
List<String> resultSet = new ArrayList<String>(50);
this.checkConnection();
this.selectLike.setString(1, prefix + "%");
ResultSet q = this.selectLike.executeQuery();
while (q.next())
{
String key = q.getString(1);
int ix = key.lastIndexOf('.');
if(ix == -1)
{
continue;
}
String keyPrefix = key.substring(0, ix);
if(exactPrefixMatch)
{
if(prefix.equals(keyPrefix))
{
resultSet.add(key);
}
}
else
{
if(keyPrefix.startsWith(prefix))
{
resultSet.add(key);
}
}
}
return resultSet;
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService
* #getPropertyNamesBySuffix(java.lang.String)
*/
@Override
public List<String> getPropertyNamesBySuffix(String suffix)
{
try
{
List<String> resultKeySet = new ArrayList<String>(20);
this.checkConnection();
this.selectLike.setString(1, "%" + suffix);
ResultSet q = this.selectLike.executeQuery();
while (q.next())
{
String key = q.getString(1);
int ix = key.lastIndexOf('.');
if (ix != -1 && suffix.equals(key.substring(ix + 1)))
resultKeySet.add(key);
}
return resultKeySet;
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getString(java.lang
* .String)
*/
@Override
public String getString(String propertyName)
{
String value = (String)this.getProperty(propertyName);
if (value != null)
{
value = value.trim();
if (value.length() == 0)
{
return null;
}
}
return value;
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getString(java.lang
* .String, java.lang.String)
*/
@Override
public String getString(String propertyName, String defaultValue)
{
String value = this.getString(propertyName);
if (value == null)
{
return defaultValue;
}
return value;
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getBoolean(java.
* lang.String, boolean)
*/
@Override
public boolean getBoolean(String propertyName, boolean defaultValue)
{
Object value = this.getProperty(propertyName);
if (value == null)
{
return defaultValue;
}
return Boolean.parseBoolean(value.toString());
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getInt(java.lang
* .String, int)
*/
@Override
public int getInt(String propertyName, int defaultValue)
{
Object value = this.getProperty(propertyName);
if (value == null)
{
return defaultValue;
}
return Integer.parseInt(value.toString());
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getLong(java.lang
* .String, long)
*/
@Override
public long getLong(String propertyName, long defaultValue)
{
Object value = this.getProperty(propertyName);
if (value == null)
{
return defaultValue;
}
return Long.parseLong(value.toString());
}
/*
* (non-Javadoc)
*
* @see org.jitsi.service.configuration.ConfigurationService#
* addPropertyChangeListener(java.beans.PropertyChangeListener)
*/
@Override
public void addPropertyChangeListener(PropertyChangeListener listener)
{
this.listeners.put(null, listener);
}
/*
* (non-Javadoc)
*
* @see org.jitsi.service.configuration.ConfigurationService#
* removePropertyChangeListener(java.beans.PropertyChangeListener)
*/
@Override
public void removePropertyChangeListener(PropertyChangeListener listener)
{
this.listeners.remove(null, listener);
}
/*
* (non-Javadoc)
*
* @see org.jitsi.service.configuration.ConfigurationService#
* addPropertyChangeListener(java.lang.String,
* java.beans.PropertyChangeListener)
*/
@Override
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener)
{
this.listeners.put(propertyName, listener);
}
/*
* (non-Javadoc)
*
* @see org.jitsi.service.configuration.ConfigurationService#
* removePropertyChangeListener(java.lang.String,
* java.beans.PropertyChangeListener)
*/
@Override
public void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener)
{
this.listeners.remove(propertyName, listener);
}
/*
* (non-Javadoc)
*
* @see org.jitsi.service.configuration.ConfigurationService#
* addVetoableChangeListener
* (org.jitsi.service.configuration.ConfigVetoableChangeListener)
*/
@Override
public void addVetoableChangeListener(ConfigVetoableChangeListener listener)
{
this.vetoListeners.put(null, listener);
}
/*
* (non-Javadoc)
*
* @see org.jitsi.service.configuration.ConfigurationService#
* removeVetoableChangeListener
* (org.jitsi.service.configuration.ConfigVetoableChangeListener)
*/
@Override
public void removeVetoableChangeListener(
ConfigVetoableChangeListener listener)
{
this.vetoListeners.remove(null, listener);
}
/*
* (non-Javadoc)
*
* @see org.jitsi.service.configuration.ConfigurationService#
* addVetoableChangeListener(java.lang.String,
* org.jitsi.service.configuration.ConfigVetoableChangeListener)
*/
@Override
public void addVetoableChangeListener(String propertyName,
ConfigVetoableChangeListener listener)
{
this.vetoListeners.put(propertyName, listener);
}
/*
* (non-Javadoc)
*
* @see org.jitsi.service.configuration.ConfigurationService#
* removeVetoableChangeListener(java.lang.String,
* org.jitsi.service.configuration.ConfigVetoableChangeListener)
*/
@Override
public void removeVetoableChangeListener(String propertyName,
ConfigVetoableChangeListener listener)
{
this.vetoListeners.remove(propertyName, listener);
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#storeConfiguration()
*/
@Override
public void storeConfiguration() throws IOException
{
try
{
this.connection.close();
}
catch (SQLException e)
{
logger.error(e);
}
finally
{
this.connection = null;
}
}
/**
* Does nothing. The database cannot be edited from the outside.
*/
@Override
public void reloadConfiguration() throws IOException
{
// nothing to do, the file cannot be edited outside
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#purgeStoredConfiguration
* ()
*/
@Override
public void purgeStoredConfiguration()
{
try
{
this.checkConnection();
Statement st = this.connection.createStatement();
st.executeUpdate("TRUNCATE TABLE Props");
}
catch (SQLException e)
{
logger.error(e);
throw new RuntimeException(e);
}
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getScHomeDirName()
*/
@Override
public String getScHomeDirName()
{
return System.getProperty(PNAME_SC_HOME_DIR_NAME);
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getScHomeDirLocation
* ()
*/
@Override
public String getScHomeDirLocation()
{
return System.getProperty(PNAME_SC_HOME_DIR_LOCATION);
}
/*
* (non-Javadoc)
*
* @see
* org.jitsi.service.configuration.ConfigurationService#getConfigurationFilename
* ()
*/
@Override
public String getConfigurationFilename()
{
return "props.hsql.script";
}
/**
* Loads the specified default properties maps from the Jitsi installation
* directory. Typically this file is to be called for the default properties
* and the admin overrides.
*
* @param fileName the name of the file we need to load.
*/
private void loadDefaultProperties(String fileName)
{
try
{
Properties fileProps = new Properties();
InputStream fileStream;
if(OSUtils.IS_ANDROID)
{
fileStream
= getClass().getClassLoader()
.getResourceAsStream(fileName);
}
else
{
fileStream = ClassLoader.getSystemResourceAsStream(fileName);
}
fileProps.load(fileStream);
fileStream.close();
// now get those properties and place them into the mutable and
// immutable properties maps.
for (Map.Entry<Object, Object> entry : fileProps.entrySet())
{
String name = (String) entry.getKey();
String value = (String) entry.getValue();
if ( name == null
|| value == null
|| name.trim().length() == 0)
{
continue;
}
if (name.startsWith("*"))
{
name = name.substring(1);
if(name.trim().length() == 0)
{
continue;
}
//it seems that we have a valid default immutable property
immutableDefaultProperties.put(name, value);
//in case this is an override, make sure we remove previous
//definitions of this property
defaultProperties.remove(name);
}
else
{
//this property is a regular, mutable default property.
defaultProperties.put(name, value);
//in case this is an override, make sure we remove previous
//definitions of this property
immutableDefaultProperties.remove(name);
}
}
}
catch (Exception ex)
{
//we can function without defaults so we are just logging those.
logger.info("No defaults property file loaded: " + fileName
+ ". Not a problem.");
if(logger.isDebugEnabled())
logger.debug("load exception", ex);
}
}
/**
* Notify all listening objects about a prospective change.
*
* @param propertyName The property that is going to change.
* @param oldValue The previous value of the property (can be <tt>null</tt>)
* @param newValue The new value of the property (can be <tt>null</tt>)
*/
private void fireVetoableChange(String propertyName,
Object oldValue, Object newValue)
{
PropertyChangeEvent evt = new PropertyChangeEvent(
this,
propertyName,
oldValue,
newValue);
for (ConfigVetoableChangeListener l : vetoListeners.get(propertyName))
{
l.vetoableChange(evt);
}
for (ConfigVetoableChangeListener l : vetoListeners.get(null))
{
l.vetoableChange(evt);
}
}
/**
* Notify all listeners that a property has changed.
*
* @param propertyName The property that has just changed.
* @param oldValue The previous value of the property (can be <tt>null</tt>)
* @param newValue The new value of the property (can be <tt>null</tt>)
*/
private void fireChange(String propertyName,
Object oldValue, Object newValue)
{
PropertyChangeEvent evt = new PropertyChangeEvent(
this,
propertyName,
oldValue,
newValue);
for (PropertyChangeListener l : listeners.get(propertyName))
{
l.propertyChange(evt);
}
for (PropertyChangeListener l : listeners.get(null))
{
l.propertyChange(evt);
}
}
}

@ -5,7 +5,12 @@ Bundle-Vendor: jitsi.org
Bundle-Version: 0.0.1
Bundle-SymbolicName: net.java.sip.communicator.configuration
Import-Package: org.jitsi.service.configuration,
org.jitsi.service.fileaccess,
org.jitsi.service.libjitsi,
net.java.sip.communicator.util,
org.jitsi.util,
com.sun.jna,
org.osgi.framework
org.osgi.framework,
org.hsqldb,
org.hsqldb.jdbc,
com.google.common.collect

Loading…
Cancel
Save