blob: 69129ae388ecc0f6ba1c8609272ed4a5a845d637 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1997-2009 by ProSyst Software GmbH
* http://www.prosyst.com
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* ProSyst Software GmbH - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.internal.ds.storage.file;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.Dictionary;
import java.util.Vector;
import org.eclipse.equinox.internal.ds.*;
import org.eclipse.equinox.internal.ds.model.ServiceComponent;
import org.eclipse.equinox.internal.util.io.ExternalizableDictionary;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.log.LogService;
/**
* This class implements a cache for the parsed component XML descriptions.
*
* @author Pavlin Dobrev
* @author Stoyan Boshev
*/
public class FileStorage extends ComponentStorage {
//TODO: this constant should be public and shared across other bundles that use the same property.
//Probably it should be in the supplement bundle?
public static final String PROP_CHECK_CONFIG = "osgi.checkConfiguration"; //$NON-NLS-1$
private static String CUSTOM_DB_NAME = "SCR"; //$NON-NLS-1$
private BundleContext bc = null;
private ExternalizableDictionary data = new ExternalizableDictionary();
private StringBuffer pathBuffer = new StringBuffer();
private String separator;
private boolean isDirty = false;
public FileStorage(BundleContext bc) {
this.bc = bc;
separator = bc.getProperty("path.separator"); //$NON-NLS-1$
File file = bc.getDataFile(CUSTOM_DB_NAME);
FileInputStream fis = null;
try {
if (file != null && file.exists()) {
data.readObject(new BufferedInputStream(fis = new FileInputStream(file)));
}
} catch (IOException e) {
Activator.log(null, LogService.LOG_ERROR, NLS.bind(Messages.ERROR_LOADING_DATA_FILE, file.getAbsolutePath()), e);
} catch (Exception e) {
Activator.log(null, LogService.LOG_ERROR, NLS.bind(Messages.ERROR_LOADING_DATA_FILE, file.getAbsolutePath()), e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (Exception e) {
// ignore
}
}
}
}
public Vector loadComponentDefinitions(Bundle bundle, String dsHeader) {
try {
Vector components = null;
if (!Activator.DBSTORE) {
return parseXMLDeclaration(bundle, dsHeader);
}
long lastModified;
// if not dev mode, we simply use the bundle's timestamp
if (!Activator.getBoolean(PROP_CHECK_CONFIG)) {
lastModified = bundle.getLastModified();
} else {
lastModified = getLastModifiedTimestamp(bundle);
}
String[] dbBundlePath = new String[1];
dbBundlePath[0] = String.valueOf(bundle.getBundleId());
String lastModifiedValue = (String) data.get(getPath(dbBundlePath));
if (lastModifiedValue == null) {
components = parseXMLDeclaration(bundle, dsHeader);
if (components != null && components.size() != 0) {
data.put(getPath(dbBundlePath), "" + lastModified); //$NON-NLS-1$
saveComponentDefinitions(components, bundle.getBundleId());
}
} else {
long dbLastModified = Long.parseLong(lastModifiedValue);
if (lastModified != dbLastModified) {
components = parseXMLDeclaration(bundle, dsHeader);
if (components != null && components.size() != 0) {
data.put(getPath(dbBundlePath), "" + lastModified); //$NON-NLS-1$
saveComponentDefinitions(components, bundle.getBundleId());
}
} else {
components = loadComponentsFromDB(bundle);
}
}
return components;
} catch (Throwable e) {
Activator.log(null, LogService.LOG_ERROR, NLS.bind(Messages.PROCESSING_BUNDLE_FAILED, Long.toString(bundle.getBundleId()), bundle), e);
return null;
}
}
private Vector loadComponentsFromDB(Bundle bundle) throws Exception {
try {
String[] dbCompPath = new String[] {null, "COMPONENTS"}; //$NON-NLS-1$
ServiceComponent currentComponent = null;
long bundleId = bundle.getBundleId();
dbCompPath[0] = String.valueOf(bundleId);
DBObject value = new DBObject();
byte[] byteArr = (byte[]) data.get(getPath(dbCompPath));
ByteArrayInputStream tmpIn = new ByteArrayInputStream(byteArr);
value.readObject(tmpIn);
Vector components = value.components;
if (components == null) {
return null;
}
for (int i = 0; i < components.size(); i++) {
currentComponent = (ServiceComponent) components.elementAt(i);
currentComponent.bundle = bundle;
currentComponent.bc = bundle.getBundleContext();
}
return components;
} catch (Throwable t) {
Activator.log(null, LogService.LOG_ERROR, Messages.ERROR_LOADING_COMPONENTS, t);
}
return null;
}
public void deleteComponentDefinitions(long bundleID) {
String[] dbBundlePath = new String[1];
dbBundlePath[0] = String.valueOf(bundleID);
data.remove(getPath(dbBundlePath));
String[] dbCompPath = new String[] {null, "COMPONENTS"}; //$NON-NLS-1$
dbCompPath[0] = String.valueOf(bundleID);
data.remove(getPath(dbCompPath));
File file = bc.getDataFile(CUSTOM_DB_NAME);
if (file != null && file.exists()) {
//delete the file to prevent leaving old information in it
file.delete();
}
isDirty = true;
}
private void saveComponentDefinitions(Vector components, long bundleID) throws Exception {
try {
if (components == null || components.size() == 0) {
return;
}
String[] dbCompPath = new String[] {null, "COMPONENTS"}; //$NON-NLS-1$
dbCompPath[0] = String.valueOf(bundleID);
DBObject tmpObj = new DBObject(components);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
tmpObj.writeObject(buf);
data.put(getPath(dbCompPath), buf.toByteArray());
isDirty = true;
} catch (Exception e) {
Activator.log(null, LogService.LOG_ERROR, Messages.ERROR_SAVING_COMPONENT_DEFINITIONS, e);
}
}
public void stop() {
if (isDirty) {
saveFile();
}
}
private void saveFile() {
FileOutputStream fos = null;
try {
File file = bc.getDataFile(CUSTOM_DB_NAME);
if (file == null) {
//save operation is not possible
return;
}
fos = new FileOutputStream(file);
try {
data.writeObject(fos);
isDirty = false;
} catch (Exception e) {
Activator.log(null, LogService.LOG_ERROR, Messages.ERROR_WRITING_OBJECT, e);
}
} catch (FileNotFoundException e) {
Activator.log(null, LogService.LOG_ERROR, Messages.FILE_DOESNT_EXIST_OR_DIRECTORY, e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
//ignore
}
}
}
}
private String getPath(String path[]) {
synchronized (pathBuffer) {
pathBuffer.setLength(0);
for (int i = 0; i < path.length; i++) {
pathBuffer.append(path[i]).append(separator);
}
return pathBuffer.toString();
}
}
/**
* The last modified timestamp of the bundle. Should only be called in development mode
*
* @param bundle
* @return the last modified timestamp of the bundle
*/
protected long getLastModifiedTimestamp(Bundle bundle) {
if (bundle == null)
return 0;
long result = 0;
ManifestElement[] elements = parseManifestHeader(bundle);
for (int i = 0; i < elements.length; i++) {
URL componentURL = bundle.getEntry(elements[i].getValue());
if (componentURL != null) {
try {
URLConnection connection = componentURL.openConnection();
long lastModified = connection.getLastModified();
if (lastModified > result)
result = lastModified;
} catch (IOException e) {
//last modified cannot be calculated. should force reparse
return Long.MAX_VALUE;
}
}
}
return result;
}
private ManifestElement[] parseManifestHeader(Bundle bundle) {
Dictionary headers = bundle.getHeaders(""); //$NON-NLS-1$
String files = (String) headers.get(ComponentConstants.SERVICE_COMPONENT);
if (files == null)
return new ManifestElement[0];
try {
return ManifestElement.parseHeader(ComponentConstants.SERVICE_COMPONENT, files);
} catch (BundleException e) {
Activator.log(bundle.getBundleContext(), LogService.LOG_ERROR, Messages.ERROR_PARSING_MANIFEST_HEADER, e);
return new ManifestElement[0];
}
}
}