You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2009/06/27 17:53:26 UTC
svn commit: r788992 [13/25] - in /incubator/ace/trunk: gateway/ gateway/src/
gateway/src/net/ gateway/src/net/luminis/ gateway/src/net/luminis/liq/
gateway/src/net/luminis/liq/bootstrap/
gateway/src/net/luminis/liq/bootstrap/multigateway/ gateway/src/n...
Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repositoryuseradmin/impl/RoleImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repositoryuseradmin/impl/RoleImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repositoryuseradmin/impl/RoleImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repositoryuseradmin/impl/RoleImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,179 @@
+package net.luminis.liq.client.repositoryuseradmin.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/**
+ * RoleImpl works as an implements of Role, User and Group for external purposes, and
+ * as a value object for use by the {@link RepositoryUserAdminImpl}.
+ */
+public class RoleImpl implements Role, User, Group {
+ private final String m_name;
+ private final int m_type;
+ private final StringOnlyDictionary m_properties = new StringOnlyDictionary();
+ private final StringOnlyDictionary m_credentials = new StringOnlyDictionary();
+ private final Set<Role> m_members = new HashSet<Role>();
+
+ public RoleImpl(String name, int type) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name can not be null");
+ }
+ m_name = name;
+ m_type = type;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Dictionary getProperties() {
+ return m_properties;
+ }
+
+ public int getType() {
+ return m_type;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Dictionary getCredentials() {
+ return m_credentials;
+ }
+
+ public boolean hasCredential(String key, Object value) {
+ if (value == null) {
+ return false;
+ }
+
+ // Credentials can be both Strings or byte[] s.
+ Object credential = m_credentials.get(key);
+ if (credential instanceof String) {
+ return ((String) credential).equals(value);
+ }
+ else if (credential instanceof byte[]) {
+ return Arrays.equals((byte[]) value, (byte[]) credential);
+ }
+
+ return false;
+ }
+
+ public boolean addMember(Role role) {
+ return m_members.add(role);
+ }
+
+ public boolean addRequiredMember(Role role) {
+ throw new UnsupportedOperationException("addRequiredMember is not supported by RepositoryUserAdmin.");
+ }
+
+ public Role[] getMembers() {
+ List<Role> result = new ArrayList<Role>();
+ for (Role role : m_members) {
+ result.add(role);
+ }
+ return result.toArray(new Role[result.size()]);
+ }
+
+ public Role[] getRequiredMembers() {
+ throw new UnsupportedOperationException("getRequiredMembers is not supported by RepositoryUserAdmin.");
+ }
+
+ public boolean removeMember(Role role) {
+ return m_members.remove(role);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RoleImpl)) {
+ return false;
+ }
+ return m_name.equals(((RoleImpl) other).m_name) && (m_type == ((RoleImpl) other).m_type);
+ }
+
+ /**
+ * A specialization of the dictionary that only supports String keys,
+ * and String or byte[] values.
+ */
+ @SuppressWarnings("unchecked")
+ private static final class StringOnlyDictionary extends Dictionary {
+ private final Dictionary m_dict = new Hashtable<String, String>();
+
+ @Override
+ public Enumeration elements() {
+ return m_dict.elements();
+ }
+
+ @Override
+ public Object get(Object key) {
+ return m_dict.get(key);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return m_dict.isEmpty();
+ }
+
+ @Override
+ public Enumeration keys() {
+ return m_dict.keys();
+ }
+
+ @Override
+ public Object put(Object key, Object value) {
+ if (!(key instanceof String)) {
+ throw new IllegalArgumentException("key should be of type String, not " + key.getClass().getName());
+ }
+ if (!(value instanceof String) && !(value instanceof byte[])) {
+ throw new IllegalArgumentException("value should be of type String or byte[], not " + value.getClass().getName());
+ }
+ return m_dict.put(key, value);
+ }
+
+ @Override
+ public Object remove(Object key) {
+ if (!(key instanceof String)) {
+ throw new IllegalArgumentException("key should be of type String, not " + key.getClass().getName());
+ }
+ return m_dict.remove(key);
+ }
+
+ @Override
+ public int size() {
+ return m_dict.size();
+ }
+ }
+
+ /**
+ * Determines the names of the groups that this role is a member of.
+ */
+ String[] getMemberships(RepositoryUserAdminImpl parent) {
+ // TODO For performance reasons, we could cache this list in the future.
+ List<String> result = new ArrayList<String>();
+ try {
+ for (Role role : parent.getRoles(null)) {
+ if (role instanceof Group) {
+ for (Role member : ((Group) role).getMembers()) {
+ if (equals(member)) {
+ result.add(role.getName());
+ }
+ }
+ }
+ }
+ }
+ catch (InvalidSyntaxException e) {
+ // will not happen, since we pass in a null filter
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/configurator/serveruseradmin/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/configurator/serveruseradmin/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/configurator/serveruseradmin/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/configurator/serveruseradmin/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,74 @@
+package net.luminis.liq.configurator.serveruseradmin;
+
+import java.util.Dictionary;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * This bundle configures a single server user, which is to be used until we
+ * have a full-fledged user administration system.
+ */
+public class Activator extends DependencyActivatorBase {
+
+ private final static String TEST_USER = "serverUser";
+ private final static String TEST_PASSWORD = "serverPassword";
+
+ private volatile UserAdmin m_userAdmin; /* Injected by dependency manager */
+ private volatile LogService m_log; /* Injected by dependency manager */
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setImplementation(this)
+ .add(createServiceDependency().setService(UserAdmin.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+
+ public synchronized void start() {
+ // create users
+ createUser(TEST_USER, TEST_PASSWORD);
+ }
+
+ @SuppressWarnings("unchecked")
+ private User createUser(String username, String password) {
+ User user = (User) m_userAdmin.createRole(username, Role.USER);
+ if (user != null) {
+ Dictionary properties = user.getProperties();
+ if (properties != null) {
+ properties.put("username", username);
+ }
+ else {
+ m_log.log(LogService.LOG_ERROR, "Could not get properties for " + username);
+ }
+
+ Dictionary credentials = user.getCredentials();
+ if (credentials != null) {
+ credentials.put("password", password);
+ }
+ else {
+ m_log.log(LogService.LOG_ERROR, "Could not get credentials for " + username);
+ }
+ }
+ else {
+ try {
+ user = (User) m_userAdmin.getRole(username);
+ m_log.log(LogService.LOG_WARNING, "User " + username + " already existed.");
+ }
+ catch (ClassCastException e) {
+ m_log.log(LogService.LOG_WARNING, "Role " + username + " already existed (it's no user).");
+ }
+ }
+ return user;
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/configurator/useradmin/task/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/configurator/useradmin/task/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/configurator/useradmin/task/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/configurator/useradmin/task/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,36 @@
+package net.luminis.liq.configurator.useradmin.task;
+
+import java.util.Properties;
+
+import net.luminis.liq.resourceprocessor.useradmin.UserAdminConfigurator;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * Activator for the UserAdmin updater task.
+ */
+public class Activator extends DependencyActivatorBase {
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ Properties props = new Properties();
+ props.put("taskName", UpdateUserAdminTask.PID);
+ props.put("description", "Synchronizes the UserAdmin with the server.");
+ manager.add(createService()
+ .setInterface(Runnable.class.getName(), props)
+ .setImplementation(UpdateUserAdminTask.class)
+ .add(createServiceDependency().setService(UserAdminConfigurator.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false))
+ .add(createConfigurationDependency().setPid(UpdateUserAdminTask.PID))
+ );
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nothing to do, the runnable will be pulled automatically.
+ }
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/configurator/useradmin/task/UpdateUserAdminTask.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/configurator/useradmin/task/UpdateUserAdminTask.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/configurator/useradmin/task/UpdateUserAdminTask.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/configurator/useradmin/task/UpdateUserAdminTask.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,159 @@
+package net.luminis.liq.configurator.useradmin.task;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Properties;
+
+import net.luminis.liq.repository.ext.CachedRepository;
+import net.luminis.liq.repository.impl.CachedRepositoryImpl;
+import net.luminis.liq.resourceprocessor.useradmin.UserAdminConfigurator;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * UpdateUserAdminTask processes the contents of a repository containing
+ * an XML description of the users that should be present in this system's
+ * user admin.<br>
+ * <br>
+ * From the first run on, this task will keep a local copy of the user repository,
+ * so login can happen when the server is offline.
+ */
+public class UpdateUserAdminTask implements Runnable, ManagedService {
+ /**
+ * The UpdateUserAdminTask is also used as its taskName for the scheduler.
+ */
+ public static final String PID = UpdateUserAdminTask.class.getName();
+
+ public static final String KEY_REPOSITORY_LOCATION = "repositoryLocation";
+ public static final String KEY_REPOSITORY_CUSTOMER = "repositoryCustomer";
+ public static final String KEY_REPOSITORY_NAME = "repositoryName";
+
+ private static final String FILE_ROOT = "userrepositories";
+ private static final String VERSION = "version";
+
+ private volatile UserAdminConfigurator m_configurator; /* Will be injected by dependency manager */
+ private volatile LogService m_log; /* Will be injected by dependency manager */
+ private volatile BundleContext m_context; /* Will be injected by dependency manager */
+
+ private CachedRepository m_repo;
+ private File m_properties;
+
+ public void run() {
+ try {
+ if (!m_repo.isCurrent()) {
+ m_configurator.setUsers(m_repo.checkout(true));
+ m_log.log(LogService.LOG_DEBUG, "UpdateUserAdminTask updates to a new version: " + m_repo.getMostRecentVersion());
+ saveVersion(m_properties, m_repo.getMostRecentVersion());
+ }
+ }
+ catch (IOException e) {
+ // If anything went wrong, this means the remote repository is not available;
+ // this also means the UserAdmin is left undisturbed.
+ }
+ }
+
+ public void start() {
+ try {
+ // Try to read the server data
+ m_configurator.setUsers(m_repo.checkout(true));
+ }
+ catch (IOException e) {
+ try {
+ m_log.log(LogService.LOG_DEBUG, "UpdateUserAdminTask failed to load remote data; falling back to local data.");
+ // If reading remote fails, try to set whatever we have locally
+ m_configurator.setUsers(m_repo.getLocal(true));
+ }
+ catch (IOException e2) {
+ // No problem, now we just have an empty user admin...
+ m_log.log(LogService.LOG_DEBUG, "UpdateUserAdminTask failed to load local data.");
+ }
+ }
+ }
+
+ /**
+ * Loads the most recent version from the given properties file.
+ */
+ private long loadVersion(File propertiesfile) {
+ long result = CachedRepositoryImpl.UNCOMMITTED_VERSION;
+ try {
+ Properties props = new Properties();
+ props.loadFromXML(new FileInputStream(propertiesfile));
+ result = Long.parseLong((String) props.get(VERSION));
+ }
+ catch (IOException ioe) {
+ // We have no data; no problem.
+ }
+ return result;
+ }
+
+ /**
+ * Saves the most recent version to the given properties file.
+ */
+ private void saveVersion(File properties, Long version) {
+ Properties props = new Properties();
+ props.put(VERSION, version.toString());
+ try {
+ FileOutputStream fileOutputStream = new FileOutputStream(properties);
+ props.storeToXML(fileOutputStream, null);
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_ERROR, "UpdateUserAdminTask failed to save local version number.");
+ }
+ }
+
+ public void updated(Dictionary dict) throws ConfigurationException {
+ if (dict != null) {
+ String locationString = (String) dict.get(KEY_REPOSITORY_LOCATION);
+ if (locationString == null) {
+ throw new ConfigurationException(KEY_REPOSITORY_LOCATION, "Property missing.");
+ }
+ URL location;
+ try {
+ location = new URL(locationString);
+ }
+ catch (MalformedURLException e) {
+ throw new ConfigurationException(KEY_REPOSITORY_LOCATION, "Location " + locationString + " is not a valid URL.");
+ }
+ String customer = (String) dict.get(KEY_REPOSITORY_CUSTOMER);
+ if (customer == null) {
+ throw new ConfigurationException(KEY_REPOSITORY_CUSTOMER, "Property missing.");
+ }
+ String name = (String) dict.get(KEY_REPOSITORY_NAME);
+ if (name == null) {
+ throw new ConfigurationException(KEY_REPOSITORY_NAME, "Property missing.");
+ }
+
+ String fileRoot = FILE_ROOT + File.separator + location.getAuthority().replace(':', '-') + location.getPath().replace('/', '\\') + File.separator + customer + File.separator + name + File.separator;
+ File local = getFile(fileRoot + "local");
+ File backup = getFile(fileRoot + "backup");
+ m_properties = getFile(fileRoot + "properties");
+
+ m_repo = new CachedRepositoryImpl(null, location, customer, name, local, backup, loadVersion(m_properties));
+ }
+ }
+
+ private File getFile(String name) {
+ File result = m_context.getDataFile(name);
+ if (!result.exists()) {
+ result.getParentFile().mkdirs();
+ try {
+ if (!result.createNewFile()) {
+ m_log.log(LogService.LOG_ERROR, "Error creating new file " + name);
+ }
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_ERROR, "Error creating new file " + name, e);
+ }
+ }
+ return result;
+ }
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/ArtifactData.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/ArtifactData.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/ArtifactData.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/ArtifactData.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,63 @@
+package net.luminis.liq.deployment.provider;
+
+import java.net.URL;
+import java.util.jar.Attributes;
+
+/**
+ * The ArtifactData as returned by the <code>DeploymentProvider</code> class in this package.
+ * It contains several pieces of data which describe the artifact and the place where it can be found.
+ */
+public interface ArtifactData {
+
+ /**
+ * Indicate if the bundle has changed. This is used when comparing artifacts in 2 versions. (see DeploymentProvider)
+ * If you requested one version it always returns true.
+ *
+ * @return if this artifact has changed.
+ */
+ public boolean hasChanged();
+
+ /**
+ * @return <code>true</code> if this artifact is a bundle; <code>false</code> otherwise.
+ */
+ public boolean isBundle();
+
+ /**
+ * @return <code>true</code> if this artifact is a customizer that contains a resource processor; <code>false</code> otherwise.
+ */
+ public boolean isCustomizer();
+
+ /**
+ * @return the filename of the artifact
+ */
+ public String getFilename();
+
+ /**
+ * @return the symbolic name, if this artifact is a bundle.
+ */
+ public String getSymbolicName();
+
+ /**
+ * @return the version, if this artifact is a bundle. If it is an artifact, this function
+ * will always return "0.0.0".
+ */
+ public String getVersion();
+
+ /**
+ * @return the url to the artifact data.
+ */
+ public URL getUrl();
+
+ /**
+ * @return the processor Pid to be used for this resource, if any.
+ */
+ public String getProcessorPid();
+
+ /**
+ * @return a set of attributes that describes this artifact in a manifest.
+ * @param fixPackage Indicating whether this set of headers is intended to be part
+ * of a fixpackage.
+ */
+ public Attributes getManifestAttributes(boolean fixPackage);
+
+}
\ No newline at end of file
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/DeploymentProvider.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/DeploymentProvider.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/DeploymentProvider.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/DeploymentProvider.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,52 @@
+package net.luminis.liq.deployment.provider;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * An interface that provides the meta information for the bundles
+ * in a certain version number.
+ */
+public interface DeploymentProvider {
+
+ /**
+ * Get the collection of bundleData for a specific version. This data can be used to generate a deployment package.
+ * The ArtifactData.hasChanged method will return true for all bundles in this collection
+ *
+ * @return a collection of bundledata. If there are no bundles in this version, return an empty list
+ * @throws IllegalArgumentException if the gateway or version do not exist
+ * @throws IOException If an IOException occurs.
+ */
+ public List<ArtifactData> getBundleData(String gatewayId, String version) throws IllegalArgumentException, IOException;
+
+ /**
+ * This data can be used to generate a fix package. It gives the differences between the versionFrom and versionTo.
+ *
+ * Changes between versions are indicated by ArtifactData.hasChanged:
+ * <ol>
+ * <li> If a bundle was present in versionFrom and not in VersionTo, it will not be in the collection</li>
+ * <li> If a bundle existed in versionFrom and exists unchanged in VersionTo, hasChanged will return false</li>
+ * <li> If a bundle existed in versionFrom and exists changed (i.e. other version) in versionTo, hasChanged will return true</li>
+ * <li> If a bundle did not exist in versionFrom and exists in VersionTo, hasChanged will return true</li>
+ * </ol>
+ *
+ * @return a list of bundles.
+ * @throws IllegalArgumentException if the gateway, the versionFrom or versionTo do no exist
+ * @throws IOException If an IOException occurs.
+ */
+
+ public List<ArtifactData> getBundleData(String gatewayId, String versionFrom, String versionTo) throws IllegalArgumentException, IOException;
+
+ /**
+ * Returns a list of versions for a specific gateway. The list is sorted in
+ * ascending order, so the latest version is the last one in the list.
+ *
+ * @param gatewayId The id of the gateway for which all available deployment package
+ * versions are being retrieved.
+ * @return All available deployment package versions for a specific gateway. If none available,
+ * return an empty List.
+ * If the gateway doesn't exist, an IllegalArgumentException is thrown
+ * @throws IOException If an IOException occurs.
+ */
+ public List<String> getVersions(String gatewayId) throws IllegalArgumentException, IOException;
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/filebased/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/filebased/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/filebased/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/filebased/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,30 @@
+package net.luminis.liq.deployment.provider.filebased;
+
+import net.luminis.liq.deployment.provider.DeploymentProvider;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+ public static final String PID = "net.luminis.liq.deployment.provider.filebased";
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setInterface(DeploymentProvider.class.getName(), null)
+ .setImplementation(FileBasedProvider.class)
+ .add(createConfigurationDependency()
+ .setPid(PID)
+ )
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext arg0, DependencyManager arg1) throws Exception {
+ // TODO Auto-generated method stub
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/filebased/FileBasedProvider.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/filebased/FileBasedProvider.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/filebased/FileBasedProvider.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/filebased/FileBasedProvider.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,289 @@
+package net.luminis.liq.deployment.provider.filebased;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.jar.Attributes;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import net.luminis.liq.deployment.provider.ArtifactData;
+import net.luminis.liq.deployment.provider.DeploymentProvider;
+import net.luminis.liq.deployment.provider.impl.ArtifactDataImpl;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * This class reads data from the filesystem. It contains deployment data in the following format: <storage dir>/<gateway-name>/<bundle-version>/<jars>
+ * example : storage-directory/ storage-directory/gateway-a storage-directory/gateway-a/1.0.0
+ * storage-directory/gateway-a/1.0.0/bundle1.jar storage-directory/gateway-a/1.0.0/bundle2.jar storage-directory/gateway-a/1.1.0
+ * storage-directory/gateway-a/1.1.0/bundle2.jar storage-directory/gateway-a/1.1.0/bundle3.jar The versions are in the
+ * org.osgi.framework.Version format.
+ */
+public class FileBasedProvider implements DeploymentProvider, ManagedService {
+
+ private static final String DIRECTORY_NAME = "BaseDirectoryName";
+
+ private final int OSGI_R4_MANIFEST_VERSION = 2;
+
+ private volatile File m_baseDirectory;
+
+ private volatile LogService m_log;
+
+ private final Semaphore m_disk = new Semaphore(1, true);
+
+ /**
+ * Get the bundle data from the bundles in the <data dir>/<gateway>/<version> directory It reads the manifest from all the
+ * .jar files in that directory. If the manifest cannot be found, This method can only parse OSGi R4 bundles
+ */
+ public List<ArtifactData> getBundleData(String gatewayId, String version) throws IllegalArgumentException {
+
+ List<String> versions = getVersions(gatewayId);
+ if (!versions.contains(version)) {
+ throw new IllegalArgumentException("Unknown version " + version + " requested");
+ }
+ File gatewayDirectory = new File(m_baseDirectory, gatewayId);
+ File versionDirectory = new File(gatewayDirectory, version);
+ List<ArtifactData> bundleData = new ArrayList<ArtifactData>();
+
+ JarInputStream jarInputStream = null;
+
+ File[] jarFiles = versionDirectory.listFiles();
+ for (int i = 0; i < jarFiles.length; i++) {
+ Manifest bundleManifest = null;
+ File jarFile = jarFiles[i];
+ try {
+ jarInputStream = new JarInputStream(new FileInputStream(jarFile));
+ bundleManifest = jarInputStream.getManifest();
+ }
+ catch (IOException ioe) {
+ m_log.log(LogService.LOG_WARNING, "Error making inputstream", ioe);
+ continue;
+ }
+ finally {
+ if (jarInputStream != null) {
+ try {
+ jarInputStream.close();
+ }
+ catch (Exception ioe) {
+ m_log.log(LogService.LOG_ERROR, "Error closing the file input stream", ioe);
+ }
+ }
+ }
+ Attributes mainAttributes = bundleManifest.getMainAttributes();
+ String manifestVersion = mainAttributes.getValue(Constants.BUNDLE_MANIFESTVERSION);
+ String symbolicName = mainAttributes.getValue(Constants.BUNDLE_SYMBOLICNAME);
+ String bundleVersion = mainAttributes.getValue(Constants.BUNDLE_VERSION);
+
+ if ((manifestVersion != null) && (symbolicName != null) && (bundleVersion != null)) {
+ // ok, now at least we have the required attributes
+ // now check if they have the expected values
+ if (OSGI_R4_MANIFEST_VERSION != new Integer(manifestVersion).intValue()) {
+ m_log.log(LogService.LOG_WARNING, "Invalid bundle:" + jarFile.getAbsolutePath() + " has the wrong manifest version.");
+ }
+ else {
+ // it is the right manifest version
+ if (symbolicName.trim().equals("")) {
+ m_log.log(LogService.LOG_WARNING, "Invalid bundle:" + jarFile.getAbsolutePath() + " the symbolic name is empty.");
+ }
+ else {
+ // it also has the right symbolic name
+ try {
+ new Version(bundleVersion);
+ // Do a file.toURI().toURL() to preserve special path characters
+ // see http://www.javalobby.org/java/forums/t19698.html
+ URL bundleUrl = new URL(null, jarFile.toURI().toURL().toString(), new URLStreamHandler() {
+
+ @Override
+ protected URLConnection openConnection(final URL u) throws IOException {
+ return new URLConnection(u) {
+
+ @Override
+ public void connect() throws IOException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ final InputStream parent;
+ try {
+ parent = new URL(u.toURI().toURL().toString()).openStream();
+ }
+ catch (URISyntaxException ex) {
+ throw new IOException(ex.getMessage());
+ }
+ return new InputStream() {
+ @Override
+ public int read() throws IOException {
+ return parent.read();
+ }
+
+ @Override
+ public int read(byte[] buffer) throws IOException {
+ return read(buffer, 0, buffer.length);
+ }
+
+ @Override
+ public int read(byte[] buffer, int off, int length) throws IOException {
+ m_disk.acquireUninterruptibly();
+ try {
+ return parent.read(buffer, off, length);
+ }
+ finally {
+ m_disk.release();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ parent.close();
+ }
+ };
+ }
+ };
+ }
+
+ });
+ bundleData.add(new ArtifactDataImpl(jarFile.getName(), symbolicName, bundleVersion, bundleUrl, true));
+ }
+ catch (IllegalArgumentException iae) {
+ m_log.log(LogService.LOG_WARNING, "Invalid bundle:" + jarFile.getAbsolutePath() + " has an illegal version", iae);
+ }
+ catch (MalformedURLException mue) {
+ m_log.log(LogService.LOG_WARNING, "Invalid bundle:" + jarFile.getAbsolutePath() + " unable to convert path to URL", mue);
+ }
+ }
+ }
+ }
+ else {
+ m_log.log(LogService.LOG_WARNING, "Invalid bundle:" + jarFile.getAbsolutePath() + " is missing required attributes");
+ }
+ }
+
+ return bundleData;
+ }
+
+ public List<ArtifactData> getBundleData(String gatewayId, String versionFrom, String versionTo) throws IllegalArgumentException {
+ List<ArtifactData> dataVersionFrom = getBundleData(gatewayId, versionFrom);
+ List<ArtifactData> dataVersionTo = getBundleData(gatewayId, versionTo);
+
+ Iterator<ArtifactData> it = dataVersionTo.iterator();
+ while (it.hasNext()) {
+ ArtifactDataImpl bundleDataVersionTo = (ArtifactDataImpl) it.next();
+ // see if there was previously a version of this bundle.
+ ArtifactData bundleDataVersionFrom = getBundleData(bundleDataVersionTo.getSymbolicName(), dataVersionFrom);
+ bundleDataVersionTo.setChanged(!bundleDataVersionTo.equals(bundleDataVersionFrom));
+ }
+ return dataVersionTo;
+ }
+
+ /**
+ * Check for the existence of bundledata in the collection for a bundle with the given symbolic name
+ *
+ * @param symbolicName
+ * @return
+ */
+ private ArtifactData getBundleData(String symbolicName, Collection<ArtifactData> data) {
+ Iterator<ArtifactData> it = data.iterator();
+ while (it.hasNext()) {
+ ArtifactData bundle = it.next();
+ if (bundle.getSymbolicName().equals(symbolicName)) {
+ return bundle;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Look in the baseDirectory for the specified gateway. If it exists, get the list of directories in there and check if they
+ * conform to the <code>org.osgi.framework.Version</code> format. If it does, it will be in the list of versions that are
+ * returned. If there are no valid versions, return an empty list. If the gateway cannot be found, an
+ * IllegalArgumentException is thrown. The list will be sorted on version.
+ */
+ @SuppressWarnings("unchecked")
+ public List<String> getVersions(String gatewayId) throws IllegalArgumentException {
+ List<Version> versionList = new ArrayList<Version>();
+ File gatewayDirectory = new File(m_baseDirectory.getAbsolutePath(), gatewayId);
+ if (gatewayDirectory.isDirectory()) {
+ // ok, it is a directory. Now treat all the subdirectories as seperate versions
+ File[] files = gatewayDirectory.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ File possibleVersionDirectory = files[i];
+ if (possibleVersionDirectory.isDirectory()) {
+ // ok, it is a directory. Now see if it is a version
+ try {
+ Version version = Version.parseVersion(possibleVersionDirectory.getName());
+ // no exception, but is could still be an empty version
+ if (!version.equals(Version.emptyVersion)) {
+ versionList.add(version);
+ }
+ }
+ catch (IllegalArgumentException iae) {
+ // do nothing. This version will be ignored.
+ m_log.log(LogService.LOG_WARNING, "Directory " + possibleVersionDirectory.toString() + " does not contain a valid version number", iae);
+ }
+ }
+ }
+ if (files.length == 0) {
+ m_log.log(LogService.LOG_DEBUG, "No versions found for gateway " + gatewayId);
+ }
+ }
+ else {
+ throw new IllegalArgumentException("Gateway directory " + gatewayDirectory.toString() + " is requested but not found.");
+ }
+ // now sort the list of versions and convert all values to strings.
+ Collections.sort(versionList);
+ List<String> stringVersionList = new ArrayList<String>();
+ Iterator<Version> it = versionList.iterator();
+ while (it.hasNext()) {
+ String version = (it.next()).toString();
+ stringVersionList.add(version);
+ }
+ return stringVersionList;
+ }
+
+ /**
+ * Update the configuration for this bundle. It checks if the basedirectory exists and is a directory.
+ */
+ @SuppressWarnings("unchecked")
+ public void updated(Dictionary settings) throws ConfigurationException {
+ if (settings != null) {
+ String baseDirectoryName = getNotNull(settings, DIRECTORY_NAME, "The base directory cannot be null");
+ File baseDirectory = new File(baseDirectoryName);
+ if (!baseDirectory.exists() || !baseDirectory.isDirectory()) {
+ throw new ConfigurationException(DIRECTORY_NAME, "The directory called '" + baseDirectoryName + "' " + (baseDirectory.exists() ? "is no directory." : "doesn't exist."));
+ }
+ m_baseDirectory = baseDirectory;
+ }
+ }
+
+ /**
+ * Convenience method for getting settings from a configuration dictionary.
+ */
+ @SuppressWarnings("unchecked")
+ private String getNotNull(Dictionary settings, String id, String errorMessage) throws ConfigurationException {
+ String result = (String) settings.get(id);
+ if (result == null) {
+ throw new ConfigurationException(id, errorMessage);
+ }
+ return result;
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/impl/ArtifactDataImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/impl/ArtifactDataImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/impl/ArtifactDataImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/impl/ArtifactDataImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,189 @@
+package net.luminis.liq.deployment.provider.impl;
+
+import java.net.URL;
+import java.util.Map;
+import java.util.jar.Attributes;
+
+import net.luminis.liq.client.repository.object.DeploymentArtifact;
+import net.luminis.liq.deployment.provider.ArtifactData;
+
+import org.osgi.framework.Constants;
+
+/**
+ * Implementation of <code>ArtifactData</code>. It overrides equals to make comparisons between versions easier.
+ */
+public class ArtifactDataImpl implements ArtifactData {
+ public final static String HEADER_NAME = "Name";
+ public static final String CUSTOMIZER = "DeploymentPackage-Customizer";
+ public static final String PROCESSORPID = "Resource-Processor";
+
+ private final String m_filename;
+ private final String m_symbolicName;
+ private final String m_version;
+ private final Map<String, String> m_directives;
+
+ private final URL m_url;
+ private volatile boolean m_hasChanged;
+
+ /**
+ * Constructs an ArtifactDataImpl object.
+ * @param url The URL to the bundle. It will also be used to create the filename.
+ * The file-part of the url (after the last /) should It must only contain characters [A-Za-z0-9._-].
+ * @param directives A map of extra directives.
+ * @param symbolicName The symbolicname of the bundle.
+ * @param version The version of the bundle. If this is <code>null</code> or empty, it will be
+ * normalized to "0.0.0".
+ * @param hasChanged Indication of whether this bundle has changed relative to the previous deployment.
+ */
+ public ArtifactDataImpl(URL url, Map<String, String> directives, String symbolicName, String version, boolean hasChanged) {
+ this(url, symbolicName, version, null, directives, hasChanged);
+ }
+
+ /**
+ * Constructs an ArtifactDataImpl object.
+ * @param url The URL to the bundle. It will also be used to create the filename.
+ * The file-part of the url (after the last /) should It must only contain characters [A-Za-z0-9._-].
+ * @param directives A map of extra directives.
+ * @param hasChanged Indication of whether this bundle has changed relative to the previous deployment.
+ */
+ public ArtifactDataImpl(URL url, Map<String, String> directives, boolean hasChanged) {
+ this(url, null, null, null, directives, hasChanged);
+ }
+
+ /**
+ * Constructs an ArtifactDataImpl object.
+ * @param filename The filename of the bundle. If passed, it must only contain characters [A-Za-z0-9._-]; can be null.
+ * @param symbolicName The symbolicname of the bundle.
+ * @param version The version of the bundle. If this is <code>null</code> or empty, it will be
+ * normalized to "0.0.0".
+ * @param url The URL to the bundle. If filename is null, this will be used to create the filename; hence, the file-part of
+ * the url (after the last /) should adhere to the same rules as filename.
+ * @param hasChanged Indication of whether this bundle has changed relative to the previous deployment.
+ */
+ public ArtifactDataImpl(String filename, String symbolicName, String version, URL url, boolean hasChanged) {
+ this(url, symbolicName, version, filename, null, hasChanged);
+ }
+
+ private ArtifactDataImpl(URL url, String symbolicName, String version, String filename, Map<String, String> directives, boolean hasChanged) {
+ m_url = url;
+
+ if (filename != null) {
+ m_filename = filename;
+ }
+ else {
+ String urlString = m_url.toString();
+ m_filename = (urlString == null) ? null : urlString.substring(urlString.lastIndexOf('/') + 1);
+ }
+
+ for (byte b : m_filename.getBytes()) {
+ if (!(((b >= 'A') && (b <= 'Z')) || ((b >= 'a') && (b <= 'z')) || ((b >= '0') && (b <= '9')) || (b == '.') || (b == '-') || (b == '_'))) {
+ throw new IllegalArgumentException("Filename " + m_filename + " " + (filename == null ? "constructed from the url" : "") + " contains an illegal character '" + new String(new byte[] {b}) + "'");
+ }
+ }
+
+ m_symbolicName = symbolicName;
+ if ((version == null) || (version.trim().length() == 0)) {
+ m_version = "0.0.0";
+ }
+ else {
+ m_version = version;
+ }
+ m_directives = directives;
+ m_hasChanged = hasChanged;
+ }
+
+ public String getFilename() {
+ return m_filename;
+ }
+
+ public String getSymbolicName() {
+ return m_symbolicName;
+ }
+
+ public String getVersion() {
+ return m_version;
+ }
+
+ public String getProcessorPid() {
+ if (m_directives != null) {
+ return m_directives.get(DeploymentArtifact.DIRECTIVE_KEY_PROCESSORID);
+ }
+ return null;
+ }
+
+ public URL getUrl() {
+ return m_url;
+ }
+
+ public boolean hasChanged() {
+ return m_hasChanged;
+ }
+
+ /**
+ * @param hasChanged Indicate the bundle has changed
+ */
+ public void setChanged(boolean hasChanged) {
+ m_hasChanged = hasChanged;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof ArtifactDataImpl)) {
+ return false;
+ }
+ ArtifactDataImpl jarFile2 = (ArtifactDataImpl) other;
+
+ if (getSymbolicName() != null) {
+ // this is a bundle
+ return getSymbolicName().equals(jarFile2.getSymbolicName()) &&
+ getVersion().equals(jarFile2.getVersion());
+ }
+ else {
+ // this is another artifact.
+ return m_url.equals(jarFile2.getUrl());
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 11;
+ if (getSymbolicName() != null) {
+ result = result ^ getSymbolicName().hashCode();
+ }
+ result = result ^ getVersion().hashCode();
+ result = result ^ getUrl().hashCode();
+ return result;
+ }
+
+ public Attributes getManifestAttributes(boolean fixPackage) {
+ Attributes a = new Attributes();
+
+ if (!isBundle()) {
+ // this is a regular artifact
+ a.putValue(PROCESSORPID, getProcessorPid());
+ }
+ else {
+ a.putValue(Constants.BUNDLE_SYMBOLICNAME, getSymbolicName());
+ a.putValue(Constants.BUNDLE_VERSION, getVersion());
+ // this is a bundle
+ if (isCustomizer()) {
+ a.putValue(CUSTOMIZER, "true");
+ }
+ }
+
+ if (!hasChanged() && fixPackage) {
+ a.putValue("DeploymentPackage-Missing", "true");
+ }
+
+ return a;
+ }
+
+ public boolean isCustomizer() {
+ return (m_directives != null) && "true".equals(m_directives.get(DeploymentArtifact.DIRECTIVE_ISCUSTOMIZER));
+ }
+
+ public boolean isBundle() {
+ return getSymbolicName() != null;
+ }
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/repositorybased/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/repositorybased/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/repositorybased/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/repositorybased/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,30 @@
+package net.luminis.liq.deployment.provider.repositorybased;
+
+import net.luminis.liq.deployment.provider.DeploymentProvider;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+ public static final String PID = "net.luminis.liq.deployment.provider.repositorybased";
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setInterface(DeploymentProvider.class.getName(), null)
+ .setImplementation(RepositoryBasedProvider.class)
+ .add(createConfigurationDependency()
+ .setPid(PID)
+ )
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext arg0, DependencyManager arg1) throws Exception {
+ // TODO Auto-generated method stub
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/repositorybased/RepositoryBasedProvider.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/repositorybased/RepositoryBasedProvider.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/repositorybased/RepositoryBasedProvider.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/provider/repositorybased/RepositoryBasedProvider.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,635 @@
+package net.luminis.liq.deployment.provider.repositorybased;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.SoftReference;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathVariableResolver;
+
+import net.luminis.liq.client.repository.helper.bundle.BundleHelper;
+import net.luminis.liq.client.repository.object.DeploymentArtifact;
+import net.luminis.liq.deployment.provider.ArtifactData;
+import net.luminis.liq.deployment.provider.DeploymentProvider;
+import net.luminis.liq.deployment.provider.impl.ArtifactDataImpl;
+import net.luminis.liq.repository.RangeIterator;
+import net.luminis.liq.repository.Repository;
+import net.luminis.liq.repository.ext.BackupRepository;
+import net.luminis.liq.repository.ext.CachedRepository;
+import net.luminis.liq.repository.impl.CachedRepositoryImpl;
+import net.luminis.liq.repository.impl.FilebasedBackupRepository;
+import net.luminis.liq.repository.impl.RemoteRepository;
+
+import org.osgi.framework.Version;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+/**
+ * The RepositoryBasedProvider provides version information and bundle data by the DeploymentProvider interface. It uses a
+ * Repository to get its information from, which it parses using a SAX parser.
+ */
+public class RepositoryBasedProvider implements DeploymentProvider, ManagedService {
+ private static final String URL = "url";
+ private static final String NAME = "name";
+ private static final String CUSTOMER = "customer";
+ private volatile LogService m_log;
+
+ /** This variable is volatile since it can be changed by the Updated() method. */
+ private volatile CachedRepository m_cachedRepository;
+
+ /**
+ * This variable is volatile since it can be changed by the updated() method. Furthermore, it will be used to inject a
+ * custom repository in the integration test.
+ */
+ private volatile Repository m_directRepository;
+
+ public List<ArtifactData> getBundleData(String gatewayId, String version) throws IllegalArgumentException, IOException {
+ return getBundleData(gatewayId, null, version);
+ }
+
+ public List<ArtifactData> getBundleData(String gatewayId, String versionFrom, String versionTo) throws IllegalArgumentException, IOException {
+ try {
+ if (versionFrom != null) {
+ Version.parseVersion(versionFrom);
+ }
+ Version.parseVersion(versionTo);
+ }
+ catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(nfe);
+ }
+
+ InputStream input = null;
+ List<ArtifactData> dataVersionTo = null;
+ List<ArtifactData> dataVersionFrom = null;
+
+ List<URLDirectivePair>[] pairs = null;
+ try {
+ input = getRepositoryStream();
+ if (versionFrom == null) {
+ pairs = getURLDirectivePairs(input, gatewayId, new String[] { versionTo });
+ }
+ else {
+ pairs = getURLDirectivePairs(input, gatewayId, new String[] { versionFrom, versionTo });
+ }
+ }
+ catch (IOException ioe) {
+ m_log.log(LogService.LOG_WARNING, "Problem parsing source version.", ioe);
+ throw ioe;
+ }
+ finally {
+ if (input != null) {
+ try {
+ input.close();
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_DEBUG, "Error closing stream", e);
+ }
+ }
+ }
+
+ if ((pairs != null) && (pairs.length > 1)) {
+ dataVersionFrom = getBundleDataByDocument(pairs[0]);
+ dataVersionTo = getBundleDataByDocument(pairs[1]);
+ Iterator<ArtifactData> it = dataVersionTo.iterator();
+ while (it.hasNext()) {
+ ArtifactDataImpl bundleDataVersionTo = (ArtifactDataImpl) it.next();
+ // see if there was previously a version of this bundle, and update the 'changed' property accordingly.
+ if (bundleDataVersionTo.isBundle()) {
+ ArtifactData bundleDataVersionFrom = getBundleData(bundleDataVersionTo.getSymbolicName(), dataVersionFrom);
+ bundleDataVersionTo.setChanged(!bundleDataVersionTo.equals(bundleDataVersionFrom));
+ }
+ else {
+ ArtifactData bundleDataVersionFrom = getBundleData(bundleDataVersionTo.getUrl(), dataVersionFrom);
+ bundleDataVersionTo.setChanged(bundleDataVersionFrom == null);
+ }
+ }
+ }
+ else {
+ dataVersionTo = getBundleDataByDocument(pairs[0]);
+ }
+
+ return dataVersionTo != null ? dataVersionTo : new ArrayList<ArtifactData>();
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<String> getVersions(String gatewayId) throws IllegalArgumentException, IOException {
+ List<String> stringVersionList = new ArrayList<String>();
+ InputStream input = null;
+
+ try {
+ input = getRepositoryStream();
+ List<Version> versionList = getAvailableVersions(input, gatewayId);
+ if (versionList.isEmpty()) {
+ m_log.log(LogService.LOG_DEBUG, "No versions found for gateway " + gatewayId);
+ }
+ else {
+ // now sort the list of versions and convert all values to strings.
+ Collections.sort(versionList);
+ Iterator<Version> it = versionList.iterator();
+ while (it.hasNext()) {
+ String version = (it.next()).toString();
+ stringVersionList.add(version);
+ }
+ }
+ }
+ catch (IllegalArgumentException iae) {
+ // just move on.
+ }
+ catch (IOException ioe) {
+ m_log.log(LogService.LOG_DEBUG, "Problem parsing DeploymentRepository", ioe);
+ throw ioe;
+ }
+ finally {
+ if (input != null) {
+ try {
+ input.close();
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_DEBUG, "Error closing stream", e);
+ }
+ }
+ }
+
+ return stringVersionList;
+ }
+
+ /**
+ * Helper method to get the bundledata given an inputstream to a repository xml file
+ *
+ * @param input An input stream to the XML data to be parsed.
+ * @return A list of ArtifactData object representing this version.
+ */
+ private List<ArtifactData> getBundleDataByDocument(List<URLDirectivePair> urlDirectivePairs) throws IllegalArgumentException {
+ List<ArtifactData> result = new ArrayList<ArtifactData>();
+
+ // get the bundledata for each URL
+ for (URLDirectivePair pair : urlDirectivePairs) {
+ Map<String, String> directives = pair.getDirective();
+
+ if (directives.get(DeploymentArtifact.DIRECTIVE_KEY_PROCESSORID) == null) {
+ // this is a bundle.
+ String symbolicName = directives.remove(BundleHelper.KEY_SYMBOLICNAME);
+ String bundleVersion = directives.remove(BundleHelper.KEY_VERSION);
+ if (symbolicName != null) {
+ // it is the right symbolic name
+ if (symbolicName.trim().equals("")) {
+ m_log.log(LogService.LOG_WARNING, "Invalid bundle:" + pair.toString() + " the symbolic name is empty.");
+ }
+ else {
+ result.add(new ArtifactDataImpl(pair.getUrl(), directives, symbolicName, bundleVersion, true));
+ }
+ }
+ }
+ else {
+ // it is an artifact.
+ result.add(new ArtifactDataImpl(pair.getUrl(), directives, true));
+ }
+
+ }
+ return result;
+ }
+
+ /**
+ * Helper method check for the existence of artifact data in the collection for a bundle with the given url.
+ *
+ * @param url The url to be found.
+ * @return The <code>ArtifactData</code> object that has this <code>url</code>, or <code>null</code> if none can be
+ * found.
+ */
+ private ArtifactData getBundleData(URL url, Collection<ArtifactData> data) {
+ ArtifactData bundle = null;
+ Iterator<ArtifactData> it = data.iterator();
+ while (it.hasNext()) {
+ bundle = it.next();
+ if (bundle.getUrl().equals(url)) {
+ return bundle;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Helper method check for the existence of artifact data in the collection for a bundle with the given symbolic name.
+ *
+ * @param symbolicName The symbolic name to be found.
+ * @return The <code>ArtifactData</code> object that has this <code>symbolicName</code>, or <code>null</code> if none
+ * can be found.
+ */
+ private ArtifactData getBundleData(String symbolicName, Collection<ArtifactData> data) {
+ ArtifactData bundle = null;
+ Iterator<ArtifactData> it = data.iterator();
+ while (it.hasNext()) {
+ bundle = it.next();
+ if ((bundle.getSymbolicName() != null) && bundle.getSymbolicName().equals(symbolicName)) {
+ return bundle;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the available deployment versions for a gateway
+ *
+ * @param input A dom document representation of the repository
+ * @param gatewayId The gatwayId
+ * @return A list of available versions
+ */
+ private List<Version> getAvailableVersions(InputStream input, String gatewayId) throws IllegalArgumentException {
+ //result list
+ List<Version> versionList = new ArrayList<Version>();
+ XPathContext context = XPathContext.getInstance();
+
+ try {
+ NodeList versions = context.getVersions(gatewayId, input);
+ if (versions != null) {
+ for (int j = 0; j < versions.getLength(); j++) {
+ Node n = versions.item(j);
+ String versionValue = n.getTextContent();
+ try {
+ Version version = Version.parseVersion(versionValue);
+ // no exception, but is could still be an empty version
+ if (!version.equals(Version.emptyVersion)) {
+ versionList.add(version);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ // Ignore this version
+ m_log.log(LogService.LOG_WARNING, "Deploymentversion ignored: ", nfe);
+ }
+ }
+ }
+
+ return versionList;
+ }
+ catch (XPathExpressionException xee) {
+ throw new IllegalArgumentException(xee);
+ }
+ finally {
+ context.destroy();
+ }
+ }
+
+ /*
+ * This class is used to cache compiled xpath expression for the queries we need on a per thread basis. In order
+ * to do this a thread local is used to cache an instance of this class per calling thread. The reference to this
+ * instance is wrapped in a soft reference to make it possible to GC the instance in case memory is low.
+ * <p>
+ * Example Usage:
+ * <pre>
+ * XPathContext context = XPathContext.getInstance();
+ *
+ * try
+ * {
+ * // to get all artifacts of a number of versions:
+ * context.init(gatewayId, versions, input);
+ *
+ * for (int i = 0; i < versions.length; i++)
+ * {
+ * Node version = context.getVersion(i);
+ * // Do work with version
+ * }
+ * // to get all versions of a number of a gateway:
+ * NodeList versions = context.getVersions(gatewayId, input);
+ * // Do wortk with versions
+ * }
+ * finally
+ * {
+ * context.destory();
+ * }
+ * </pre>
+ */
+ private static final class XPathContext implements XPathVariableResolver {
+ private static final ThreadLocal<SoftReference<XPathContext>> m_cache = new ThreadLocal<SoftReference<XPathContext>>();
+
+ private final XPath m_xPath = XPathFactory.newInstance().newXPath();
+
+ private final XPathExpression m_attributesExpression;
+
+ private final XPathExpression m_versionsExpression;
+
+ private final Map<Integer, XPathExpression> m_expressions = new HashMap<Integer, XPathExpression>();
+
+ private String m_gatewayId;
+
+ private String[] m_versions;
+
+ private Node m_node;
+
+ private String m_version;
+
+ private XPathContext() {
+ m_xPath.setXPathVariableResolver(this);
+ try {
+ m_attributesExpression = m_xPath.compile("//deploymentversions/deploymentversion/attributes/child::gatewayID[text()=$id]/../child::version[text()=$version]/../../artifacts");
+ m_versionsExpression = m_xPath.compile("//deploymentversions/deploymentversion/attributes/child::gatewayID[text()=$id]/parent::attributes/version/text()");
+ }
+ catch (XPathExpressionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @return A thread local instance from the cache.
+ */
+ public static XPathContext getInstance() {
+ SoftReference<XPathContext> ref = m_cache.get();
+ XPathContext instance = null;
+ if (ref != null) {
+ instance = ref.get();
+ }
+
+ if (instance == null) {
+ ref = null;
+ instance = new XPathContext();
+ }
+
+ if (ref == null) {
+ m_cache.set(new SoftReference<XPathContext>(instance));
+ }
+
+ return instance;
+ }
+
+ /**
+ * @param gatewayId the id of the gateway
+ * @param input the stream to read repository from
+ * @return the versions in the repo for the given gatewayId or null if none
+ * @throws XPathExpressionException in case something goes wrong
+ */
+ public NodeList getVersions(String gatewayId, InputStream input) throws XPathExpressionException {
+ m_gatewayId = gatewayId;
+ return (NodeList) m_versionsExpression.evaluate(new InputSource(input), XPathConstants.NODESET);
+ }
+
+ /**
+ * @param gatewayId the id of the gateway
+ * @param versions the versions to return
+ * @param input the stream to read repository from
+ * @return true if versions can be found, otherwise false
+ * @throws XPathExpressionException if something goes wrong
+ */
+ @SuppressWarnings("boxing")
+ public boolean init(String gatewayId, String[] versions, InputStream input) throws XPathExpressionException {
+ XPathExpression expression = m_expressions.get(versions.length);
+ if (expression == null) {
+ StringBuilder versionStatement = new StringBuilder("//deploymentversions/deploymentversion/attributes/child::gatewayID[text()=$id]/following::version[text()=$0");
+ for (int i = 1; i < versions.length; i++) {
+ versionStatement.append(" or ").append(".=$").append(i);
+ }
+ versionStatement.append("]/../../..");
+ expression = m_xPath.compile(versionStatement.toString());
+ m_expressions.put(versions.length, expression);
+ }
+ m_gatewayId = gatewayId;
+ m_versions = versions;
+
+ m_node = (Node) expression.evaluate(new InputSource(input), XPathConstants.NODE);
+ return (m_node != null);
+ }
+
+ /**
+ * @param i the index into the versions form init
+ * @return the version at index i
+ * @throws XPathExpressionException if something goes wrong
+ */
+ public Node getVersion(int i) throws XPathExpressionException {
+ m_version = m_versions[i];
+ return (Node) m_attributesExpression.evaluate(m_node, XPathConstants.NODE);
+ }
+
+ /**
+ * reset this thread local instance
+ */
+ public void destroy() {
+ m_node = null;
+ m_version = null;
+ m_gatewayId = null;
+ m_versions = null;
+ }
+
+ /**
+ * @param name id|version|<version-index>
+ * @return id->gatewayId | version->version | version-index -> versions[version-index]
+ */
+ public Object resolveVariable(QName name) {
+ String localPart = name.getLocalPart();
+ if ("id".equals(localPart)) {
+ return m_gatewayId;
+ }
+ else if ("version".equals(localPart)) {
+ return m_version;
+ }
+ return m_versions[Integer.parseInt(localPart)];
+ }
+ }
+
+ /**
+ * Helper method to retrieve urls and directives for a gateway-version combination.
+ *
+ * @param input An input stream from which an XML representation of a deployment repository can be read.
+ * @param gatewayId The gatewayId to be used
+ * @param versions An array of versions.
+ * @return An array of lists of URLDirectivePairs. For each version in <code>versions</code>, a separate list will be
+ * created; the index of a version in the <code>versions</code> array is equal to the index of its result in the
+ * result array.
+ * @throws IllegalArgumentException if the gatewayId or versions cannot be found in the input stream, or if
+ * <code>input</code> does not contain an XML stream.
+ */
+ @SuppressWarnings("unchecked")
+ private List<URLDirectivePair>[] getURLDirectivePairs(InputStream input, String gatewayId, String[] versions) throws IllegalArgumentException {
+
+ XPathContext context = XPathContext.getInstance();
+ List<URLDirectivePair>[] result = new List[versions.length]; //unfortunately, we cannot use a typed list array.
+
+ try {
+ if (!context.init(gatewayId, versions, input)) {
+ m_log.log(LogService.LOG_WARNING, "Versions not found for Gateway: " + gatewayId);
+ throw new IllegalArgumentException("Versions not found.");
+ }
+ for (int i = 0; i < versions.length; i++) {
+ result[i] = new ArrayList<URLDirectivePair>();
+
+ // find all artifacts for the version we're currently working on.
+ Node artifactNode = null;
+ try {
+ artifactNode = context.getVersion(i);
+ }
+ catch (XPathExpressionException e) {
+ m_log.log(LogService.LOG_WARNING, "Version " + versions[i] + " not found for Gateway: " + gatewayId);
+ continue;
+ }
+ NodeList artifacts = artifactNode.getChildNodes();
+ // Read the artifacts
+ for (int artifactNumber = 0; artifactNumber < artifacts.getLength(); artifactNumber++) {
+ Node artifact = artifacts.item(artifactNumber);
+
+ NodeList artifactElements = artifact.getChildNodes();
+
+ String url = null;
+ Map<String, String> directives = new HashMap<String, String>();
+
+ for (int elementNumber = 0; elementNumber < artifactElements.getLength(); elementNumber++) {
+ // find the attributes of this artifact we are interested in.
+ Node element = artifactElements.item(elementNumber);
+
+ if (element.getNodeName().equals("url")) {
+ url = element.getTextContent();
+ }
+ else if (element.getNodeName().equals("directives")) {
+ // we found the directives? put all of them into our map.
+ NodeList directivesElements = element.getChildNodes();
+ for (int nDirective = 0; nDirective < directivesElements.getLength(); nDirective++) {
+ Node directivesElement = directivesElements.item(nDirective);
+ if (!"#text".equals(directivesElement.getNodeName())) {
+ directives.put(directivesElement.getNodeName(), directivesElement.getTextContent());
+ }
+ }
+ }
+ }
+
+ if (url != null) {
+ try {
+ result[i].add(new URLDirectivePair(new URL(url), directives));
+ }
+ catch (MalformedURLException mue) {
+ m_log.log(LogService.LOG_WARNING, "The BundleUrl is malformed: ", mue);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+ catch (XPathExpressionException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ finally {
+ context.destroy();
+ }
+ }
+
+ /**
+ * Helper to get an input stream to the currently used deployment repository.
+ *
+ * @return An input stream to the repository document. Will return an empty stream if none can be found.
+ * @throws IOException if there is a problem communicating with the local or remote repository.
+ */
+ private InputStream getRepositoryStream() throws IOException {
+ // cache the repositories, since we do not want them to change while we're in this method.
+ CachedRepository cachedRepository = m_cachedRepository;
+ Repository repository = m_directRepository;
+ InputStream result;
+
+ if (cachedRepository != null) {
+ // we can use the cached repository
+ if (cachedRepository.isCurrent()) {
+ result = cachedRepository.getLocal(true);
+ }
+ else {
+ result = cachedRepository.checkout(true);
+ }
+ }
+ else {
+ RangeIterator ri = repository.getRange().iterator();
+ long resultVersion = 0;
+ while (ri.hasNext()) {
+ resultVersion = ri.next();
+ }
+ if (resultVersion != 0) {
+ result = repository.checkout(resultVersion);
+ }
+ else {
+ throw new IllegalArgumentException("There is no deployment information available.");
+ }
+ }
+
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void updated(Dictionary settings) throws ConfigurationException {
+ if (settings != null) {
+ String url = getNotNull(settings, URL, "DeploymentRepository URL not configured.");
+ String name = getNotNull(settings, NAME, "RepositoryName not configured.");
+ String customer = getNotNull(settings, CUSTOMER, "RepositoryCustomer not configured.");
+
+ //create the remote repository and set it.
+ try {
+ BackupRepository backup = null;
+ try {
+ backup = new FilebasedBackupRepository(File.createTempFile("currentrepository", null), File.createTempFile("backuprepository", null));
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_WARNING, "Unable to create temporary files for FilebasedBackupRepository");
+ }
+
+ // We always create the remote repository. If we can create a backup repository, we will wrap a CachedRepository
+ // around it.
+ m_directRepository = new RemoteRepository(new URL(url), customer, name);
+ if (backup == null) {
+ m_cachedRepository = null;
+ }
+ else {
+ m_cachedRepository = new CachedRepositoryImpl(null, m_directRepository, backup, CachedRepositoryImpl.UNCOMMITTED_VERSION);
+ }
+ }
+ catch (MalformedURLException mue) {
+ throw new ConfigurationException(URL, mue.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Convenience method for getting settings from a configuration dictionary.
+ */
+ @SuppressWarnings("unchecked")
+ private String getNotNull(Dictionary settings, String id, String errorMessage) throws ConfigurationException {
+ String result = (String) settings.get(id);
+ if (result == null) {
+ throw new ConfigurationException(id, errorMessage);
+ }
+ return result;
+ }
+
+ /**
+ * Helper class to store a pair of URL and directive, in which the directive may be empty.
+ */
+ private class URLDirectivePair {
+ final private URL m_url;
+
+ final private Map<String, String> m_directives;
+
+ URLDirectivePair(URL url, Map<String, String> directives) {
+ m_url = url;
+ m_directives = directives;
+ }
+
+ public URL getUrl() {
+ return m_url;
+ }
+
+ public Map<String, String> getDirective() {
+ return m_directives;
+ }
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/servlet/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/servlet/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/servlet/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/servlet/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,30 @@
+package net.luminis.liq.deployment.servlet;
+import javax.servlet.http.HttpServlet;
+
+import net.luminis.liq.deployment.provider.DeploymentProvider;
+import net.luminis.liq.deployment.streamgenerator.StreamGenerator;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+ public static final String PID = "net.luminis.liq.deployment.servlet";
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setInterface(HttpServlet.class.getName(), null)
+ .setImplementation(DeploymentServlet.class)
+ .add(createServiceDependency().setService(StreamGenerator.class).setRequired(true))
+ .add(createServiceDependency().setService(DeploymentProvider.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false))
+ .add(createConfigurationDependency().setPropagate(true).setPid(PID)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+}
\ No newline at end of file
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/servlet/DeploymentServlet.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/servlet/DeploymentServlet.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/servlet/DeploymentServlet.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/servlet/DeploymentServlet.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,152 @@
+package net.luminis.liq.deployment.servlet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.List;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.luminis.liq.deployment.provider.DeploymentProvider;
+import net.luminis.liq.deployment.streamgenerator.StreamGenerator;
+
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * The DeploymentServlet class provides in a list of versions available for a gateway and a stream
+ * of data containing the DeploymentPackage (or fix package) for a specific gateway and version.
+ */
+public class DeploymentServlet extends HttpServlet implements ManagedService {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String CURRENT = "current";
+ public static final String VERSIONS = "versions";
+ public static final String DP_MIMETYPE = "application/vnd.osgi.dp";
+ public static final String TEXT_MIMETYPE = "text/plain";
+
+ private volatile LogService m_log; /* injected by dependency manager */
+ private volatile StreamGenerator m_streamGenerator; /* injected by dependency manager */
+ private volatile DeploymentProvider m_provider; /* injected by dependency manager */
+
+ /**
+ * Responds to GET requests sent to this endpoint, the response depends on the requested path:
+ * <li>http://host/endpoint/gatewayid/versions/ returns a list of versions available for the specified gateway
+ * <li>http://host/endpoint/gatewayid/versions/x.y.z returns a deployment package stream for the specified gateway and version
+ *
+ * The status code of the response can be one of the following:
+ * <li><code>HttpServletResponse.SC_BAD_REQUEST</code> - If no gateway is specified or the request is malformed in a different way.
+ * <li><code>HttpServletResponse.SC_NOT_FOUND</code> - If the specified gateway or version does not exist.
+ * <li><code>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</code> - If there was a problem processing the request.
+ * <li><code>HttpServletResponse.SC_OK</code> - If all went fine
+ */
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) {
+ String path = request.getPathInfo();
+ if (path == null) {
+ sendError(response, HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid");
+ return;
+ }
+
+ String[] pathElements = path.split("/");
+ int numberOfElements = pathElements.length;
+
+ if ((numberOfElements < 3) || (numberOfElements > 4) || !VERSIONS.equals(pathElements[2])) {
+ sendError(response, HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid");
+ return;
+ }
+
+ String gatewayID = pathElements[1];
+ List<String> versions;
+ try {
+ versions = m_provider.getVersions(gatewayID);
+ }
+ catch (IllegalArgumentException iae) {
+ String description = "Unknown gateway (" + gatewayID + ")";
+ m_log.log(LogService.LOG_WARNING, description, iae);
+ sendError(response, HttpServletResponse.SC_NOT_FOUND, description);
+ return;
+ }
+ catch (IOException ioe) {
+ String description = "Error getting available versions.";
+ m_log.log(LogService.LOG_WARNING, description, ioe);
+ sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, description);
+ return;
+ }
+
+ ServletOutputStream output = null;
+ try {
+ output = response.getOutputStream();
+ if (numberOfElements == 3) {
+ response.setContentType(TEXT_MIMETYPE);
+ for (String version : versions) {
+ output.print(version);
+ output.print("\n");
+ }
+ }
+ else {
+ String version = pathElements[3];
+ if (!versions.contains(version)) {
+ sendError(response, HttpServletResponse.SC_NOT_FOUND, "Unknown version (" + version + ")");
+ return;
+ }
+ String current = request.getParameter(CURRENT);
+
+ InputStream inputStream;
+ if (current != null) {
+ inputStream = m_streamGenerator.getDeploymentPackage(gatewayID, current, version);
+ }
+ else {
+ inputStream = m_streamGenerator.getDeploymentPackage(gatewayID, version);
+ }
+
+ response.setContentType(DP_MIMETYPE);
+ byte[] buffer = new byte[1024 * 32];
+ for (int bytesRead = inputStream.read(buffer); bytesRead != -1; bytesRead = inputStream.read(buffer)) {
+ output.write(buffer, 0, bytesRead);
+ }
+ }
+ }
+ catch (IOException ex) {
+ String description = "Problem reading request or response data";
+ m_log.log(LogService.LOG_WARNING, description, ex);
+ sendError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, description);
+ }
+ finally {
+ try {
+ if (output != null) {
+ output.close();
+ }
+ }
+ catch (Exception ex) {
+ m_log.log(LogService.LOG_WARNING, "Exception trying to close stream after request: " + request.getRequestURL(), ex);
+ }
+ }
+ }
+
+ // send a response with the specified status code and description
+ private void sendError(HttpServletResponse response, int statusCode, String description) {
+ m_log.log(LogService.LOG_WARNING, "Deployment request failed: " + description);
+ try {
+ response.sendError(statusCode, description);
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_WARNING, "Unable to send error response with status code '" + statusCode + "'", e);
+ }
+ }
+
+ @Override
+ public String getServletInfo() {
+ return "LiQ Deployment Servlet Endpoint";
+ }
+
+ @SuppressWarnings("unchecked")
+ public void updated(Dictionary settings) throws ConfigurationException {
+ // Nothing needs to be done - handled by DependencyManager
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/StreamGenerator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/StreamGenerator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/StreamGenerator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/StreamGenerator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,28 @@
+package net.luminis.liq.deployment.streamgenerator;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface StreamGenerator {
+
+ /**
+ * Returns an input stream with the requested deployment package.
+ *
+ * @param id the ID of the package
+ * @param version the version of the package
+ * @return an input stream
+ * @throws IOException when the stream could not be generated
+ */
+ public InputStream getDeploymentPackage(String id, String version) throws IOException;
+
+ /**
+ * Returns an input stream with the requested deployment fix package.
+ *
+ * @param id the ID of the package.
+ * @param fromVersion the version of the target.
+ * @param toVersion the version the target should be in after applying the package.
+ * @return an input stream.
+ * @throws IOException when the stream could not be generated.
+ */
+ public InputStream getDeploymentPackage(String id, String fromVersion, String toVersion) throws IOException;
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/impl/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/impl/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/impl/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/impl/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,27 @@
+package net.luminis.liq.deployment.streamgenerator.impl;
+
+import net.luminis.liq.deployment.provider.DeploymentProvider;
+import net.luminis.liq.deployment.streamgenerator.StreamGenerator;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+
+public class Activator extends DependencyActivatorBase {
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setInterface(StreamGenerator.class.getName(), null)
+ .setImplementation(StreamGeneratorImpl.class)
+ .add(createServiceDependency()
+ .setService(DeploymentProvider.class)
+ .setRequired(true)
+ )
+ );
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ }
+}