/* * 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(); } } }