ace-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ma...@apache.org
Subject svn commit: r1357570 [21/34] - in /ace/sandbox/marrs: cnf/ cnf/ext/ cnf/lib/ cnf/releaserepo/ cnf/repo/ cnf/repo/.obrcache/ cnf/repo/.obrcache/http%3A%2F%2Fbundles.bndtools.org.s3.amazonaws.com%2Fcom.jcraft.jsch/ cnf/repo/.obrcache/http%3A%2F%2Fbundles...
Date Thu, 05 Jul 2012 12:10:06 GMT
Added: ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/src/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminImpl.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/src/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminImpl.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/src/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminImpl.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/src/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminImpl.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,633 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repositoryuseradmin.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.ace.client.repositoryuseradmin.RepositoryUserAdmin;
+import org.apache.ace.repository.Repository;
+import org.apache.ace.repository.ext.CachedRepository;
+import org.apache.ace.repository.ext.impl.CachedRepositoryImpl;
+import org.apache.ace.repository.ext.impl.FilebasedBackupRepository;
+import org.apache.ace.repository.ext.impl.RemoteRepository;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.log.LogService;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.io.StreamException;
+import com.thoughtworks.xstream.io.xml.DomDriver;
+
+/**
+ * RepositoryUserAdminImpl can checkout, commit and revert a repository
+ * containing user data. It uses XStream to read and write the data.
+ */
+public class RepositoryUserAdminImpl implements RepositoryUserAdmin {
+
+    private static final String REPOSITORY_USER_ADMIN_PREFS = "repositoryUserAdminPrefs";
+    private static final String PREFS_LOCAL_FILE_ROOT = "repositoryUserAdmin";
+    private static final String PREFS_LOCAL_FILE_LOCATION = "FileLocation";
+    private static final String PREFS_LOCAL_FILE_CURRENT = "current";
+    private static final String PREFS_LOCAL_FILE_BACKUP = "backup";
+
+    private volatile BundleContext m_context;
+    private volatile LogService m_log;
+    private volatile PreferencesService m_preferences;
+
+    private final Map<String, RoleImpl> m_roles = new ConcurrentHashMap<String, RoleImpl>();
+    private CachedRepository m_repository;
+    /**
+     * Lock to be used when making changes to m_repository.
+     */
+    private final Object m_repositoryLock = new Object();
+    private Preferences m_repositoryPrefs;
+
+    public void login(User user, URL repositoryLocation, String repositoryCustomer, String repositoryName) throws IOException {
+        synchronized(m_repositoryLock) {
+            // Create our own backup repository
+            RemoteRepository remote = new RemoteRepository(repositoryLocation, repositoryCustomer, repositoryName);
+            m_repositoryPrefs = getUserPrefs(user, repositoryLocation, repositoryCustomer, repositoryName);
+            m_repository = getCachedRepositoryFromPreferences(user, remote);
+
+            // Fill the store with any data that might be available locally
+            try {
+                read(m_repository.getLocal(true));
+            }
+            catch (IOException ioe) {
+                // TODO why is this logged as an error when it occurs when there simply is no data?
+                m_log.log(LogService.LOG_ERROR, "Error retrieving local data.", ioe);
+            }
+        }
+    }
+
+    public void logout(boolean force) throws IOException {
+        // logout stores the data locally, ready for the next run
+        synchronized(m_repositoryLock) {
+            if (!force) {
+                ensureLoggedin();
+            }
+            try {
+                writeLocal();
+            }
+            catch (IOException ioe) {
+                if (!force) {
+                    throw ioe;
+                }
+            }
+            catch (RuntimeException re) {
+                if (!force) {
+                    throw re;
+                }
+            }
+            m_repository = null;
+        }
+    }
+
+    public void checkout() throws IOException {
+        synchronized(m_repositoryLock) {
+            ensureLoggedin();
+            read(m_repository.checkout(false));
+            storeVersion();
+        }
+    }
+
+    public void commit() throws IOException {
+        synchronized(m_repositoryLock) {
+            ensureLoggedin();
+            // First write to the local store, and then commit it
+            writeLocal();
+            m_repository.commit();
+            storeVersion();
+        }
+    }
+
+    /**
+     * Helper method to write out the contents of the RepositoryUserAdminImpl to
+     * a repository. This method will create a new thread to do the writing, and
+     * wait for the thread to be ready.
+     * @throws java.io.IOException Thrown when either this thread, or the thread that is
+     * started to do the writing, throws an exception.
+     */
+    private void writeLocal() throws IOException {
+        PipedInputStream in = new PipedInputStream();
+        final PipedOutputStream out = new PipedOutputStream(in);
+        final Semaphore semaphore = new Semaphore(0);
+        final Exception[] exceptions = new Exception[1];
+        new Thread("RepositoryUserAdmin writer") {
+            @Override
+            public void run() {
+                try {
+                    write(out);
+                }
+                catch (IOException e) {
+                    m_log.log(LogService.LOG_ERROR, "Error writing out contents of RepositoryAdminUser", e);
+                    exceptions[0] = e;
+                }
+                catch (IllegalArgumentException iae) {
+                    m_log.log(LogService.LOG_ERROR, "Error writing out contents of RepositoryAdminUser", iae);
+                    exceptions[0] = iae;
+                }
+                semaphore.release();
+            }
+        }.start();
+        m_repository.writeLocal(in);
+        try {
+            if (!semaphore.tryAcquire(30, TimeUnit.SECONDS)) {
+                throw new IOException("Error writing the contents of RepositoryUserAdmin.");
+            }
+        }
+        catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+        if (exceptions[0] != null) {
+            if (exceptions[0] instanceof IOException) {
+                throw (IOException) exceptions[0];
+            }
+            if (exceptions[0] instanceof RuntimeException) {
+                throw (RuntimeException) exceptions[0];
+            }
+        }
+    }
+
+    public void revert() throws IOException {
+        synchronized(m_repositoryLock) {
+            ensureLoggedin();
+            m_repository.revert();
+            read(m_repository.getLocal(false));
+        }
+    }
+
+    /**
+     * Makes sure a user is logged in before 'stuff' can be done. Make sure the
+     * calling thread is holding the m_repositoryLock.
+     */
+    private void ensureLoggedin() {
+        if (m_repository == null) {
+            throw new IllegalStateException("This operation requires a user to be logged in.");
+        }
+    }
+
+    /**
+     * Reads the content of the stream, and updates this service's
+     * contents accordingly. The caller of this method should hold the
+     * m_repositoryLock.
+     */
+    @SuppressWarnings("unchecked")
+    private void read(InputStream input) {
+        m_roles.clear();
+        // We use DomDriver because the standard XPP driver has issues with attributes.
+        XStream xstream = new XStream(/*new DomDriver()*/);
+        xstream.registerConverter(ROLEMAPCONVERTER);
+        xstream.registerConverter(ROLECONVERTER);
+        xstream.registerConverter(DICTCONVERTER);
+        xstream.aliasType("roles", Map.class);
+        try {
+            Map<String, RoleImpl> fromXML = (Map<String, RoleImpl>) xstream.fromXML(input);
+            m_roles.putAll(fromXML);
+        }
+        catch (StreamException e) {
+            // no problem: this means that the remote repository is empty.
+        }
+    }
+
+    /**
+     * Writes the current contents of this service.
+     * The caller of this method should hold the m_repositoryLock.
+     * @param out An output stream to write to. It will be closed by the this method.
+     * @throws java.io.IOException When there is a problem creating the stream, or
+     * the other end of the stream fails.
+     */
+    private void write(OutputStream out) throws IOException {
+        XStream xstream = new XStream(new DomDriver());
+        xstream.registerConverter(ROLEMAPCONVERTER);
+        xstream.registerConverter(ROLECONVERTER);
+        xstream.registerConverter(DICTCONVERTER);
+        xstream.aliasType("roles", Map.class);
+        xstream.toXML(m_roles, out);
+        try {
+            out.close();
+        }
+        catch (IOException e) {
+            m_log.log(LogService.LOG_ERROR, "Error closing XStream output stream.", e);
+            throw e;
+        }
+    }
+
+    /**
+     * Gets the preferences for a user/location/customer/name combination.
+     */
+    private Preferences getUserPrefs(User user, URL location, String customer, String name) {
+        Preferences userPrefs = m_preferences.getUserPreferences(user.getName());
+        Preferences userAdminPrefs = userPrefs.node(REPOSITORY_USER_ADMIN_PREFS);
+        Preferences repoPref = userAdminPrefs.node(location.getAuthority() + location.getPath());
+        Preferences customerPref = repoPref.node(customer);
+        return customerPref.node(name);
+    }
+
+    /**
+     * Creates a cached repository based on preferences.
+     */
+    private CachedRepository getCachedRepositoryFromPreferences(User user, Repository repository) throws IOException {
+        long mostRecentVersion = m_repositoryPrefs.getLong("version", CachedRepositoryImpl.UNCOMMITTED_VERSION);
+        File current = getFileFromPreferences(PREFS_LOCAL_FILE_CURRENT);
+        File backup = getFileFromPreferences(PREFS_LOCAL_FILE_BACKUP);
+        return new CachedRepositoryImpl(user, repository, new FilebasedBackupRepository(current, backup), mostRecentVersion);
+    }
+
+    /**
+     * Writes the current version of the repository we are working on to the preferences.
+     */
+    private void storeVersion() {
+        m_repositoryPrefs.putLong("version", m_repository.getMostRecentVersion());
+    }
+
+    /**
+     * Gets a named file in preferences. If the file does not yet exist, it will
+     * be created, and its location noted in the preferences.
+     */
+    private File getFileFromPreferences(String type) throws IOException {
+        String directory = m_repositoryPrefs.get(PREFS_LOCAL_FILE_LOCATION, "");
+
+        if ((directory == "") || !m_context.getDataFile(PREFS_LOCAL_FILE_ROOT + "/" + directory).isDirectory()) {
+            if (!m_context.getDataFile(PREFS_LOCAL_FILE_ROOT + "/" + directory).isDirectory() && (directory != "")) {
+                m_log.log(LogService.LOG_WARNING, "Directory '" + directory + "' should exist according to the preferences, but it does not.");
+            }
+            // The file did not exist, so create a new one.
+            File directoryFile = null;
+            File bundleDataDir = m_context.getDataFile(PREFS_LOCAL_FILE_ROOT);
+            if (!bundleDataDir.isDirectory()) {
+                if (!bundleDataDir.mkdir()) {
+                    throw new IOException("Error creating the local repository root directory.");
+                }
+            }
+            directoryFile = File.createTempFile("repo", "", bundleDataDir);
+
+            directoryFile.delete(); // No problem if this goes wrong, it just means it wasn't there yet.
+            if (!directoryFile.mkdir()) {
+                throw new IOException("Error creating the local repository storage directory.");
+            }
+            m_repositoryPrefs.put(PREFS_LOCAL_FILE_LOCATION, directoryFile.getName());
+            return new File(directoryFile, type);
+        }
+        else {
+            // Get the given file from that location.
+            return m_context.getDataFile(PREFS_LOCAL_FILE_ROOT + "/" + directory + "/" + type);
+        }
+    }
+
+    /* ******************************
+     * The UserAdmin implementation *
+     * ******************************/
+
+    public Role createRole(String name, int type) {
+        if ((type != Role.USER) && (type != Role.GROUP)) {
+            throw new IllegalArgumentException("Type " + type + " is unknown.");
+        }
+
+        // event tough we have a ConcurrentHashMap, we still should make the checking for existence
+        // and actual creation an atomic operation.
+        synchronized (m_roles) {
+            if (m_roles.containsKey(name)) {
+                return null;
+            }
+
+            RoleImpl result = new RoleImpl(name, type);
+            m_roles.put(name, result);
+            return result;
+        }
+    }
+
+    public Authorization getAuthorization(User user) {
+        throw new UnsupportedOperationException("getAuthorization is not supported by RepositoryUserAdmin.");
+    }
+
+    public Role getRole(String name) {
+        return m_roles.get(name);
+    }
+
+    public Role[] getRoles(String filter) throws InvalidSyntaxException {
+        if (filter == null) {
+            return m_roles.values().toArray(new Role[m_roles.size()]);
+        }
+
+        Filter f = m_context.createFilter(filter);
+
+        List<Role> result = new ArrayList<Role>();
+        for (RoleImpl impl : m_roles.values()) {
+            if (f.match(impl.getProperties())) {
+                result.add(impl);
+            }
+        }
+
+        // The spec requires us to return null when we have no results.
+        return result.size() > 0 ? result.toArray(new Role[result.size()]) : null;
+    }
+
+    public User getUser(String key, String value) {
+        List<User> result = new ArrayList<User>();
+        for (Role role : m_roles.values()) {
+            if ((role.getType() == Role.USER) && value.equals(role.getProperties().get(key))) {
+                result.add((User) role);
+            }
+        }
+
+        return result.size() == 1 ? result.get(0) : null;
+    }
+
+    public boolean removeRole(String name) {
+        RoleImpl role = m_roles.remove(name);
+        if (role == null) {
+            return false;
+        }
+        for (String groupName : role.getMemberships(this)) {
+            RoleImpl group = m_roles.get(groupName);
+            if (group != null) {
+                group.removeMember(role);
+            }
+        }
+        return true;
+    }
+
+    /* ***********************
+     * Serialization helpers *
+     * ***********************/
+
+    /**
+     * XStream Converter for a Dictionary, with support for both Strings and
+     * byte[]'s as values. Resulting format:
+     * <pre>
+     * &lt;keyname1 type = "String"&gt;value1&lt;/keyname1&gt;
+     * &lt;keyname1 type = "byte[]"&gt;value1&lt;/keyname1&gt;
+     * </pre>
+     */
+    @SuppressWarnings("unchecked")
+    private static final Converter DICTCONVERTER = new Converter() {
+        public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) {
+            Dictionary dict = (Dictionary) object;
+            Enumeration e = dict.keys();
+            while (e.hasMoreElements()) {
+                String key = (String) e.nextElement();
+                Object value = dict.get(key);
+                writer.startNode(key);
+                if (value instanceof String) {
+                    writer.addAttribute("type", "String");
+                    writer.setValue((String) value);
+                }
+                else if (value instanceof byte[]) {
+                    writer.addAttribute("type", "byte[]");
+                    writer.setValue(new String((byte[]) value));
+                }
+                else if (value == null) {
+                    throw new IllegalArgumentException("Encountered a null value in the dictionary for key " + key);
+                }
+                else {
+                    throw new IllegalArgumentException("The dictionary contains a non-recognized value " + value.getClass().getName() + " for key " + key);
+                }
+                writer.endNode();
+            }
+        }
+
+        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext converter) {
+            Dictionary result = new Hashtable<String, Object>();
+            while (reader.hasMoreChildren()) {
+                reader.moveDown();
+                Object value;
+                if ((reader.getAttribute("type") == null) || reader.getAttribute("type").equals("String")) {
+                    value = reader.getValue();
+                }
+                else if (reader.getAttribute("type").equals("byte[]")) {
+                    value = reader.getValue().getBytes();
+                }
+                else {
+                    throw new IllegalArgumentException("Encountered an unknown type tag: " + reader.getAttribute("type"));
+                }
+                result.put(reader.getNodeName(), value);
+                reader.moveUp();
+            }
+            return result;
+        }
+
+        public boolean canConvert(Class clazz) {
+            return Dictionary.class.isAssignableFrom(clazz);
+        }
+    };
+
+    /**
+     * XStream convertor for RoleImpl objects. Resulting format:
+     * <pre>
+     * &lt;user name="me"&gt;
+     *     &lt;properties&gt;
+     *     ...up to DICTCONVERTER...
+     *     &lt;/properties&gt;
+     *     &lt;credentials&gt;
+     *     ...up to DICTCONVERTER...
+     *     &lt;/credentials&gt;
+     *     &lt;memberof&gt;group1&lt;/memberof&gt;
+     *     &lt;memberof&gt;group2&lt;/memberof&gt;
+     * &lt;/user>
+     * </pre>
+     * This converter will use the context property 'deserialized' to find
+     * groups that the currently deserialized entry should be a member of.
+     */
+    @SuppressWarnings("unchecked")
+    private final Converter ROLECONVERTER = new Converter() {
+        public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) {
+            RoleImpl role = (RoleImpl) object;
+
+            if (role.getType() == Role.USER) {
+                writer.startNode("user");
+            }
+            else {
+                writer.startNode("group");
+            }
+            writer.addAttribute("name", role.getName());
+
+            writer.startNode("properties");
+            context.convertAnother(role.getProperties());
+            writer.endNode();
+
+            writer.startNode("credentials");
+            context.convertAnother(role.getCredentials());
+            writer.endNode();
+
+            for (String s : role.getMemberships(RepositoryUserAdminImpl.this)) {
+                writer.startNode("memberof");
+                writer.setValue(s);
+                writer.endNode();
+            }
+
+            writer.endNode();
+        }
+
+        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+            int type;
+            if (reader.getNodeName().equals("user")) {
+                type = Role.USER;
+            }
+            else if (reader.getNodeName().equals("group")) {
+                type = Role.GROUP;
+            }
+            else {
+                throw new IllegalArgumentException("Encountered an unknown node name: " + reader.getNodeName());
+            }
+
+            RoleImpl result = new RoleImpl(reader.getAttribute("name"), type);
+
+            while (reader.hasMoreChildren()) {
+                reader.moveDown();
+                if (reader.getNodeName().equals("properties")) {
+                    copyDict(result.getProperties(), (Dictionary<String, Object>) context.convertAnother(reader, Dictionary.class));
+                }
+                else if (reader.getNodeName().equals("credentials")) {
+                    copyDict(result.getCredentials(), (Dictionary<String, Object>) context.convertAnother(reader, Dictionary.class));
+                }
+                else if (reader.getNodeName().equals("memberof")) {
+                    ((Map<String, RoleImpl>) context.get("deserialized")).get(reader.getValue()).addMember(result);
+                }
+                reader.moveUp();
+            }
+
+            return result;
+        }
+
+        /**
+         * Helper method that copies the contents of one dictionary to another.
+         */
+        private void copyDict(Dictionary to, Dictionary from) {
+            Enumeration<String> e = from.keys();
+            while (e.hasMoreElements()) {
+                String key = e.nextElement();
+                to.put(key, from.get(key));
+            }
+        }
+
+        public boolean canConvert(Class clazz) {
+            return RoleImpl.class.isAssignableFrom(clazz);
+        }
+    };
+
+    /**
+     * XStream converter for a Map which contains Roles. Resulting format:
+     * <pre>
+     * &lt;roles&gt;
+     *     ...up to ROLECONVERTER...
+     *     ...up to ROLECONVERTER...
+     * &lt;/roles&gt;
+     * </pre>
+     * This converter will use the 'deserialized' context property to store the map
+     * of already deserialized roles, so ROLECONVERTER can use that.<br>
+     * Furthermore, it uses a simple form of cycle detection when serializing.
+     */
+    private final Converter ROLEMAPCONVERTER = new Converter() {
+
+        @SuppressWarnings("unchecked")
+        public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) {
+            Map<String, RoleImpl> todo = new HashMap<String, RoleImpl>();
+            todo.putAll(((Map) object));
+
+            /*
+             * We only serialize roles that have no dependencies on roles that have not yet been
+             * serialized. To do so, we check all dependencies of a role, and see whether any of these
+             * still has to be serialized. If so, we skip that role for now, and try to serialize it
+             * in a later run. We go over the list a number of times, until it stops shrinking.
+             */
+            int removed = 1;
+            while (removed != 0) {
+                // We need to store the elements we have handled separately: we cannot remove them from todo directly.
+                Set<String> done = new HashSet<String>();
+                for (RoleImpl role : todo.values()) {
+                    String[] memberships = role.getMemberships(RepositoryUserAdminImpl.this);
+                    if (!contains(memberships, todo.keySet())) {
+                        context.convertAnother(role);
+                        done.add(role.getName());
+                    }
+                }
+                for (String s : done) {
+                    todo.remove(s);
+                }
+                removed = done.size();
+            }
+            if (!todo.isEmpty()) {
+                // removed has to be 0, so no elements have been removed from todo in the previous run. However,
+                // if todo now is not empty, we know we have a circular dependency.
+                throw new IllegalArgumentException("The role tree contains a circular dependency, and cannot be serialized.");
+            }
+        }
+
+        /**
+         * @return <code>false</code> if none of the elements from subset appear in
+         * set, <code>true</code> otherwise.
+         */
+        private boolean contains(String[] subset, Set<String> set) {
+            for (String s : subset) {
+                if (set.contains(s)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+            Map<String, RoleImpl> result = new HashMap<String, RoleImpl>();
+            context.put("deserialized", result);
+            while (reader.hasMoreChildren()) {
+                reader.moveDown();
+                RoleImpl role = (RoleImpl) context.convertAnother(reader, RoleImpl.class);
+                result.put(role.getName(), role);
+                reader.moveUp();
+            }
+            return result;
+        }
+
+        @SuppressWarnings("unchecked")
+        public boolean canConvert(Class clazz) {
+            return Map.class.isAssignableFrom(clazz);
+        }
+    };
+
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/src/org/apache/ace/client/repositoryuseradmin/impl/RoleImpl.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/src/org/apache/ace/client/repositoryuseradmin/impl/RoleImpl.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/src/org/apache/ace/client/repositoryuseradmin/impl/RoleImpl.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/src/org/apache/ace/client/repositoryuseradmin/impl/RoleImpl.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.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 org.apache.ace.client.repositoryuseradmin.impl.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()]);
+    }
+
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/test/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminSerializationTest.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/test/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminSerializationTest.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/test/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminSerializationTest.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/test/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminSerializationTest.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repositoryuseradmin.impl;
+
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.ace.range.SortedRangeSet;
+import org.apache.ace.repository.ext.CachedRepository;
+import org.apache.ace.test.utils.TestUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.log.LogService;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class RepositoryUserAdminSerializationTest {
+
+    private RepositoryUserAdminImpl m_impl;
+
+    private MockCachedRepository m_cachedRepository;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() {
+        m_impl = new RepositoryUserAdminImpl();
+        TestUtils.configureObject(m_impl, BundleContext.class, TestUtils.createMockObjectAdapter(BundleContext.class, new Object() {
+            @SuppressWarnings("unused")
+            public Filter createFilter(String s) throws InvalidSyntaxException {
+                return FrameworkUtil.createFilter(s);
+            }
+        }));
+
+        // We configure the CachedRepository ourselves, so there is no need to login
+        m_cachedRepository = new MockCachedRepository();
+        TestUtils.configureObject(m_impl, CachedRepository.class, m_cachedRepository);
+        TestUtils.configureObject(m_impl, PreferencesService.class);
+        TestUtils.configureObject(m_impl, LogService.class);
+        TestUtils.configureObject(m_impl, Preferences.class); // A Preferences is cached for storing the version.
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test(groups = { UNIT })
+    public void testRepositoryUserAdminSerialization() throws Exception {
+        // Create some data
+        User user = (User) m_impl.createRole("me", Role.USER);
+        user.getProperties().put("fullname", "Mr. M. Me");
+        user.getCredentials().put("password", "swordfish");
+        user.getCredentials().put("certificate", new byte[] {'4', '2'});
+        Group group = (Group) m_impl.createRole("myGroup", Role.GROUP);
+        group.getProperties().put("description", "One group to rule them all.");
+        group.addMember(user);
+
+        // Write it to the store
+        new Thread("RepositoryUserAdmin committer") {
+            @Override
+            public void run() {
+                try {
+                    m_impl.commit();
+                }
+                catch (Exception e) {
+                    System.err.println("Error writing data");
+                    e.printStackTrace(System.err);
+                }
+            }
+        }.start();
+
+        // wait for impl to be ready, and retrieve what he has written
+        Object[] request  = m_cachedRepository.getRequest(true);
+        assert request[0].equals("writeLocal");
+        InputStream input = (InputStream) request[1];
+        request = m_cachedRepository.getRequest(true);
+        assert request[0].equals("commit");
+
+        String data1 = getInputStreamAsString(input);
+
+        // alter the contents
+        m_impl.createRole("otherme", Role.USER);
+        m_impl.removeRole("myGroup");
+
+        m_cachedRepository.addResponse(new ByteArrayInputStream(data1.getBytes()));
+
+        final Semaphore sem = new Semaphore(0);
+
+        // make impl read what it has just written
+        new Thread("RepositoryUserAdmin committer") {
+            @Override
+            public void run() {
+                try {
+                    m_impl.checkout();
+                }
+                catch (Exception e) {
+                    System.err.println("Error reading data");
+                    e.printStackTrace(System.err);
+                }
+                sem.release();
+            }
+        }.start();
+
+        // wait for the reading to be done
+        sem.tryAcquire(5, TimeUnit.SECONDS);
+
+        request = m_cachedRepository.getRequest(true);
+        assert request[0].equals("checkout");
+
+        // inspect the current contents of impl
+        Role[] roles = m_impl.getRoles(null);
+        assert roles.length == 2 : "Found " + roles.length + " roles in stead of 2.";
+        for (Role role : roles) {
+            if (role.equals(user)) {
+                assert user.hasCredential("password", "swordfish");
+                assert user.hasCredential("certificate", new byte[] {'4', '2'});
+            }
+            else if (role.equals(group)) {
+                assert ((Group) role).getMembers().length == 1 : "We expect one member in the group in stream of " + ((Group) role).getMembers().length;
+                assert ((Group) role).getMembers()[0].equals(user);
+            }
+            else {
+                assert false : "Found an unknown role: " + role.toString() + " (" + role.getName() + ")";
+            }
+        }
+    }
+
+    @Test(groups = { UNIT })
+    public void testCircularDependency() throws Exception {
+        Group g1 = (Group) m_impl.createRole("group1", Role.GROUP);
+        Group g2 = (Group) m_impl.createRole("group2", Role.GROUP);
+        g1.addMember(g2);
+        g2.addMember(g1);
+
+        try {
+            m_impl.commit();
+            assert false : "There is a circular dependency, this should be detected and reason for failure.";
+        }
+        catch (IllegalArgumentException iae) {
+            // expected
+        }
+    }
+
+    /**
+     * A mock cached repository, used for checking calls and staging responses to impl.
+     */
+    private static class MockCachedRepository extends MockResponder implements CachedRepository {
+
+        public InputStream checkout(boolean fail) throws IOException {
+            handleRequest("checkout", fail);
+            return (InputStream) handleResponse();
+        }
+
+        public boolean commit() throws IOException {
+            handleRequest("commit");
+            return false;
+        }
+
+        public InputStream getLocal(boolean fail) throws IOException {
+            return null;
+        }
+
+        public long getMostRecentVersion() {
+            return 0;
+        }
+
+        public boolean isCurrent() throws IOException {
+            return false;
+        }
+
+        public boolean revert() throws IOException {
+            return false;
+        }
+
+        public void writeLocal(InputStream data) throws IOException {
+            handleRequest("writeLocal", data);
+        }
+
+        public InputStream checkout(long version) throws IOException, IllegalArgumentException {
+            return null;
+        }
+
+        public boolean commit(InputStream data, long fromVersion) throws IOException, IllegalArgumentException {
+            return false;
+        }
+
+        public SortedRangeSet getRange() throws IOException {
+            return null;
+        }
+
+    }
+
+    /**
+     * Base class responder, used for inspecting calls and staging responses.
+     */
+    private static class MockResponder {
+        protected BlockingQueue<Object> m_responses = new LinkedBlockingQueue<Object>();
+        protected BlockingQueue<Object[]> m_requests = new LinkedBlockingQueue<Object[]>();
+
+        public void addResponse(Object response) {
+            m_responses.add(response);
+        }
+
+        public Object[] getRequest(boolean wait) {
+            if (wait) {
+                Object[] result = null;
+                try {
+                    result = m_requests.poll(5, TimeUnit.SECONDS);
+                }
+                catch (Exception e) {
+                    System.err.println("Interrupted while waiting for blocked queue.");
+                    Thread.currentThread().interrupt();
+                }
+                if (result == null) {
+                    assert false : "Even after 5 seconds, no request was ready for us.";
+                }
+                return result;
+            }
+            else {
+                return m_requests.poll();
+            }
+        }
+
+        public boolean moreRequests() {
+            return !m_requests.isEmpty();
+        }
+
+        protected void handleRequest(Object... objs) {
+            m_requests.add(objs);
+        }
+
+        protected Object handleResponse() {
+            return m_responses.poll();
+        }
+    }
+
+    /**
+     * Helper method that gets the contents of a stream into a single string.
+     */
+    private static String getInputStreamAsString(InputStream in) throws IOException {
+        char[] buf = new char[1];
+        StringBuilder found = new StringBuilder();
+        InputStreamReader bf = new InputStreamReader(in);
+        while (bf.read(buf) > 0) {
+            found.append(buf);
+        }
+        bf.close();
+        return found.toString();
+    }
+
+}

Added: ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/test/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminTest.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/test/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminTest.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/test/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminTest.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.repository.useradmin/test/org/apache/ace/client/repositoryuseradmin/impl/RepositoryUserAdminTest.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repositoryuseradmin.impl;
+
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+
+import org.apache.ace.test.utils.TestUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class RepositoryUserAdminTest {
+
+    private RepositoryUserAdminImpl m_impl;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() {
+        m_impl = new RepositoryUserAdminImpl();
+        TestUtils.configureObject(m_impl, BundleContext.class, TestUtils.createMockObjectAdapter(BundleContext.class, new Object() {
+            @SuppressWarnings("unused")
+            public Filter createFilter(String s) throws InvalidSyntaxException {
+                return FrameworkUtil.createFilter(s);
+            }
+        }));
+    }
+
+    /**
+     * Tests basic creation and membership of groups.
+     */
+    @Test(groups = { UNIT })
+    public void testCreation() {
+        User user = (User) m_impl.createRole("me", Role.USER);
+        Group group = (Group) m_impl.createRole("myGroup", Role.GROUP);
+        group.addMember(user);
+        assert group.getMembers().length == 1 : "We expect to find one member, not " + group.getMembers().length;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test(groups = { UNIT })
+    public void testUserProperties() {
+        User user = (User) m_impl.createRole("me", Role.USER);
+        user.getProperties().put("fullname", "Mr. M. Me");
+        assert m_impl.getUser("fullname", "Mr. M. Me").equals(user);
+
+        Group group = (Group) m_impl.createRole("theGroup", Role.GROUP);
+        assert m_impl.getUser("fullname", "Mr. M. Me").equals(user); // We should not find the group we just created
+
+        m_impl.removeRole("me");
+        assert m_impl.getUser("fullname", "Mr. M. Me") == null; // We should not find the group we just created
+    }
+
+    @Test(groups = { UNIT })
+    public void testGetRoles() throws InvalidSyntaxException {
+        User user = (User) m_impl.createRole("me", Role.USER);
+        user.getProperties().put("fullname", "Mr. M. Me");
+        Group group = (Group) m_impl.createRole("myGroup", Role.GROUP);
+        Role[] roles = m_impl.getRoles(null);
+        assert roles.length == 2;
+        roles = m_impl.getRoles("(fullname=Mr. M. Me)");
+        assert roles.length == 1;
+        roles = m_impl.getRoles("(fullname=Mr. U. Me)");
+        assert roles == null; // Spec requires us to return null in stead of an empty array
+    }
+
+    @Test(groups = { UNIT })
+    public void testCreateDoubleRole() throws InvalidSyntaxException {
+        User user = (User) m_impl.createRole("test", Role.USER);
+        Group group = (Group) m_impl.createRole("test", Role.GROUP);
+        assert group == null;
+        assert m_impl.getRole("test").equals(user);
+        assert m_impl.getRoles(null).length == 1;
+    }
+
+    @Test(groups = { UNIT })
+    public void testCredentials() throws InvalidSyntaxException {
+        User user = (User) m_impl.createRole("me", Role.USER);
+        user.getCredentials().put("password", "swordfish");
+        assert user.hasCredential("password", "swordfish");
+        assert !user.hasCredential("pet", "swordfish");
+        assert !user.hasCredential("password", "barracuda");
+    }
+
+    @Test(groups = { UNIT })
+    public void testStringOnlyDictionary() throws InvalidSyntaxException {
+        User user = (User) m_impl.createRole("me", Role.USER);
+        try {
+            user.getProperties().put("clearanceLevel", new Integer(5));
+            assert false : "Only String or byte[] values should be allowed.";
+        }
+        catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            user.getProperties().put("clearanceLevel", '5');
+            assert false : "Only String or byte[] values should be allowed.";
+        }
+        catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            user.getProperties().put("clearanceLevel", "5");
+        }
+        catch (IllegalArgumentException iae) {
+            assert false : "String values should be allowed.";
+        }
+
+        try {
+            user.getProperties().put("clearanceLevel", new byte[] {'5'});
+        }
+        catch (IllegalArgumentException iae) {
+            assert false : "byte[] values should be allowed.";
+        }
+
+        try {
+            user.getProperties().put(new String[] {"clearanceLevel"}, "5");
+            assert false : "String[] keys should not be allowed.";
+        }
+        catch (IllegalArgumentException iae) {
+            // expected
+        }
+
+        try {
+            user.getProperties().put(new byte[] {'c','l','e','a','r','a','n','c','e','L','e','v','e','l'}, "5");
+            assert false : "byte[] keys should not be allowed.";
+        }
+        catch (IllegalArgumentException iae) {
+            // expected
+        }
+    }
+}

