All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.osgi.internal.location.BasicLocation Maven / Gradle / Ivy

There is a newer version: 1.9.3.RC1
Show newest version
/*******************************************************************************
 * Copyright (c) 2004, 2022 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:
 *     IBM Corporation - initial API and implementation
 *     Christoph Läubrich - Issue #38 - Expose the url of a location as service properties
 *******************************************************************************/
package org.eclipse.osgi.internal.location;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration.ConfigValues;
import org.eclipse.osgi.internal.framework.EquinoxContainer;
import org.eclipse.osgi.internal.log.EquinoxLogServices;
import org.eclipse.osgi.internal.messages.Msg;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

/**
 * Internal class.
 */
public class BasicLocation implements Location {
	private static String DEFAULT_LOCK_FILENAME = ".metadata/.lock"; //$NON-NLS-1$

	final private boolean isReadOnly;
	final private URL defaultValue;
	final private String property;
	final private String dataAreaPrefix;
	final private ConfigValues configValues;
	final private AtomicBoolean debug;
	final private EquinoxContainer container;

	private URL location;
	private Location parent;

	// locking related fields
	private File lockFile;
	private Locker locker;

	private ServiceRegistration serviceRegistration;

	public BasicLocation(String property, URL defaultValue, boolean isReadOnly, String dataAreaPrefix,
			ConfigValues configValues, EquinoxContainer container, AtomicBoolean debug) {
		this.property = property;
		this.defaultValue = defaultValue;
		this.isReadOnly = isReadOnly;
		// make sure the prefix ends with '/' if it is not empty/null
		String tempDataAreaPrefix = dataAreaPrefix == null ? "" : dataAreaPrefix; //$NON-NLS-1$
		tempDataAreaPrefix = tempDataAreaPrefix.replace('\\', '/');
		if (tempDataAreaPrefix.length() > 0 && tempDataAreaPrefix.charAt(tempDataAreaPrefix.length() - 1) != '/') {
			tempDataAreaPrefix += '/';
		}
		this.dataAreaPrefix = tempDataAreaPrefix;
		this.configValues = configValues;
		this.container = container;
		this.debug = debug;
	}

	@Override
	public boolean allowsDefault() {
		return defaultValue != null;
	}

	@Override
	public URL getDefault() {
		return defaultValue;
	}

	@Override
	public synchronized Location getParentLocation() {
		return parent;
	}

	@Override
	public synchronized URL getURL() {
		if (location == null && defaultValue != null) {
			if (debug.get()) {
				EquinoxLogServices logServices = container.getLogServices();
				// Note that logServices can be null if we are very early in the startup.
				if (logServices != null) {
					logServices.log(EquinoxContainer.NAME, FrameworkLogEntry.INFO,
							"Called Location.getURL() when it has not been set for: \"" + property + "\"", //$NON-NLS-1$//$NON-NLS-2$
							new RuntimeException("Call stack for Location.getURL()")); //$NON-NLS-1$
				}
			}
			setURL(defaultValue, false);
		}
		return location;
	}

	private synchronized URL getLocation() {
		return location;
	}

	@Override
	public synchronized boolean isSet() {
		return location != null;
	}

	@Override
	public boolean isReadOnly() {
		return isReadOnly;
	}

	/**
	 * @deprecated
	 */
	@Override
	public boolean setURL(URL value, boolean lock) throws IllegalStateException {
		try {
			return set(value, lock);
		} catch (IOException e) {
			return false;
		}
	}

	@Override
	public synchronized boolean set(URL value, boolean lock) throws IllegalStateException, IOException {
		return set(value, lock, null);
	}

	@Override
	public synchronized boolean set(URL value, boolean lock, String lockFilePath)
			throws IllegalStateException, IOException {
		boolean gotLock = false;
		synchronized (this) {
			if (location != null)
				throw new IllegalStateException(Msg.ECLIPSE_CANNOT_CHANGE_LOCATION);
			File file = null;
			if (value.getProtocol().equalsIgnoreCase("file")) { //$NON-NLS-1$
				try {
					File f = LocationHelper.decodePath(new File(value.getPath()));
					String basePath = f.getCanonicalPath();
					value = LocationHelper.buildURL("file:" + basePath, true); //$NON-NLS-1$
				} catch (IOException e) {
					// do nothing just use the original value
				}
				if (lockFilePath != null && lockFilePath.length() > 0) {
					File givenLockFile = new File(lockFilePath);
					if (givenLockFile.isAbsolute()) {
						file = givenLockFile;
					} else {
						file = new File(value.getPath(), lockFilePath);
					}
				} else {
					file = new File(value.getPath(), DEFAULT_LOCK_FILENAME);
				}
			}
			if (lock && !isReadOnly) {
				if (!lock(file, value)) {
					return false;
				}
				gotLock = true;
			}
			lockFile = file;
			location = value;
			if (property != null) {
				configValues.setConfiguration(property, location.toExternalForm());
			}
		}
		updateUrl(serviceRegistration);
		return lock ? gotLock : true;
	}

