blob: 38dd965dd56c38fd3908b32b7bc838c7043c39af [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2018 Cognos Incorporated, IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Cognos Incorporated - initial API and implementation
* IBM Corporation - bug fixes and enhancements
*******************************************************************************/
package org.eclipse.equinox.internal.cm;
import java.io.*;
import java.security.*;
import java.util.*;
import org.eclipse.equinox.internal.cm.reliablefile.*;
import org.osgi.framework.*;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.log.LogService;
/**
* ConfigurationStore manages all active configurations along with persistence. The current
* implementation uses a filestore and serialization of the configuration dictionaries to files
* identified by their pid. Persistence details are in the constructor, saveConfiguration, and
* deleteConfiguration and can be factored out separately if required.
*/
class ConfigurationStore {
private final ConfigurationAdminFactory configurationAdminFactory;
private static final String STORE_DIR = "store"; //$NON-NLS-1$
private static final String DATA_PRE = "data"; //$NON-NLS-1$
private static final String CFG_EXT = ".cfg"; //$NON-NLS-1$
private final Map<String, ConfigurationImpl> configurations = new HashMap<>();
private int createdPidCount = 0;
private final File store;
public ConfigurationStore(ConfigurationAdminFactory configurationAdminFactory, BundleContext context) {
this.configurationAdminFactory = configurationAdminFactory;
store = context.getDataFile(STORE_DIR);
if (store == null)
return; // no persistent store
store.mkdir();
File[] configurationFiles = store.listFiles();
for (int i = 0; i < configurationFiles.length; ++i) {
String configurationFileName = configurationFiles[i].getName();
if (!configurationFileName.endsWith(CFG_EXT))
continue;
InputStream ris = null;
ObjectInputStream ois = null;
boolean deleteFile = false;
try {
ris = new ReliableFileInputStream(configurationFiles[i]);
ois = new ObjectInputStream(ris);
@SuppressWarnings("unchecked")
Dictionary<String, Object> dictionary = (Dictionary<String, Object>) ois.readObject();
// before adding, make sure the bundle exists if the location is set
String location = (String) dictionary.get(ConfigurationAdmin.SERVICE_BUNDLELOCATION);
if (location != null && context.getBundle(location) == null) {
Boolean boundProp = (Boolean) dictionary.remove(ConfigurationImpl.LOCATION_BOUND);
if (boundProp != null && boundProp.booleanValue()) {
dictionary.remove(ConfigurationAdmin.SERVICE_BUNDLELOCATION);
}
}
ConfigurationImpl config = new ConfigurationImpl(configurationAdminFactory, this, dictionary, configurationFiles[i]);
configurations.put(config.getPid(), config);
} catch (IOException e) {
String message = e.getMessage();
String pid = configurationFileName.substring(0, configurationFileName.length() - 4);
String errorMessage = "{Configuration Admin - pid = " + pid + "} could not be restored." + ((message == null) ? "" : " " + message); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
configurationAdminFactory.log(LogService.LOG_ERROR, errorMessage);
deleteFile = true;
} catch (ClassNotFoundException e) {
configurationAdminFactory.log(LogService.LOG_ERROR, e.getMessage());
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
// ignore
}
}
if (ris != null) {
try {
ris.close();
} catch (IOException e) {
// ignore
}
}
}
if (deleteFile) {
ReliableFile.delete(configurationFiles[i]);
configurationFiles[i].delete();
}
}
}
public Object saveConfiguration(String pid, ConfigurationImpl config, final Object token) throws IOException {
if (store == null)
return null; // no persistent store
config.checkLocked();
final Dictionary<String, Object> configProperties = config.getAllProperties(true);
if (configProperties == null) {
return null;
}
try {
final File storeCopy = store;
return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
@Override
public File run() throws Exception {
File toFile = token == null ? File.createTempFile(DATA_PRE, CFG_EXT, storeCopy) : (File) token;
writeConfigurationFile(toFile, configProperties);
return toFile;
}
});
} catch (PrivilegedActionException e) {
throw (IOException) e.getException();
}
}
void writeConfigurationFile(File configFile, Dictionary<String, Object> configProperties) throws IOException {
OutputStream ros = null;
ObjectOutputStream oos = null;
try {
configFile.createNewFile();
ros = new ReliableFileOutputStream(configFile);
oos = new ObjectOutputStream(ros);
oos.writeObject(configProperties);
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
// ignore
}
}
if (ros != null) {
try {
ros.close();
} catch (IOException e) {
// ignore
}
}
}
}
public synchronized void removeConfiguration(String pid, final Object token) {
configurations.remove(pid);
if (store == null || token == null)
return; // no persistent store
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
deleteConfigurationFile((File) token);
return null;
}
});
}
void deleteConfigurationFile(File configFile) {
ReliableFile.delete(configFile);
configFile.delete();
}
public synchronized ConfigurationImpl getConfiguration(String pid, String location, boolean bind) {
ConfigurationImpl config = configurations.get(pid);
if (config == null) {
config = new ConfigurationImpl(configurationAdminFactory, this, null, pid, location, bind);
configurations.put(pid, config);
}
return config;
}
public synchronized ConfigurationImpl createFactoryConfiguration(String factoryPid, String location, boolean bind) {
String pid = factoryPid + "-" + new Date().getTime() + "-" + createdPidCount++; //$NON-NLS-1$ //$NON-NLS-2$
ConfigurationImpl config = new ConfigurationImpl(configurationAdminFactory, this, factoryPid, pid, location, bind);
configurations.put(pid, config);
return config;
}
public synchronized ConfigurationImpl findConfiguration(String pid) {
return configurations.get(pid);
}
public ConfigurationImpl[] getFactoryConfigurations(String factoryPid) {
List<ConfigurationImpl> resultList = new ArrayList<>();
synchronized (this) {
resultList.addAll(configurations.values());
}
for (Iterator<ConfigurationImpl> it = resultList.iterator(); it.hasNext();) {
ConfigurationImpl config = it.next();
String otherFactoryPid = config.getFactoryPid();
if (otherFactoryPid == null || !otherFactoryPid.equals(factoryPid))
it.remove();
}
return resultList.toArray(new ConfigurationImpl[resultList.size()]);
}
public ConfigurationImpl[] listConfigurations(Filter filter) {
List<ConfigurationImpl> resultList = new ArrayList<>();
synchronized (this) {
resultList.addAll(configurations.values());
}
for (Iterator<ConfigurationImpl> it = resultList.iterator(); it.hasNext();) {
ConfigurationImpl config = it.next();
Dictionary<String, Object> properties = config.getAllProperties(false);
if (properties == null || !filter.match(properties)) {
it.remove();
}
}
int size = resultList.size();
return size == 0 ? null : (ConfigurationImpl[]) resultList.toArray(new ConfigurationImpl[size]);
}
public void unbindConfigurations(Bundle bundle) {
ConfigurationImpl[] copy;
synchronized (this) {
copy = configurations.values().toArray(new ConfigurationImpl[configurations.size()]);
}
for (ConfigurationImpl config : copy) {
config.unbind(bundle);
}
}
}