Added: ace/sandbox/marrs/org.apache.ace.client.rest/.classpath
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/.classpath?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/.classpath (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/.classpath Thu Jul  5 12:09:30 2012
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" output="bin_test" path="test"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
+	<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>

Added: ace/sandbox/marrs/org.apache.ace.client.rest/.project
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/.project?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/.project (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/.project Thu Jul  5 12:09:30 2012
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.apache.ace.client.rest</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>bndtools.core.bndbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>bndtools.core.bndnature</nature>
+	</natures>
+</projectDescription>

Added: ace/sandbox/marrs/org.apache.ace.client.rest/bnd.bnd
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/bnd.bnd?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/bnd.bnd (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/bnd.bnd Thu Jul  5 12:09:30 2012
@@ -0,0 +1,19 @@
+-buildpath: osgi.core,\
+	osgi.cmpn,\
+	org.apache.felix.dependencymanager,\
+	org.apache.ace.connectionfactory;version=latest,\
+	org.apache.ace.authentication.api;version=latest,\
+	org.apache.ace.client.repository.api;version=latest,\
+	org.apache.ace.repository.api;version=latest,\
+	org.apache.ace.range.api;version=latest,\
+	org.apache.ace.util;version=latest,\
+	javax.servlet,\
+	../cnf/lib/gson-1.7.1.jar;version=file,\
+	org.mockito.mockito-all
+
+Private-Package: org.apache.ace.client.rest,\
+	com.google.gson,\
+	com.google.gson.annotations,\
+	com.google.gson.internal,\
+	com.google.gson.reflect,\
+	com.google.gson.stream
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.client.rest/build.xml
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/build.xml?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/build.xml (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/build.xml Thu Jul  5 12:09:30 2012
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="project" default="build"> 
+	<import file="../cnf/build.xml"/>
+</project>

Added: ace/sandbox/marrs/org.apache.ace.client.rest/pom.xml
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/pom.xml?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/pom.xml (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/pom.xml Thu Jul  5 12:09:30 2012
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        Licensed to the Apache Software Foundation (ASF) under one or more
+        contributor license agreements.  See the NOTICE file distributed with
+        this work for additional information regarding copyright ownership.
+        The ASF licenses this file to You under the Apache License, Version 2.0
+        (the "License"); you may not use this file except in compliance with
+        the License.  You may obtain a copy of the License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.ace</groupId>
+        <artifactId>ace-pom</artifactId>
+        <version>0.8.1-SNAPSHOT</version>
+        <relativePath>../pom/pom.xml</relativePath>
+    </parent>
+
+    <version>0.8.1-SNAPSHOT</version>
+    <artifactId>org.apache.ace.client.rest</artifactId>
+    <packaging>bundle</packaging>
+
+    <name>Apache ACE :: Client :: REST API</name>
+    <description />
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/ace/trunk/ace-client-rest</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/ace/trunk/ace-client-rest</developerConnection>
+        <url>http://svn.apache.org/repos/asf/ace/trunk/ace-client-rest</url>
+    </scm>
+
+    <properties>
+        <import.package>
+	        *
+        </import.package>
+        <private.package>
+            org.apache.ace.client.rest,
+            com.google.gson,
+            com.google.gson.*
+        </private.package>
+        <bundle.activator>org.apache.ace.client.rest.Activator</bundle.activator>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.authentication.api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.connectionfactory</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.client.repository.api</artifactId>
+        </dependency>
+         <dependency>
+            <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.repository.api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.repository.ext</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.server.log.store</artifactId>
+        </dependency>
+         <dependency>
+            <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.range.api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ace</groupId>
+            <artifactId>org.apache.ace.client.repository.helper.bundle</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.dependencymanager</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>1.7.1</version>
+        </dependency>
+    </dependencies>
+</project>

Added: ace/sandbox/marrs/org.apache.ace.client.rest/src/org/apache/ace/client/rest/Activator.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/src/org/apache/ace/client/rest/Activator.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/src/org/apache/ace/client/rest/Activator.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/src/org/apache/ace/client/rest/Activator.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.rest;
+
+import javax.servlet.Servlet;
+
+import org.apache.ace.client.repository.SessionFactory;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+    public static final String RESTCLIENT_PID = "org.apache.ace.client.rest";
+    
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws Exception {
+        manager.add(createComponent()
+            .setInterface(Servlet.class.getName(), null)
+            .setImplementation(RESTClientServlet.class)
+            .add(createServiceDependency()
+                .setService(SessionFactory.class)
+                .setRequired(true)
+            )
+            .add(createConfigurationDependency()
+                .setPropagate(true)
+                .setPid(RESTCLIENT_PID)
+            )
+            .add(createServiceDependency()
+                .setService(LogService.class)
+                .setRequired(false)
+            )
+        );
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+    }
+}

Added: ace/sandbox/marrs/org.apache.ace.client.rest/src/org/apache/ace/client/rest/LogEventSerializer.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.rest/src/org/apache/ace/client/rest/LogEventSerializer.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.rest/src/org/apache/ace/client/rest/LogEventSerializer.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.rest/src/org/apache/ace/client/rest/LogEventSerializer.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.rest;
+
+import java.lang.reflect.Type;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.apache.ace.log.AuditEvent;
+import org.apache.ace.log.LogEvent;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+
+public class LogEventSerializer implements JsonSerializer<LogEvent> {
+
+	public JsonElement serialize(LogEvent e, Type typeOfSrc, JsonSerializationContext context) {
+        DateFormat format = SimpleDateFormat.getDateTimeInstance();
+        JsonObject event = new JsonObject();
+        event.addProperty("logId", e.getLogID());
+        event.addProperty("id", e.getID());
+        event.addProperty("time", format.format(new Date(e.getTime())));
+        event.addProperty("type", toAuditEventType(e.getType()));
+        JsonObject eventProperties = new JsonObject();
+        Dictionary p = e.getProperties();
+        Enumeration keyEnumeration = p.keys();
+        while (keyEnumeration.hasMoreElements()) {
+            Object key = keyEnumeration.nextElement();
+            eventProperties.addProperty(key.toString(), p.get(key).toString());
+        }
+        event.add("properties", eventProperties);
+        return event;
+    }
+
+    private String toAuditEventType(int type) {
+        switch (type) {
+            case AuditEvent.BUNDLE_INSTALLED: return "bundle installed";
+            case AuditEvent.BUNDLE_RESOLVED: return "bundle resolved";
+            case AuditEvent.BUNDLE_STARTED: return "bundle started";
+            case AuditEvent.BUNDLE_STOPPED: return "bundle stopped";
+            case AuditEvent.BUNDLE_UNRESOLVED: return "bundle unresolved";
+            case AuditEvent.BUNDLE_UPDATED: return "bundle updated";
+            case AuditEvent.BUNDLE_UNINSTALLED: return "bundle uninstalled";
+            case AuditEvent.BUNDLE_STARTING: return "bundle starting";
+            case AuditEvent.BUNDLE_STOPPING: return "bundle stopping";
+            case AuditEvent.FRAMEWORK_INFO: return "framework info";
+            case AuditEvent.FRAMEWORK_WARNING: return "framework warning";
+            case AuditEvent.FRAMEWORK_ERROR: return "framework error";
+            case AuditEvent.FRAMEWORK_REFRESH: return "framework refresh";
+            case AuditEvent.FRAMEWORK_STARTED: return "framework started";
+            case AuditEvent.FRAMEWORK_STARTLEVEL: return "framework startlevel";
+            case AuditEvent.DEPLOYMENTADMIN_INSTALL: return "deployment admin install";
+            case AuditEvent.DEPLOYMENTADMIN_UNINSTALL: return "deployment admin uninstall";
+            case AuditEvent.DEPLOYMENTADMIN_COMPLETE: return "deployment admin complete";
+            case AuditEvent.DEPLOYMENTCONTROL_INSTALL: return "deployment control install";
+            default: return Integer.toString(type);
+        }
+    }
+}



Mime
View raw message