blob: cb81fa23710d5d066a75a4020cf94d5d8fd4ba0f [file] [log] [blame]
/*
* 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.prefs.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.felix.prefs.BackingStoreManager;
import org.apache.felix.prefs.PreferencesDescription;
import org.apache.felix.prefs.PreferencesImpl;
import org.osgi.framework.BundleContext;
import org.osgi.service.prefs.BackingStoreException;
/**
* This implementating of the backing store uses the bundle mechanism to store
* binary data.
*/
public class DataFileBackingStoreImpl extends StreamBackingStoreImpl {
/** The root directory (or null if not available) */
protected final File rootDirectory;
public DataFileBackingStoreImpl(BundleContext context) {
super(context);
this.rootDirectory = context.getDataFile("");
}
public DataFileBackingStoreImpl(BundleContext context, File rootDirectory) {
super(context);
this.rootDirectory = rootDirectory;
}
/**
* @see org.apache.felix.sandbox.preferences.impl.StreamBackingStoreImpl#checkAccess()
*/
@Override
protected void checkAccess() throws BackingStoreException {
if ( this.rootDirectory == null ) {
throw new BackingStoreException("Saving of data files to the bundle context is currently not supported.");
}
}
/**
* @see org.apache.felix.sandbox.preferences.impl.StreamBackingStoreImpl#getOutputStream(org.apache.felix.sandbox.preferences.PreferencesDescription)
*/
@Override
protected OutputStream getOutputStream(PreferencesDescription desc) throws IOException {
final File file = this.getFile(desc);
return getFileOutputStream(file);
}
/**
* @see org.apache.felix.prefs.BackingStore#availableBundles()
*/
public Long[] availableBundles() {
// If the root directory is not available, then we do nothing!
try {
this.checkAccess();
} catch (BackingStoreException ignore) {
return new Long[0];
}
final Set<Long> bundleIds = new HashSet<Long>();
final File[] children = getFilesList(this.rootDirectory);
for( int i=0; i<children.length; i++ ) {
final File current = children[i];
final PreferencesDescription desc = this.getDescription(current);
if ( desc != null ) {
bundleIds.add(desc.getBundleId());
}
}
return bundleIds.toArray(new Long[bundleIds.size()]);
}
protected PreferencesDescription getDescription(File file) {
final String fileName = file.getName();
// parse the file name to get: bundle id, user|system identifer
if ( fileName.startsWith("P") && fileName.endsWith(".ser") ) {
final String name = fileName.substring(1, fileName.length() - 4);
final String key;
final String identifier;
int pos = name.indexOf("_");
if ( pos != -1 ) {
identifier = name.substring(pos+1);
key = name.substring(0, pos);
} else {
key = name;
identifier = null;
}
final Long bundleId = Long.valueOf(key);
return new PreferencesDescription(bundleId, identifier);
}
return null;
}
/**
* @see org.apache.felix.prefs.BackingStore#remove(java.lang.Long)
*/
public void remove(Long bundleId) throws BackingStoreException {
this.checkAccess();
final File[] children = getFilesList(this.rootDirectory);
for( int i=0; i<children.length; i++ ) {
final File current = children[i];
final PreferencesDescription desc = this.getDescription(current);
if ( desc != null ) {
if ( desc.getBundleId().equals(bundleId) ) {
current.delete();
}
}
}
}
/**
* @see org.apache.felix.prefs.BackingStore#loadAll(org.apache.felix.prefs.BackingStoreManager, java.lang.Long)
*/
public PreferencesImpl[] loadAll(BackingStoreManager manager, Long bundleId) throws BackingStoreException {
this.checkAccess();
final List<PreferencesImpl> list = new ArrayList<PreferencesImpl>();
final File[] children = getFilesList(this.rootDirectory);
for( int i=0; i<children.length; i++ ) {
final File current = children[i];
final PreferencesDescription desc = this.getDescription(current);
if ( desc != null ) {
if ( desc.getBundleId().equals(bundleId) ) {
final PreferencesImpl root = new PreferencesImpl(desc, manager);
try {
final FileInputStream fis = getFileInputStream(current);
this.read(root, fis);
fis.close();
} catch (IOException ioe) {
throw new BackingStoreException("Unable to load preferences.", ioe);
}
list.add(root);
}
}
}
return list.toArray(new PreferencesImpl[list.size()]);
}
/**
* @see org.apache.felix.prefs.BackingStore#load(org.apache.felix.prefs.BackingStoreManager, org.apache.felix.prefs.PreferencesDescription)
*/
public PreferencesImpl load(BackingStoreManager manager, PreferencesDescription desc) throws BackingStoreException {
this.checkAccess();
final File file = this.getFile(desc);
if ( fileExists(file).booleanValue() ) {
try {
final PreferencesImpl root = new PreferencesImpl(desc, manager);
final FileInputStream fis = getFileInputStream(file);
this.read(root, fis);
fis.close();
return root;
} catch (IOException ioe) {
throw new BackingStoreException("Unable to load preferences.", ioe);
}
}
return null;
}
/**
* Get the file fo the preferences tree.
* @param desc
* @return
*/
protected File getFile(PreferencesDescription desc) {
final StringBuffer buffer = new StringBuffer("P");
buffer.append(desc.getBundleId());
if ( desc.getIdentifier() != null ) {
buffer.append('_');
buffer.append(desc.getIdentifier());
}
buffer.append(".ser");
final File file = new File(this.rootDirectory, buffer.toString());
return file;
}
// few utility methods to access File APIs from a privileged block
private static File[] getFilesList(final File file) {
return AccessController.doPrivileged(new PrivilegedAction<File[]>() {
public File[] run() {
return file.listFiles();
}
});
}
private static Boolean fileExists(final File file) {
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
return (file.exists() ? Boolean.TRUE : Boolean.FALSE);
}
});
}
private static FileInputStream getFileInputStream(final File file) throws IOException {
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<FileInputStream>() {
public FileInputStream run() throws FileNotFoundException {
return new FileInputStream(file);
}
});
} catch (PrivilegedActionException e) {
// e.getException() should be an instance of FileNotFoundException, as
// only "checked" exceptions will be "wrapped" in PrivilegedActionException.
throw (FileNotFoundException) e.getException();
}
}
private static FileOutputStream getFileOutputStream(final File file) throws IOException {
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<FileOutputStream>() {
public FileOutputStream run() throws FileNotFoundException {
return new FileOutputStream(file);
}
});
} catch (PrivilegedActionException e) {
// e.getException() should be an instance of FileNotFoundException, as
// only "checked" exceptions will be "wrapped" in PrivilegedActionException.
throw (FileNotFoundException) e.getException();
}
}
}