	private void updateUrl(ServiceRegistration registration) {
		if (registration != null) {
			Dictionary properties = registration.getReference().getProperties();
			properties.put(Location.SERVICE_PROPERTY_URL, getLocation().toExternalForm());
			try {
				registration.setProperties(properties);
			} catch (IllegalStateException e) {
				// can't update it then...
			}
		}
	}

	public synchronized void setParent(Location value) {
		parent = value;
	}

	@Override
	public synchronized boolean lock() throws IOException {
		if (!isSet())
			throw new IOException(Msg.location_notSet);
		return lock(lockFile, location);
	}

	@Override
	public synchronized boolean isLocked() throws IOException {
		if (!isSet())
			return false;
		return isLocked(lockFile);
	}

	/*
	 * This must be called while holding the synchronization lock for (this)
	 */
	private boolean lock(File lock, URL locationValue) throws IOException {
		if (isReadOnly)
			throw new IOException(NLS.bind(Msg.location_folderReadOnly, lock));
		if (lock == null) {
			if (locationValue != null && !"file".equalsIgnoreCase(locationValue.getProtocol())) //$NON-NLS-1$
				throw new IOException(NLS.bind(Msg.location_notFileProtocol, locationValue));
			throw new IllegalStateException(Msg.location_noLockFile); // this is really unexpected
		}
		if (isLocked())
			return false;
		File parentFile = new File(lock.getParent());
		if (!parentFile.isDirectory()) {
			parentFile.mkdirs();
			if (!parentFile.isDirectory())
				throw new IOException(NLS.bind(Msg.location_folderReadOnly, parentFile));
		}
		setLocker(lock);
		if (locker == null)
			return true;
		boolean locked = false;
		try {
			locked = locker.lock();
			return locked;
		} finally {
			if (!locked)
				locker = null;
		}
	}

	/*
	 * This must be called while holding the synchronization lock for (this)
	 */
	private boolean isLocked(File lock) throws IOException {
		if (lock == null || isReadOnly)
			return true;
		if (!lock.exists())
			return false;
		setLocker(lock);
		return locker.isLocked();
	}

	/*
	 * This must be called while holding the synchronization lock for (this)
	 */
	private void setLocker(File lock) {
		if (locker != null)
			return;
		String lockMode = configValues.getConfiguration(LocationHelper.PROP_OSGI_LOCKING, LocationHelper.LOCKING_NIO);
		locker = LocationHelper.createLocker(lock, lockMode, debug.get());
	}

	@Override
	public synchronized void release() {
		if (locker != null)
			locker.release();
	}

	@Override
	public Location createLocation(Location parentLocation, URL defaultLocation, boolean readonly) {
		BasicLocation result = new BasicLocation(null, defaultLocation, readonly, dataAreaPrefix, configValues,
				container, debug);
		result.setParent(parentLocation);
		return result;
	}

	@Override
	public URL getDataArea(String filename) throws IOException {
		URL base = getURL();
		if (base == null)
			throw new IOException(Msg.location_notSet);
		String prefix = base.toExternalForm();
		if (prefix.length() > 0 && prefix.charAt(prefix.length() - 1) != '/')
			prefix += '/';
		filename = filename.replace('\\', '/');
		if (filename.length() > 0 && filename.charAt(0) == '/')
			filename = filename.substring(1);
		String spec = prefix + dataAreaPrefix + filename;
		boolean trailingSlash = spec.length() > 0 && spec.charAt(spec.length() - 1) == '/';
		return LocationHelper.buildURL(spec, trailingSlash);
	}

	public void register(BundleContext bundleContext) {
		Dictionary locationProperties = new Hashtable<>(3);
		locationProperties.put(Location.SERVICE_PROPERTY_TYPE, property);
		URL url = getLocation();
		URL defaultUrl = getDefault();
		if (url != null) {
			locationProperties.put(Location.SERVICE_PROPERTY_URL, url.toExternalForm());
		}
		if (defaultUrl != null) {
			locationProperties.put(Location.SERVICE_PROPERTY_DEFAULT_URL, defaultUrl.toExternalForm());
		}
		ServiceRegistration register = bundleContext.registerService(Location.class, this, locationProperties);
		synchronized (this) {
			serviceRegistration = register;
		}
		// just paranoid here... checking if between we have registered and where able
		// to set the serviceRegistration the url has changed...
		if (url != getLocation()) {
			updateUrl(register);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy