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 [14/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/deployment/streamgenerator/impl/StreamGeneratorImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/impl/StreamGeneratorImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/impl/StreamGeneratorImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/deployment/streamgenerator/impl/StreamGeneratorImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,240 @@
+package net.luminis.liq.deployment.streamgenerator.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.SoftReference;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import net.luminis.liq.deployment.provider.ArtifactData;
+import net.luminis.liq.deployment.provider.DeploymentProvider;
+import net.luminis.liq.deployment.streamgenerator.StreamGenerator;
+
+/**
+ * Stream generator for deployment packages. Communicates with a data provider to get the meta data for the streams. Part of the
+ * meta
+ */
+public class StreamGeneratorImpl implements StreamGenerator {
+ private volatile DeploymentProvider m_provider;
+
+ /**
+ * 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 {
+ //return execute(new Worker(id, version));
+ List<ArtifactData> data = m_provider.getBundleData(id, version);
+ Manifest manifest = new Manifest();
+ Attributes main = manifest.getMainAttributes();
+
+ main.putValue("Manifest-Version", "1.0");
+ main.putValue("DeploymentPackage-SymbolicName", id);
+ main.putValue("DeploymentPackage-Version", version);
+
+ // Note: getEntries() returns a map. This means that the order of the entries
+ // in the manifest is _not_ defined; this should be fine, as far as the
+ // deployment admin spec goes.
+ for (ArtifactData bd : data) {
+ manifest.getEntries().put(bd.getFilename(), bd.getManifestAttributes(false));
+ }
+
+ return DeploymentPackageStream.createStreamForThread(manifest, data.iterator(), false);
+ }
+
+ /**
+ * 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 {
+ //return execute(new WorkerFixPackage(id, fromVersion, toVersion));
+ List<ArtifactData> data = m_provider.getBundleData(id, fromVersion, toVersion);
+ Manifest manifest = new Manifest();
+ Attributes main = manifest.getMainAttributes();
+
+ main.putValue("Manifest-Version", "1.0");
+ main.putValue("DeploymentPackage-SymbolicName", id);
+ main.putValue("DeploymentPackage-Version", toVersion);
+ main.putValue("DeploymentPackage-FixPack", "[" + fromVersion + "," + toVersion + ")");
+
+ for (ArtifactData bd : data) {
+ manifest.getEntries().put(bd.getFilename(), bd.getManifestAttributes(true));
+ }
+
+ return DeploymentPackageStream.createStreamForThread(manifest, data.iterator(), true);
+ }
+
+ private static final class DeploymentPackageStream extends InputStream {
+ private byte[] m_readBuffer;
+
+ private byte[] m_buffer;
+
+ private final OutputBuffer m_outputBuffer = new OutputBuffer(this);
+
+ private JarOutputStream m_output;
+
+ private Iterator<ArtifactData> m_iter;
+
+ private InputStream m_current = null;
+
+ private int m_pos = 0;
+
+ private int m_max = 0;
+
+ private boolean m_fixPack;
+
+ private DeploymentPackageStream() {
+ this(64 * 1024);
+ }
+
+ private DeploymentPackageStream(int bufferSize) {
+ m_buffer = new byte[bufferSize];
+ m_readBuffer = new byte[bufferSize];
+ }
+
+ private static final ThreadLocal<SoftReference<DeploymentPackageStream>> m_cache = new ThreadLocal<SoftReference<DeploymentPackageStream>>();
+
+ static DeploymentPackageStream createStreamForThread(Manifest man, Iterator<ArtifactData> iter, boolean fixpack) throws IOException {
+ SoftReference<DeploymentPackageStream> ref = m_cache.get();
+ DeploymentPackageStream dps = null;
+ if (ref != null) {
+ dps = ref.get();
+ }
+
+ if (dps == null) {
+ dps = new DeploymentPackageStream();
+ m_cache.set(new SoftReference<DeploymentPackageStream>(dps));
+ }
+
+ if (dps.isInUse()) {
+ dps = new DeploymentPackageStream();
+ }
+
+ dps.init(man, iter, fixpack);
+
+ return dps;
+ }
+
+ private boolean isInUse() {
+ return m_output == null;
+ }
+
+ private void init(Manifest man, Iterator<ArtifactData> iter, boolean fixPack) throws IOException {
+ m_max = 0;
+ m_pos = 0;
+ m_output = new JarOutputStream(m_outputBuffer, man);
+ m_output.flush();
+ m_iter = iter;
+ m_fixPack = fixPack;
+ next();
+ }
+
+ private void next() throws IOException {
+ ArtifactData current = (m_iter.hasNext()) ? m_iter.next() : null;
+
+ if (current == null) {
+ m_output.close();
+ }
+ else if (!m_fixPack || current.hasChanged()) {
+ m_current = current.getUrl().openStream();
+ m_output.putNextEntry(new ZipEntry(current.getFilename()));
+ }
+ else {
+ next();
+ }
+ }
+
+ @Override
+ public int read() throws IOException {
+ while (m_pos == m_max) {
+ if (m_current == null) {
+ if (m_output != null) {
+ m_output.close();
+ }
+ m_output = null;
+ m_iter = null;
+ return -1;
+ }
+ m_pos = 0;
+ m_max = 0;
+ int len = m_current.read(m_readBuffer);
+ if (len != -1) {
+ m_output.write(m_readBuffer, 0, len);
+ m_output.flush();
+ }
+ else {
+ try {
+ m_current.close();
+ }
+ catch (Exception ex) {
+ // Not much we can do
+ }
+ m_current = null;
+ m_output.closeEntry();
+ m_output.flush();
+ next();
+ }
+ }
+
+ return m_buffer[m_pos++] & 0xFF;
+ }
+
+ void write(int b) {
+ if (m_max == m_buffer.length) {
+ byte[] tmp = new byte[m_buffer.length + 8192];
+ System.arraycopy(m_buffer, 0, tmp, 0, m_buffer.length);
+ m_buffer = tmp;
+ }
+ m_buffer[m_max++] = (byte) b;
+ }
+
+ @Override
+ public void close() {
+ if (m_output != null) {
+ try {
+ m_output.close();
+ m_output = null;
+ }
+ catch (Exception ex) {
+ // Not much we can do
+ }
+ }
+ if (m_current != null) {
+ try {
+ m_current.close();
+ m_current = null;
+ }
+ catch (Exception ex) {
+ // Not much we can do
+ }
+ }
+ m_iter = null;
+ }
+
+ private static final class OutputBuffer extends OutputStream {
+ private final DeploymentPackageStream m_stream;
+
+ public OutputBuffer(DeploymentPackageStream stream) {
+ m_stream = stream;
+ }
+
+ @Override
+ public void write(int b) {
+ m_stream.write(b);
+ }
+ }
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/http/listener/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/http/listener/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/http/listener/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/http/listener/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,185 @@
+package net.luminis.liq.http.listener;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.Servlet;
+import javax.servlet.http.HttpServlet;
+
+import net.luminis.liq.http.listener.constants.HttpConstants;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.log.LogService;
+
+/**
+ * Service responsible for registrating HttpServlets at HttpServices.<p>
+ *
+ * When a HttpServlet is being added or removed, the callback methods in this class are being
+ * called via the DependencyManager. A Servlet is being added to all HttpServices currently
+ * available or removed from all available HttpServices.<p>
+ *
+ * In case a HttpService is being added or removed, other callback methods are being called
+ * via the DependencyManager. When a HttpService is added, all previously registered Servlets
+ * are being registered to this new HttpService. In case of removal, all Servlet endpoints are
+ * being removed from the HttpService that is going to be removed.<p>
+ */
+public class Activator extends DependencyActivatorBase {
+
+ private volatile LogService m_log; // injected
+ private final Set<ServiceReference> m_httpServices = new HashSet<ServiceReference>();
+ private final Map<ServiceReference, String> m_servlets = new HashMap<ServiceReference, String>();
+ private BundleContext m_context;
+
+ @Override
+ public synchronized void init(BundleContext context, DependencyManager manager) throws Exception {
+ m_context = context;
+ manager.add(createService()
+ .setImplementation(this)
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false))
+ .add(createServiceDependency()
+ .setService(HttpService.class)
+ .setAutoConfig(false)
+ .setCallbacks("addHttpService", "removeHttpService"))
+ .add(createServiceDependency()
+ .setService(HttpServlet.class)
+ .setAutoConfig(false)
+ .setCallbacks("addHttpServlet", "changeHttpServlet", "removeHttpServlet")));
+ }
+
+ /**
+ * Callback method used in case a HttpServlet is being added. This Servlet is being added
+ * to all available HttpServices under the endpoint configured via the Configurator.
+ *
+ * @param ref reference to the Servlet
+ */
+ public synchronized void addHttpServlet(ServiceReference ref) {
+ // register servlet to all HttpServices
+ String endpoint = (String)ref.getProperty(HttpConstants.ENDPOINT);
+ m_servlets.put(ref, endpoint);
+ Servlet servlet = (Servlet)m_context.getService(ref);
+ for (ServiceReference reference : m_httpServices) {
+ HttpService httpService = (HttpService) m_context.getService(reference);
+ try {
+ if ((httpService != null) && (endpoint != null) && (servlet != null)) {
+ httpService.registerServlet(endpoint, servlet, null, null);
+ }
+ else {
+ m_log.log(LogService.LOG_WARNING, "Unable to register servlet with endpoint '" + endpoint + "'");
+ }
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_WARNING, "Already registered under existing endpoint", e);
+ }
+ }
+ }
+
+ public synchronized void changeHttpServlet(ServiceReference ref) {
+ removeServlet(ref, m_servlets.get(ref));
+ addHttpServlet(ref);
+ }
+
+ /**
+ * Callback method used in case a HttpServlet is being removed. This Servlet is being removed
+ * from all available HttpServices using the endpoint configured via the Configurator.
+ *
+ * @param ref reference to the Servlet
+ */
+ public synchronized void removeHttpServlet(ServiceReference ref) {
+ // remove servlet from all HttpServices
+ String endpoint = (String)ref.getProperty(HttpConstants.ENDPOINT);
+ removeServlet(ref, endpoint);
+ }
+
+ private void removeServlet(ServiceReference ref, String endpoint) {
+ m_servlets.remove(ref);
+ for (ServiceReference reference : m_httpServices) {
+ HttpService httpService = (HttpService) m_context.getService(reference);
+ if ((httpService != null) && (endpoint != null)) {
+ try {
+ httpService.unregister(endpoint);
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_WARNING, "Servlet cannot be unregistered, maybe not registered under this endpoint", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Callback method used in case a HttpService is being added. To this Service all previously
+ * registered Servlet are added under the endpoints of the Servlets (which are configured
+ * via the Configurator).
+ *
+ * @param ref reference to the Service
+ */
+ public synchronized void addHttpService(ServiceReference ref, HttpService httpService) {
+ m_httpServices.add(ref);
+ // register all servlets to this new HttpService
+ for (ServiceReference reference : m_servlets.keySet()) {
+ Servlet servlet = (Servlet)m_context.getService(reference);
+ String endpoint = (String)reference.getProperty(HttpConstants.ENDPOINT);
+ if ((servlet != null) && (endpoint != null)) {
+ try {
+ httpService.registerServlet(endpoint, servlet, null, null);
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_WARNING, "Already registered under existing endpoint", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Callback method used in case a HttpService is being removed. From this Service all previously
+ * registered Servlet are removed using the endpoints of the Servlets (which are configured
+ * via the Configurator).
+ *
+ * @param ref reference to the Service
+ */
+ public synchronized void removeHttpService(ServiceReference ref, HttpService httpService) {
+ m_httpServices.remove(ref);
+ // remove references from the unregistered HttpService
+ unregisterEndpoints(httpService);
+ }
+
+ @Override
+ public synchronized void destroy(BundleContext context, DependencyManager arg1) throws Exception {
+ for (ServiceReference httpRef : m_httpServices) {
+ HttpService httpService = (HttpService)m_context.getService(httpRef);
+ if (httpService != null) {
+ unregisterEndpoints(httpService);
+ }
+ }
+ m_httpServices.clear();
+ m_servlets.clear();
+ m_context = null;
+ }
+
+ /**
+ * Unregisters all Servlets (via their endpoints) from the HttpService being passed to
+ * this method.
+ *
+ * @param httpService the HttpService that is being unregistered
+ */
+ private void unregisterEndpoints(HttpService httpService) {
+ for (ServiceReference reference : m_servlets.keySet()) {
+ String endpoint = (String)reference.getProperty(HttpConstants.ENDPOINT);
+ if (endpoint != null) {
+ try {
+ httpService.unregister(endpoint);
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_WARNING, "Servlet cannot be unregistered, maybe not registered under this endpoint", e);
+ }
+ }
+ }
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/http/listener/constants/HttpConstants.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/http/listener/constants/HttpConstants.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/http/listener/constants/HttpConstants.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/http/listener/constants/HttpConstants.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,8 @@
+package net.luminis.liq.http.listener.constants;
+
+public interface HttpConstants {
+ /**
+ * Endpoint constant to be used by several Servlet bundles.
+ */
+ public static final String ENDPOINT = "net.luminis.liq.server.servlet.endpoint";
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/LocationService.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/LocationService.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/LocationService.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/LocationService.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,9 @@
+package net.luminis.liq.location;
+
+import java.net.URL;
+
+public interface LocationService {
+ public URL getLocation();
+ public String getServerType();
+ public int getServerLoad();
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,47 @@
+package net.luminis.liq.location.upnp;
+
+import net.luminis.liq.location.LocationService;
+import net.luminis.liq.location.upnp.util.HostUtil;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.upnp.UPnPDevice;
+
+public class Activator extends DependencyActivatorBase {
+
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+
+ //we need these to construct the actual presentation url for the service
+ int port = Integer.valueOf(context.getProperty("org.osgi.service.http.port"));
+ String host = HostUtil.getHost();
+
+ ProvisioningDevice psDevice = new ProvisioningDevice(host, port);
+
+
+
+ //this service is configured with the correct settings
+ manager.add(createService()
+ .setImplementation(new LocationServiceImpl(host, port))
+ .setInterface(LocationService.class.getName(), null)
+ .add(createConfigurationDependency().setPid(LocationServiceImpl.PID))
+ );
+
+ //this service depends on the highest ranked location service
+ manager.add(createService()
+ .setImplementation(psDevice)
+ .setInterface(UPnPDevice.class.getName(), psDevice.getDescriptions(null))
+ .setComposition("getComposition")
+ .add(createServiceDependency().setService(HttpService.class).setRequired(true))
+ .add(createServiceDependency().setService(LocationService.class).setRequired(true))
+ );
+
+ };
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/LocationServiceImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/LocationServiceImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/LocationServiceImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/LocationServiceImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,101 @@
+package net.luminis.liq.location.upnp;
+
+import java.net.URL;
+import java.util.Dictionary;
+
+import net.luminis.liq.location.LocationService;
+
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ *
+ * the actual implementation for the Location Service.
+ *
+ *
+ * @author dennisg
+ *
+ */
+public class LocationServiceImpl implements LocationService, ManagedService {
+
+
+
+
+
+ public final static String PID = "net.luminis.liq.location.upnp.LocationService";
+ private final static String ENDPOINT_URL = "endpoint.url";
+ private final static String ENDPOINT_TYPE = "endpoint.type";
+
+
+ private final String m_host;
+
+ private final int m_port;
+
+ private URL m_location;
+
+ private String m_serverType;
+
+
+ public LocationServiceImpl(String host, int port) {
+ m_host = host;
+ m_port = port;
+ }
+
+ private String get(Dictionary dict, String key) throws ConfigurationException {
+ Object val = dict.get(key);
+
+ if (val == null) {
+ throw new ConfigurationException(key, "no such key defined");
+ }
+
+ if (val instanceof String) {
+ return (String)val;
+ }
+ throw new ConfigurationException(key, "invalid format (not a String)");
+
+ }
+
+
+ public synchronized void updated(Dictionary dict) throws ConfigurationException {
+
+ if (dict == null) {
+ return;
+ }
+
+ String serverType = get(dict, ENDPOINT_TYPE);
+ String url = get(dict, ENDPOINT_URL);
+
+ URL location = null;
+
+ try {
+ //we expect something like:
+ //http://<host>:<port>/xyz
+
+ url = url.replaceFirst("<host>", m_host);
+ url = url.replaceFirst("<port>", "" + m_port);
+ location = new URL(url);
+
+ }
+ catch (Exception e) {
+ throw new ConfigurationException(null, e.getMessage());
+ }
+
+ //all's well: apply
+ m_serverType = serverType;
+ m_location = location;
+
+ }
+
+ public URL getLocation() {
+ return m_location;
+ }
+
+ public String getServerType() {
+ return m_serverType;
+ }
+
+ public int getServerLoad() {
+ return (int)(40 + Math.random()*10);
+ }
+
+}
\ No newline at end of file
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/ProvisioningDevice.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/ProvisioningDevice.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/ProvisioningDevice.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/ProvisioningDevice.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,144 @@
+package net.luminis.liq.location.upnp;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.util.Dictionary;
+import java.util.Properties;
+import java.util.UUID;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.osgi.service.http.HttpService;
+import org.osgi.service.upnp.UPnPDevice;
+import org.osgi.service.upnp.UPnPIcon;
+import org.osgi.service.upnp.UPnPService;
+
+public class ProvisioningDevice extends HttpServlet implements UPnPDevice {
+
+ final private String DEVICE_ID = "uuid:" + UUID.randomUUID();
+
+ final static String BASE_URL = "/upnp/provisioningdevice";
+
+ private Properties m_properties;
+
+ //managed
+ private HttpService m_http;
+
+ private final String m_host;
+ private final int m_port;
+
+ private final UPnPLocationServiceWrapper m_wrapper;
+
+ public ProvisioningDevice(String host, int port) throws Exception {
+ m_host = host;
+ m_port = port;
+ m_wrapper = new UPnPLocationServiceWrapper();
+
+ setupDeviceProperties();
+ }
+
+ public Object[] getComposition() {
+ return new Object[]{this, m_wrapper};
+ }
+
+ private void start() throws Exception {
+ m_http.registerServlet(BASE_URL, this, null, null);
+ }
+
+
+ private void stop() {
+ m_http.unregister(BASE_URL);
+ }
+
+ private void setupDeviceProperties() throws MalformedURLException {
+
+ m_properties = new Properties();
+ m_properties.put(UPnPDevice.UPNP_EXPORT,"");
+
+
+ //this is odd, we have to have this
+ // property here, otherwise the device will not be exported
+ //although the docs say otherwise
+ m_properties.put(
+ org.osgi.service.device.Constants.DEVICE_CATEGORY,
+ new String[]{UPnPDevice.DEVICE_CATEGORY}
+ );
+
+ m_properties.put(UPnPDevice.FRIENDLY_NAME,"UPnP Provisioning Device");
+ m_properties.put(UPnPDevice.MANUFACTURER,"luminis iQ Products");
+ m_properties.put(UPnPDevice.MANUFACTURER_URL,"http://www.luminiq.nl");
+ m_properties.put(UPnPDevice.MODEL_DESCRIPTION,"A Provisioning Device that is automagically locatable by gateways");
+ m_properties.put(UPnPDevice.MODEL_NAME,"DennisG");
+ m_properties.put(UPnPDevice.MODEL_NUMBER,"1.0");
+ m_properties.put(UPnPDevice.MODEL_URL,"https://opensource.luminis.net/subversion/oss/projects/upnp-models/");
+
+ m_properties.put(UPnPDevice.PRESENTATION_URL, "http://" + m_host + ":" + m_port + BASE_URL);
+ m_properties.put(UPnPDevice.SERIAL_NUMBER,"123456789");
+ m_properties.put(UPnPDevice.TYPE, UPnPConstants.PROVISIONING_DEVICE_TYPE);
+ m_properties.put(UPnPDevice.UDN, DEVICE_ID);
+ m_properties.put(UPnPDevice.UPC,"123456789");
+
+
+ m_properties.put(UPnPService.ID, m_wrapper.getId());
+ m_properties.put(UPnPService.TYPE, m_wrapper.getType());
+
+ }
+
+
+ public Dictionary getDescriptions(String name) {
+ return m_properties;
+ }
+
+ public UPnPIcon[] getIcons(String name) {
+ //sorry, no icons yet
+ return new UPnPIcon[0];
+ }
+
+ public UPnPService getService(String id) {
+ if (m_wrapper.getId().equals(id)) {
+ return m_wrapper;
+ }
+ return null;
+ }
+
+ public UPnPService[] getServices() {
+ return new UPnPService[]{m_wrapper};
+ }
+
+
+
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html>");
+ out.println("<head><title>Luminis Provisioning Device</title></head>");
+ out.println("<body>");
+ out.println(" <center>");
+ out.println(" <h1><font face='Arial' color='#808080'>Luminis Provisioning Device</font></h1>");
+
+ out.println(" <p><strong>Location: <i>" + m_wrapper.getLocation() + "</i></strong</p>");
+ out.println(" <p><strong>Server Type: [" + m_wrapper.getServerType() + "]</strong</p>");
+ out.println(" <p><strong>Server Load: [" + m_wrapper.getServerLoad() + "%]</strong</p>");
+
+ out.println(" <p><a href=" + BASE_URL + "/>Refresh</a></p>");
+ out.println(" </center>");
+ out.println(" </body>");
+ out.println("</html>");
+ out.flush();
+ }
+
+
+ @Override
+ public void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+ doGet(request, response);
+ }
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/UPnPConstants.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/UPnPConstants.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/UPnPConstants.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/UPnPConstants.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,8 @@
+package net.luminis.liq.location.upnp;
+
+public interface UPnPConstants {
+
+ public final static String PROVISIONING_DEVICE_TYPE = "urn:schemas-upnp-org:device:ProvisioningDevice:1";
+ public final static String LOCATION_SERVICE_TYPE = "urn:schemas-upnp-org:service:LocationService:1";
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/UPnPLocationServiceWrapper.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/UPnPLocationServiceWrapper.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/UPnPLocationServiceWrapper.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/UPnPLocationServiceWrapper.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,93 @@
+package net.luminis.liq.location.upnp;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.luminis.liq.location.LocationService;
+import net.luminis.liq.location.upnp.actions.GetLocationAction;
+import net.luminis.liq.location.upnp.actions.GetServerLoadAction;
+import net.luminis.liq.location.upnp.actions.GetServerTypeAction;
+
+import org.osgi.service.upnp.UPnPAction;
+import org.osgi.service.upnp.UPnPService;
+import org.osgi.service.upnp.UPnPStateVariable;
+
+public class UPnPLocationServiceWrapper implements UPnPService, LocationService {
+
+ final static private String SERVICE_ID = "urn:upnp-org:serviceId:LocationService:1";
+
+ //managed
+ private volatile LocationService m_ls;
+
+ volatile private Map<String, UPnPAction> m_actions;
+
+
+ public UPnPLocationServiceWrapper() {
+
+ m_actions = new HashMap<String, UPnPAction>();
+
+ UPnPAction location = new GetLocationAction(this);
+ m_actions.put(location.getName(), location);
+
+ UPnPAction type = new GetServerTypeAction(this);
+ m_actions.put(type.getName(), type);
+
+ UPnPAction load = new GetServerLoadAction(this);
+ m_actions.put(load.getName(), load);
+ }
+
+ public URL getLocation() {
+ return m_ls.getLocation();
+ }
+
+ public String getServerType() {
+ return m_ls.getServerType();
+ }
+
+ public int getServerLoad() {
+ return (int)(40 + Math.random()*10);
+ }
+
+
+ public UPnPAction getAction(String actionName) {
+ return m_actions.get(actionName);
+ }
+
+ public UPnPAction[] getActions() {
+ return m_actions.values().toArray(new UPnPAction[0]);
+ }
+
+ public String getId() {
+ return SERVICE_ID;
+ }
+
+ public UPnPStateVariable getStateVariable(String id) {
+ UPnPAction action = m_actions.get(id);
+ if (action != null) {
+ return action.getStateVariable(null);
+ }
+ return null;
+ }
+
+ public UPnPStateVariable[] getStateVariables() {
+ int i=0;
+ UPnPStateVariable[] states = new UPnPStateVariable[m_actions.size()];
+ for (UPnPAction action : m_actions.values()) {
+ states[i++] = action.getStateVariable(null);
+ }
+ return states;
+ }
+
+ public String getType() {
+ return UPnPConstants.LOCATION_SERVICE_TYPE;
+ }
+
+
+ public String getVersion() {
+ return "1";
+ }
+
+
+
+}
\ No newline at end of file
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetLocationAction.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetLocationAction.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetLocationAction.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetLocationAction.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,79 @@
+package net.luminis.liq.location.upnp.actions;
+
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import net.luminis.liq.location.LocationService;
+
+import org.osgi.service.upnp.UPnPAction;
+import org.osgi.service.upnp.UPnPStateVariable;
+
+public class GetLocationAction implements UPnPAction {
+
+ final private String NAME = "GetLocation";
+ final private String RET_TARGET_VALUE = "RetLocationValue";
+ final private String[] OUT_ARG_NAMES = new String[]{RET_TARGET_VALUE};
+ private UPnPStateVariable state;
+
+ private final LocationService m_locationService;
+
+
+ public GetLocationAction(LocationService ls) {
+ m_locationService = ls;
+ state = new StateVarImpl();
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getReturnArgumentName() {
+ return RET_TARGET_VALUE;
+ }
+
+ public String[] getInputArgumentNames() {
+ return null;
+ }
+
+ public String[] getOutputArgumentNames() {
+ return OUT_ARG_NAMES;
+ }
+
+ public UPnPStateVariable getStateVariable(String argumentName) {
+ return state;
+ }
+
+ public Dictionary invoke(Dictionary args) throws Exception {
+ URL location = m_locationService.getLocation();
+
+ Hashtable result = new Hashtable();
+ result.put(RET_TARGET_VALUE, location.toExternalForm());
+ return result;
+ }
+
+
+ private class StateVarImpl extends StateVar {
+
+
+ public String getName() {
+ return "Location";
+ }
+
+ public Object getCurrentValue() {
+ URL location = m_locationService.getLocation();
+ if (location != null) {
+ return location.toString();
+ }
+ return null;
+ }
+
+ public Class getJavaDataType() {
+ return String.class;
+ }
+
+ public String getUPnPDataType() {
+ return TYPE_STRING;
+ }
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetServerLoadAction.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetServerLoadAction.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetServerLoadAction.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetServerLoadAction.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,91 @@
+package net.luminis.liq.location.upnp.actions;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import net.luminis.liq.location.LocationService;
+
+import org.osgi.service.upnp.UPnPAction;
+import org.osgi.service.upnp.UPnPStateVariable;
+
+
+public class GetServerLoadAction implements UPnPAction {
+
+ final private String NAME = "GetServerLoad";
+ final private String RET_TARGET_VALUE = "RetServerLoadValue";
+ final private String[] OUT_ARG_NAMES = new String[]{RET_TARGET_VALUE};
+ private UPnPStateVariable state;
+
+ private final LocationService m_locationService;
+
+
+ public GetServerLoadAction(LocationService ls) {
+ m_locationService = ls;
+ state = new StateVarImpl();
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getReturnArgumentName() {
+ return RET_TARGET_VALUE;
+ }
+
+ public String[] getInputArgumentNames() {
+
+ return null;
+ }
+
+ public String[] getOutputArgumentNames() {
+ return OUT_ARG_NAMES;
+ }
+
+ public UPnPStateVariable getStateVariable(String argumentName) {
+ return state;
+ }
+
+ public Dictionary invoke(Dictionary args) throws Exception {
+ int load = m_locationService.getServerLoad();
+
+ Hashtable result = new Hashtable();
+ result.put(RET_TARGET_VALUE, load);
+ return result;
+ }
+
+ private class StateVarImpl extends StateVar {
+
+
+ public String getName() {
+ return "ServerLoad";
+ }
+
+ public Object getCurrentValue() {
+ return m_locationService.getServerLoad();
+ }
+
+ public Class getJavaDataType() {
+ return Integer.TYPE;
+ }
+
+ public String getUPnPDataType() {
+ return TYPE_INT;
+ }
+
+ @Override
+ public Number getMinimum() {
+ return 0;
+ }
+
+ @Override
+ public Number getMaximum() {
+ return 100;
+ }
+
+ @Override
+ public Number getStep() {
+ return 1;
+ }
+ }
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetServerTypeAction.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetServerTypeAction.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetServerTypeAction.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/GetServerTypeAction.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,78 @@
+package net.luminis.liq.location.upnp.actions;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import net.luminis.liq.location.LocationService;
+
+import org.osgi.service.upnp.UPnPAction;
+import org.osgi.service.upnp.UPnPStateVariable;
+
+
+public class GetServerTypeAction implements UPnPAction {
+
+ final private String NAME = "GetServerType";
+ final private String RET_TARGET_VALUE = "RetServerTypeValue";
+ final private String[] OUT_ARG_NAMES = new String[]{RET_TARGET_VALUE};
+ private UPnPStateVariable state;
+
+ private final LocationService m_locationService;
+
+
+ public GetServerTypeAction(LocationService ls) {
+ m_locationService = ls;
+ state = new StateVarImpl();
+
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getReturnArgumentName() {
+ return RET_TARGET_VALUE;
+ }
+
+ public String[] getInputArgumentNames() {
+
+ return null;
+ }
+
+ public String[] getOutputArgumentNames() {
+ return OUT_ARG_NAMES;
+ }
+
+ public UPnPStateVariable getStateVariable(String argumentName) {
+ return state;
+ }
+
+ public Dictionary invoke(Dictionary args) throws Exception {
+ String type = m_locationService.getServerType();
+
+ Hashtable result = new Hashtable();
+ result.put(RET_TARGET_VALUE, type);
+ return result;
+ }
+
+
+ private class StateVarImpl extends StateVar {
+
+
+ public String getName() {
+ return "ServerType";
+ }
+
+ public Object getCurrentValue() {
+ return m_locationService.getServerType();
+ }
+
+ public Class getJavaDataType() {
+ return String.class;
+ }
+
+ public String getUPnPDataType() {
+ return TYPE_STRING;
+ }
+ }
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/StateVar.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/StateVar.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/StateVar.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/actions/StateVar.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,30 @@
+package net.luminis.liq.location.upnp.actions;
+
+import org.osgi.service.upnp.UPnPLocalStateVariable;
+
+public abstract class StateVar implements UPnPLocalStateVariable {
+
+ public String[] getAllowedValues() {
+ return null;
+ }
+
+ public Object getDefaultValue() {
+ return null;
+ }
+
+ public Number getMinimum() {
+ return null;
+ }
+
+ public Number getMaximum() {
+ return null;
+ }
+
+ public Number getStep() {
+ return null;
+ }
+
+ public boolean sendsEvents() {
+ return false;
+ }
+}
\ No newline at end of file
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/util/HostUtil.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/util/HostUtil.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/util/HostUtil.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/util/HostUtil.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,28 @@
+package net.luminis.liq.location.upnp.util;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class HostUtil {
+
+ private HostUtil(){}
+
+
+ public static final String getHost() {
+ String host;
+
+ InetAddress inet;
+ try {
+ inet = InetAddress.getLocalHost();
+ host = inet.getHostAddress();
+ } catch (UnknownHostException e) {
+ System.out.println("Warning: enable to create host name");
+ host = "localhost";
+ }
+
+ return host;
+ }
+
+
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/util/Inspector.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/util/Inspector.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/util/Inspector.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/location/upnp/util/Inspector.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,105 @@
+package net.luminis.liq.location.upnp.util;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.osgi.service.upnp.UPnPAction;
+import org.osgi.service.upnp.UPnPService;
+import org.osgi.service.upnp.UPnPStateVariable;
+
+
+public class Inspector {
+
+
+
+ private static UPnPService createService(Class iface, Object target) throws Exception {
+ Method[] methods = iface.getMethods();
+
+ List<UPnPAction> list = new ArrayList<UPnPAction>();
+ for (Method method : methods) {
+ list.add(createAction(method, target));
+ }
+ return null;
+ }
+
+
+
+ private static UPnPAction createAction(Method m, Object target) {
+ return new UPnPActionImpl(m, target);
+ }
+
+
+
+
+ private static class UPnPActionImpl implements UPnPAction {
+
+ private final Method m_method;
+ private final Object m_target;
+
+ public UPnPActionImpl(Method m, Object t) {
+ m_method = m;
+ m_target = t;
+ }
+
+
+ public String getName() {
+ return m_method.getName();
+ }
+
+ public String[] getInputArgumentNames() {
+ Class[] inputArgTypes = m_method.getParameterTypes();
+ String[] inputArgs = new String[inputArgTypes.length];
+
+ if (inputArgs.length == 1) {
+ inputArgs[0] = inputArgTypes[0].getSimpleName();
+ }
+ else {
+ int i = 0;
+ for (Class inputType : inputArgTypes) {
+ inputArgs[i] = inputArgTypes[i].getSimpleName() + i;
+ i++;
+ }
+ }
+ return inputArgs;
+ }
+
+ public String[] getOutputArgumentNames() {
+ return new String[]{getReturnArgumentName()};
+ }
+
+ public String getReturnArgumentName() {
+ Class returnType = m_method.getReturnType();
+ return returnType.getSimpleName();
+ }
+
+ public UPnPStateVariable getStateVariable(String arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Dictionary invoke(Dictionary dict) throws Exception {
+ String[] argNames = getInputArgumentNames();
+ Object[] args = new Object[argNames.length];
+
+ int i=0;
+ for (String name : argNames) {
+ args[i++] = dict.get(name);
+ }
+
+ Object retVal = m_method.invoke(m_target, args);
+ if (retVal==null) {
+ return null;
+ }
+
+ Properties p = new Properties();
+ p.put(getReturnArgumentName(), retVal);
+ return p;
+ }
+
+
+
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/MetadataGenerator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/MetadataGenerator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/MetadataGenerator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/MetadataGenerator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,18 @@
+package net.luminis.liq.obr.metadata;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface MetadataGenerator {
+
+ /**
+ * Generates the repository.xml based upon the new set of Bundles in the given directory. The xml is created
+ * as result of this method in the given directory in a file called repository.xml.
+ * This methods creates the file in an atomic fashion (this includes retrying to overwrite an existing file until success).
+ *
+ * @param directory the location where to store the newly created repository.xml
+ *
+ * @throws IOException If I/O problems occur when generating the new meta data index file.
+ */
+ public void generateMetadata(File directory) throws IOException;
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/bindex/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/bindex/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/bindex/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/bindex/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,26 @@
+package net.luminis.liq.obr.metadata.bindex;
+
+import net.luminis.liq.obr.metadata.MetadataGenerator;
+
+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 {
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setInterface(MetadataGenerator.class.getName(), null)
+ .setImplementation(BIndexMetadataGenerator.class)
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nothing to be done
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/bindex/BIndexMetadataGenerator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/bindex/BIndexMetadataGenerator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/bindex/BIndexMetadataGenerator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/obr/metadata/bindex/BIndexMetadataGenerator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,57 @@
+package net.luminis.liq.obr.metadata.bindex;
+
+import java.io.File;
+import java.io.IOException;
+
+import net.luminis.liq.obr.metadata.MetadataGenerator;
+
+import org.osgi.impl.bundle.bindex.Index;
+import org.osgi.service.log.LogService;
+
+public class BIndexMetadataGenerator implements MetadataGenerator {
+
+ private static final String INDEX_FILENAME = "repository";
+ private static final String INDEX_EXTENSION = ".xml";
+
+ private LogService m_log; /* will be injected by dependencymanager */
+
+ public void generateMetadata(File directory) throws IOException {
+ if (directory.isDirectory()) {
+ File tempIndex;
+ File index = new File(directory, INDEX_FILENAME + INDEX_EXTENSION);
+ try {
+ tempIndex = File.createTempFile("repo", INDEX_EXTENSION, directory);
+ Index.main(new String[] {"-q", "-a", "-r", tempIndex.getAbsolutePath(), directory.getAbsolutePath()});
+ // TODO: try to move the index file to it's final location, this can fail if the target
+ // file was not released by a third party before we were called (not all platforms support reading and writing
+ // to a file at the same time), for now we will try 10 times and throw an IOException if the move has not
+ // succeeded by then.
+ boolean renameOK = false;
+ int attempts = 0;
+ while(!renameOK && (attempts < 10)) {
+ index.delete();
+ renameOK = tempIndex.renameTo(index);
+ if (!renameOK) {
+ attempts++;
+ Thread.sleep(1000);
+ }
+ }
+ if (!renameOK) {
+ m_log.log(LogService.LOG_ERROR, "Unable to move new repository index to it's final location.");
+ throw new IOException("Could not move temporary index file (" + tempIndex.getAbsolutePath() + ") to it's final location (" + index.getAbsolutePath() + ")");
+ }
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_ERROR, "Unable to create temporary file for new repository index.", e);
+ throw e;
+ }
+ catch (InterruptedException e) {
+ m_log.log(LogService.LOG_ERROR, "Waiting for next attempt to move temporary repository index failed.", e);
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_ERROR, "Failed to generate new repository index.", e);
+ throw new IOException("Failed to generate new repository index. + (" + e.getMessage() + ")");
+ }
+ }
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/obr/servlet/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/obr/servlet/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/obr/servlet/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/obr/servlet/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,35 @@
+package net.luminis.liq.obr.servlet;
+
+import javax.servlet.http.HttpServlet;
+
+import net.luminis.liq.obr.storage.BundleStore;
+
+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.obr.servlet";
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setInterface(HttpServlet.class.getName(), null)
+ .setImplementation(BundleServlet.class)
+ .add(createConfigurationDependency()
+ .setPropagate(true)
+ .setPid(PID))
+ .add(createServiceDependency()
+ .setService(BundleStore.class)
+ .setRequired(true))
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/obr/servlet/BundleServlet.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/obr/servlet/BundleServlet.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/obr/servlet/BundleServlet.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/obr/servlet/BundleServlet.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,185 @@
+package net.luminis.liq.obr.servlet;
+
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.luminis.liq.obr.storage.BundleStore;
+
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+public class BundleServlet extends HttpServlet implements ManagedService {
+ public static final String TEXT_MIMETYPE = "text/plain";
+
+ private static final long serialVersionUID = 1L;
+ private static final int COPY_BUFFER_SIZE = 1024;
+
+ private volatile LogService m_log; /* will be injected by dependencymanager */
+ private volatile BundleStore m_store; /* will be injected by dependencymanager */
+
+ /**
+ * Responds to POST requests sent to http://host:port/obr/resource by writing the received data to the bundle store.
+ * Will send out a response that contains one of the following status codes:
+ * <ul>
+ * <li><code>HttpServletResponse.SC_BAD_REQUEST</code> - if no resource was specified</li>
+ * <li><code>HttpServletResponse.SC_CONFLICT</code> - if the resource already exists</li>
+ * <li><code>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</code> - if there was a problem storing the resource</li>
+ * <li><code>HttpServletResponse.SC_OK</code> - if all went fine</li>
+ * </ul>
+ */
+ @Override
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) {
+ String path = request.getPathInfo();
+ if ((path == null) || (path.length() <= 1)) {
+ sendResponse(response, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ else {
+ String id = path.substring(1);
+ try {
+ if (m_store.put(id, request.getInputStream())) {
+ sendResponse(response, HttpServletResponse.SC_OK);
+ }
+ else {
+ sendResponse(response, HttpServletResponse.SC_CONFLICT);
+ }
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_WARNING, "Exception handling request: " + request.getRequestURL(), e);
+ sendResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+ }
+
+ /**
+ * Responds to DELETE requests sent to http://host:port/obr/resource by deleting the file specified.
+ * Will send out a response that contains one of the following status codes:
+ * <br>
+ * <li><code>HttpServletResponse.SC_BAD_REQUEST</code> - if no resource was specified
+ * <li><code>HttpServletResponse.SC_NOT_FOUND</code> - if the specified resource did not exist
+ * <li><code>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</code> - if there was a problem deleting the resource
+ * <li><code>HttpServletResponse.SC_OK</code> - if all went fine
+ */
+ @Override
+ protected void doDelete(HttpServletRequest request, HttpServletResponse response) {
+ String path = request.getPathInfo();
+ if ((path == null) || (path.length() <= 1)) {
+ sendResponse(response, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ else {
+ String id = path.substring(1);
+ try {
+ if (m_store.remove(id)) {
+ sendResponse(response, HttpServletResponse.SC_OK);
+ }
+ else {
+ sendResponse(response, SC_NOT_FOUND);
+ }
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_WARNING, "Exception handling request: " + request.getRequestURL(), e);
+ sendResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+ }
+
+ /**
+ * Responds to GET requests sent to http://host:port/obr/resource with a stream to the specified filename.
+ * Will send out a response that contains one of the following status codes:
+ * <br>
+ * <li><code>HttpServletResponse.SC_BAD_REQUEST</code> - if no resource is specified
+ * <li><code>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</code> - if there was a problem storing the resource
+ * <li><code>HttpServletResponse.SC_OK</code> - if all went fine
+ * <br>
+ * The response will only contain the data of the requested resource if the status code of the response is
+ * <code>HttpServletResponse.SC_OK</code>.
+ */
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) {
+ String path = request.getPathInfo();
+ if ((path == null) || (path.length() <= 1)) {
+ sendResponse(response, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ else {
+ String id = path.substring(1);
+ response.setContentType(TEXT_MIMETYPE);
+
+ ServletOutputStream output = null;
+ try {
+ output = response.getOutputStream();
+ InputStream fileStream = null;
+ try {
+ fileStream = m_store.get(id);
+
+ if (fileStream != null) {
+ // send the bundle as stream to the caller
+ byte[] buffer = new byte[COPY_BUFFER_SIZE];
+ for (int bytes = fileStream.read(buffer); bytes != -1; bytes = fileStream.read(buffer)) {
+ output.write(buffer, 0, bytes);
+ }
+ }
+ else {
+ sendResponse(response, HttpServletResponse.SC_NOT_FOUND);
+ }
+ }
+ finally {
+ if (fileStream != null) {
+ try {
+ fileStream.close();
+ }
+ catch (IOException ioe) {
+ m_log.log(LogService.LOG_WARNING, "Exception closing the stream" + request.getRequestURL(), ioe);
+ }
+ }
+ }
+ }
+ catch (IOException ex) {
+ m_log.log(LogService.LOG_WARNING, "Exception in request: " + request.getRequestURL(), ex);
+ sendResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+ 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
+ private void sendResponse(HttpServletResponse response, int statusCode) {
+ sendResponse(response, statusCode, "");
+ }
+
+ // send a response with the specified status code and description
+ private void sendResponse(HttpServletResponse response, int statusCode, String description) {
+ try {
+ response.sendError(statusCode, description);
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_WARNING, "Unable to send response with status code '" + statusCode + "'", e);
+ }
+ }
+
+ @Override
+ public String getServletInfo() {
+ return "LiQ OBR Servlet";
+ }
+
+ @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/obr/storage/BundleStore.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/BundleStore.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/BundleStore.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/BundleStore.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,37 @@
+package net.luminis.liq.obr.storage;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.osgi.service.cm.ManagedService;
+
+public interface BundleStore extends ManagedService {
+
+ /**
+ * Returns an <code>InputStream</code> to the data of the specified resource.
+ *
+ * @param fileName Identifier of the requested resource.
+ * @return <code>InputStream</code> to the requested resource or <code>null</code> if no such resource is available.
+ * @throws IOException If there was a problem returning the requested resource.
+ */
+ public InputStream get(String fileName) throws IOException;
+
+ /**
+ * Stores the specified resource in the store.
+ *
+ * @param fileName Identifier of the resource.
+ * @param data The actual data of the resource.
+ * @return <code>true</code> if the resource was successfully stored, <code>false</code> if the resource already existed
+ * @throws IOException If there was a problem reading or writing the data of the resource.
+ */
+ public boolean put(String fileName, InputStream data) throws IOException;
+
+ /**
+ * Removes the specified resource from the store.
+ *
+ * @param filename Identifier of the resource.
+ * @return <code>true</code> if the resource was successfully removed, <code>false</code> if the resource was not present to begin with
+ * @throws IOException If there was a problem removing the data of the resource from the store.
+ */
+ public boolean remove(String filename) throws IOException;
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,33 @@
+package net.luminis.liq.obr.storage.file;
+
+import net.luminis.liq.obr.metadata.MetadataGenerator;
+import net.luminis.liq.obr.storage.BundleStore;
+
+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.obr.storage.file";
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setInterface(BundleStore.class.getName(), null)
+ .setImplementation(BundleFileStore.class)
+ .add(createConfigurationDependency()
+ .setPid(PID))
+ .add(createServiceDependency()
+ .setService(MetadataGenerator.class)
+ .setRequired(true))
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nothing to do here
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/BundleFileStore.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/BundleFileStore.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/BundleFileStore.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/BundleFileStore.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,149 @@
+package net.luminis.liq.obr.storage.file;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import net.luminis.liq.obr.metadata.MetadataGenerator;
+import net.luminis.liq.obr.storage.BundleStore;
+import net.luminis.liq.obr.storage.file.constants.OBRFileStoreConstants;
+
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * This BundleStore retrieves the files from the file system. Via the Configurator the relative path is set, and all bundles and
+ * the repository.xml should be retrievable from that path (which will internally be converted to an absolute path).
+ */
+public class BundleFileStore implements BundleStore, ManagedService {
+ private static int BUFFER_SIZE = 8 * 1024;
+
+ private volatile MetadataGenerator m_metadata; /* will be injected by dependencymanager */
+ private volatile LogService m_log; /* will be injected by dependencymanager */
+
+ private final Map<String, Long> m_foundFiles = new ConcurrentHashMap<String, Long>();
+ private volatile File m_dir;
+
+ @SuppressWarnings("unused")
+ private void start() {
+ try {
+ generateMetadata();
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_ERROR, "Could not generate initial meta data for bundle repository");
+ }
+ }
+
+ public InputStream get(String fileName) throws IOException {
+ if (fileName.equals("repository.xml") && directoryChanged()) {
+ generateMetadata(); // might be called too often
+ }
+ return new FileInputStream(new File(m_dir, fileName));
+ }
+
+ public synchronized boolean put(String fileName, InputStream data) throws IOException {
+ File file = new File(m_dir, fileName);
+ if (!file.exists()) {
+ FileOutputStream output = null;
+ File tempFile = null;
+ boolean success = false;
+ try {
+ tempFile = File.createTempFile("obr", ".tmp");
+ output = new FileOutputStream(tempFile);
+ byte[] buffer = new byte[BUFFER_SIZE];
+ for (int count = data.read(buffer); count != -1; count = data.read(buffer)) {
+ output.write(buffer, 0, count);
+ }
+ success = true;
+ }
+ finally {
+ if (output != null) {
+ output.flush();
+ output.close();
+ }
+ }
+ if (success) {
+ tempFile.renameTo(file);
+ }
+ return success;
+ }
+ return false;
+ }
+
+ public synchronized boolean remove(String fileName) throws IOException {
+ File file = new File(m_dir, fileName);
+ if (file.exists()) {
+ if (file.delete()) {
+ return true;
+ }
+ else {
+ throw new IOException("Unable to delete file (" + file.getAbsolutePath() + ")");
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("boxing")
+ private boolean directoryChanged() {
+ File[] files = m_dir.listFiles();
+
+ // if number of files changed, create new metadata
+ if (files.length != m_foundFiles.size()) {
+ return true;
+ }
+
+ // iterate over the current files
+ for (File current : files) {
+ Long modifiedDateAndLengthXOR = m_foundFiles.get(current.getAbsolutePath());
+ // if one of the current files is not in the old set of files, create new metadata
+ if (modifiedDateAndLengthXOR == null) {
+ return true;
+ }
+ // else if of one of the files the size or the date has been changed, create new metadata
+ if ((current.lastModified() ^ current.length()) != modifiedDateAndLengthXOR) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("boxing")
+ public synchronized void generateMetadata() throws IOException {
+ File[] files = m_dir.listFiles();
+ m_metadata.generateMetadata(m_dir);
+ for (File current : files) {
+ m_foundFiles.put(current.getAbsolutePath(), current.lastModified() ^ current.length());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public synchronized void updated(Dictionary dict) throws ConfigurationException {
+ if (dict != null) {
+ String path = (String) dict.get(OBRFileStoreConstants.FILE_LOCATION_KEY);
+ if (path == null) {
+ throw new ConfigurationException(OBRFileStoreConstants.FILE_LOCATION_KEY, "Missing property");
+ }
+ File dir = new File(path);
+ if (!dir.equals(m_dir)) {
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+ else if (!dir.isDirectory()) {
+ throw new ConfigurationException(OBRFileStoreConstants.FILE_LOCATION_KEY, "Is not a directory: " + dir);
+ }
+ m_dir = dir;
+ m_foundFiles.clear();
+ }
+ }
+ else {
+ // clean up after getting a null as dictionary, as the service is going to be pulled afterwards
+ m_foundFiles.clear();
+ }
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/constants/OBRFileStoreConstants.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/constants/OBRFileStoreConstants.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/constants/OBRFileStoreConstants.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/obr/storage/file/constants/OBRFileStoreConstants.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,5 @@
+package net.luminis.liq.obr.storage.file.constants;
+
+public interface OBRFileStoreConstants {
+ public static final String FILE_LOCATION_KEY = "fileLocation";
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/repository/ext/BackupRepository.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/repository/ext/BackupRepository.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/repository/ext/BackupRepository.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/repository/ext/BackupRepository.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,47 @@
+package net.luminis.liq.repository.ext;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Provides an interface for backing up objects. <code>write</code> and <code>read</code>
+ * allow writing and reading of the current version of the object. <code>backup</code>
+ * backs up the object, and <code>restore</code> restores it from a previously backed up
+ * version, if any. There is no way to directly use the backup.
+ */
+public interface BackupRepository {
+
+ /**
+ * Writes the input stream to the current object.
+ * @param data The data to be written. Remember to close this stream, if necessary.
+ * @throws IOException Will be thrown when (a) the input stream gets closed
+ * unexpectedly, or (b) there is an error writing the data.
+ */
+ public void write(InputStream data) throws IOException;
+
+ /**
+ * Reads the input stream from the current object. If there is no current version,
+ * an empty stream will be returned.
+ * @return An input stream, from which can be read. Remember to close it.
+ * @throws IOException Will be thrown when there is a problem storing the data.
+ */
+ public InputStream read() throws IOException;
+
+ /**
+ * Restores a previously backuped version of the object.
+ * @return True when there was a previously backup version which has
+ * now been restored, false otherwise.
+ * @throws IOException Thrown when the restore process goes bad.
+ */
+ public boolean restore() throws IOException;
+
+ /**
+ * Backs up the current version of the object, overwriting a previous
+ * backup, if any.
+ * @return True when there was a current version to be backed up, false
+ * otherwise.
+ * @throws IOException Thrown when the restore process goes bad.
+ */
+ public boolean backup() throws IOException;
+
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/repository/ext/CachedRepository.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/repository/ext/CachedRepository.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/repository/ext/CachedRepository.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/repository/ext/CachedRepository.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,71 @@
+package net.luminis.liq.repository.ext;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import net.luminis.liq.repository.Repository;
+
+
+/**
+ * Provides a cached repository representation, allowing the storing of local changes, without
+ * committing them to the actual repository immediately.
+ */
+public interface CachedRepository extends Repository {
+
+ /**
+ * Checks our the most current version from the actual repository.
+ * @param fail Indicates that this method should throw an IOException when no data
+ * is available. Setting it to <code>false</code> will make it return an
+ * empty stream in that case.
+ * @return An input stream representing the checked out object.
+ * @throws IOException Is thrown when the actual repository's commit does.
+ */
+ public InputStream checkout(boolean fail) throws IOException;
+
+ /**
+ * Commits the most current version to the actual repository.
+ * @return true on success, false on failure (e.g. bad version number)
+ * @throws IOException Is thrown when the actual repository's commit does.
+ */
+ public boolean commit() throws IOException;
+
+ /**
+ * Gets the most recent version of the object. If no current version is available,
+ * and empty stream will be returned.
+ * @param fail Indicates that this method should throw an IOException when no data
+ * is available. Setting it to <code>false</code> will make it return an
+ * empty stream in that case.
+ * @return An input stream representing the most recently written object.
+ * @throws IOException Thrown when there is a problem retrieving the data.
+ */
+ public InputStream getLocal(boolean fail) throws IOException;
+
+ /**
+ * Writes the most recent version of the object.
+ * @throws IOException Thrown when there is a problem storing the data.
+ */
+ public void writeLocal(InputStream data) throws IOException;
+
+ /**
+ * Undoes all changes made using <code>writeLocal()</code> since the
+ * last <code>commit</code> or <code>checkout</code>.
+ * @throws IOException
+ */
+ public boolean revert() throws IOException;
+
+ /**
+ * Gets the most recent version of this repository, that is, the most recent version
+ * number that is either committed (successfully) or checked out.
+ * @return The most recent version of the underlying repository.
+ */
+ public long getMostRecentVersion();
+
+ /**
+ * Checks whether the version we have locally is current with respect to the version
+ * on the server.
+ * @return whether the version we have locally is current with respect to the version
+ * on the server.
+ * @throws IOException Thrown when an error occurs communicating with the server.
+ */
+ public boolean isCurrent() throws IOException;
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/repository/impl/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/repository/impl/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/repository/impl/Activator.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/repository/impl/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,31 @@
+package net.luminis.liq.repository.impl;
+
+
+import java.util.Properties;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.log.LogService;
+import org.osgi.service.prefs.PreferencesService;
+
+public class Activator extends DependencyActivatorBase {
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ Properties props = new Properties();
+ props.put(Constants.SERVICE_PID, "net.luminis.liq.server.repository.factory");
+ manager.add(createService()
+ .setInterface(ManagedServiceFactory.class.getName(), props)
+ .setImplementation(new RepositoryFactory(manager))
+ .add(createServiceDependency().setService(PreferencesService.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nothing to do here
+ }
+}
Added: incubator/ace/trunk/server/src/net/luminis/liq/repository/impl/CachedRepositoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/repository/impl/CachedRepositoryImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/repository/impl/CachedRepositoryImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/repository/impl/CachedRepositoryImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,149 @@
+package net.luminis.liq.repository.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import net.luminis.liq.repository.RangeIterator;
+import net.luminis.liq.repository.Repository;
+import net.luminis.liq.repository.SortedRangeSet;
+import net.luminis.liq.repository.ext.BackupRepository;
+import net.luminis.liq.repository.ext.CachedRepository;
+
+import org.osgi.service.useradmin.User;
+
+/**
+ * Provides a CachedRepository, which uses either a <code>Repository</code> and a <code>BackupRepository</code>
+ * as remote and local storage, or a URL location and two files, from which it will create a <code>Repository</code>
+ * and a <code>FileBasedBackupRepository</code>. Note that this class is not thread-safe, and should be synchronized
+ * by the caller.
+ */
+public class CachedRepositoryImpl implements CachedRepository {
+ public static final long UNCOMMITTED_VERSION = -1;
+
+ private final User m_user;
+ private volatile long m_mostRecentVersion;
+
+ private final BackupRepository m_local;
+ private final Repository m_remote;
+
+ /**
+ * Creates a cached repository which uses <code>remote</code>, <code>customer</code> and
+ * <code>name</code> to create a <code>RemoteRepository</code>, and uses the <code>Files</code>s
+ * passed in as a for local storage and backup.
+ * @param user A user object, which is allowed to access <code>remote</code>.
+ * @param remote The location of the remote repository.
+ * @param customer The customer name to be used with the remote repository.
+ * @param name The name to be used with the remote repository.
+ * @param local A local file to be used for storage of changes to the repository.
+ * @param backup A local file to be used as a local backup of what was on the server.
+ * @param mostRecentVersion The version from which <code>backup</code> was checked out or committed.
+ * If no version has been committed yet, use <code>UNCOMMITTED_VERSION</code>.
+ */
+ public CachedRepositoryImpl(User user, URL remote, String customer, String name, File local, File backup, long mostRecentVersion) {
+ this(user,
+ new RemoteRepository(remote, customer, name),
+ new FilebasedBackupRepository(local, backup),
+ mostRecentVersion);
+ }
+
+ /**
+ * Creates a cached repository using.
+ * @param user A user object, which is allowed to access <code>remote</code>.
+ * @param remote A repository which holds committed versions.
+ * @param backup A backup repository for local changes.
+ * @param mostRecentVersion The version from which <code>backup</code> was checked out or committed.
+ * If no version has been committed yet, use <code>UNCOMMITTED_VERSION</code>.
+ */
+ public CachedRepositoryImpl(User user, Repository remote, BackupRepository backup, long mostRecentVersion) {
+ m_user = user;
+ m_remote = remote;
+ m_local = backup;
+ m_mostRecentVersion = mostRecentVersion;
+ }
+
+
+ public InputStream checkout(boolean fail) throws IOException, IllegalArgumentException {
+ m_mostRecentVersion = highestRemoteVersion();
+ if (m_mostRecentVersion == 0) {
+ // If there is no remote version, then simply return an empty stream.
+ if (fail) {
+ throw new IOException("No version has yet been checked in to the repository.");
+ }
+ else {
+ return new ByteArrayInputStream(new byte[0]);
+ }
+ }
+ return checkout(m_mostRecentVersion);
+ }
+
+ public InputStream checkout(long version) throws IOException, IllegalArgumentException {
+ m_local.write(m_remote.checkout(version));
+ m_local.backup();
+
+ m_mostRecentVersion = version;
+
+ return m_local.read();
+ }
+
+ public boolean commit(InputStream data, long fromVersion) throws IOException, IllegalArgumentException {
+ m_local.write(data);
+
+ return commit(fromVersion);
+ }
+
+ public boolean commit() throws IOException {
+ if (m_mostRecentVersion < 0) {
+ throw new IllegalStateException("A commit should be preceded by a checkout.");
+ }
+ return commit(m_mostRecentVersion++);
+ }
+
+ public boolean commit(long fromVersion) throws IOException, IllegalArgumentException {
+ boolean success = m_remote.commit(m_local.read(), fromVersion);
+ if (success) {
+ m_local.backup();
+ }
+
+ return success;
+ }
+
+ public SortedRangeSet getRange() throws IOException {
+ return m_remote.getRange();
+ }
+
+ public InputStream getLocal(boolean fail) throws IllegalArgumentException, IOException {
+ if ((m_mostRecentVersion <= 0) && fail) {
+ throw new IOException("No local version available of " + m_local + ", remote " + m_remote);
+ }
+ return m_local.read();
+ }
+
+ public boolean revert() throws IOException {
+ return m_local.restore();
+ }
+
+ public void writeLocal(InputStream data) throws IllegalArgumentException, IOException {
+ m_local.write(data);
+ }
+
+ public long getMostRecentVersion() {
+ return m_mostRecentVersion;
+ }
+
+ public boolean isCurrent() throws IOException {
+ return highestRemoteVersion() == m_mostRecentVersion;
+ }
+
+ private long highestRemoteVersion() throws IOException {
+ long result = 0;
+ RangeIterator ri = getRange().iterator();
+ while (ri.hasNext()) {
+ result = ri.next();
+ }
+ return result;
+ }
+
+}