Return-Path: Delivered-To: apmail-incubator-ace-commits-archive@minotaur.apache.org Received: (qmail 40099 invoked from network); 27 Jun 2009 16:14:31 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 27 Jun 2009 16:14:31 -0000 Received: (qmail 95391 invoked by uid 500); 27 Jun 2009 16:14:42 -0000 Delivered-To: apmail-incubator-ace-commits-archive@incubator.apache.org Received: (qmail 95380 invoked by uid 500); 27 Jun 2009 16:14:42 -0000 Mailing-List: contact ace-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: ace-dev@incubator.apache.org Delivered-To: mailing list ace-commits@incubator.apache.org Delivered-To: moderator for ace-commits@incubator.apache.org Received: (qmail 83789 invoked by uid 99); 27 Jun 2009 15:54:41 -0000 X-ASF-Spam-Status: No, hits=-1998.0 required=10.0 tests=ALL_TRUSTED,FB_GET_MEDS,URIBL_RHS_DOB X-Spam-Check-By: apache.org Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r788992 [9/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/ne... Date: Sat, 27 Jun 2009 15:53:26 -0000 To: ace-commits@incubator.apache.org From: marrs@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090627155343.91C4023889B6@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/bundle/BundleHelper.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/bundle/BundleHelper.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/bundle/BundleHelper.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/bundle/BundleHelper.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,37 @@ +package net.luminis.liq.client.repository.helper.bundle; + +import net.luminis.liq.client.repository.helper.ArtifactHelper; +import net.luminis.liq.client.repository.object.ArtifactObject; + +import org.osgi.framework.Constants; + +/** + * Definitions for a BundleHelper, which are used to treat an artifact as a bundle. + */ +public interface BundleHelper extends ArtifactHelper { + public static final String KEY_SYMBOLICNAME = Constants.BUNDLE_SYMBOLICNAME; + public static final String KEY_NAME = Constants.BUNDLE_NAME; + public static final String KEY_VERSION = Constants.BUNDLE_VERSION; + public static final String KEY_VENDOR = Constants.BUNDLE_VENDOR; + public static final String KEY_RESOURCE_PROCESSOR_PID = "Deployment-ProvidesResourceProcessor"; + + public static final String MIMETYPE = "application/vnd.osgi.bundle"; + + /** + * Used to include an OSGi version range (see section 3.2.5 of the core specification) with an association. + * When included in the association's properties, this statement will cause the association to automatically + * match the highest available bundle version that matches the statement; an open ended range can be + * created by passing "0.0.0".
+ * Not specifying this attribute will lead to the Artifact2GroupAssociation props = new Hashtable(); + props.put(ArtifactObject.KEY_MIMETYPE, BundleHelper.MIMETYPE); + BundleHelperImpl helperImpl = new BundleHelperImpl(); + manager.add(createService() + .setInterface(ArtifactHelper.class.getName(), props) + .setImplementation(helperImpl)); + manager.add(createService() + .setInterface(ArtifactRecognizer.class.getName(), null) + .setImplementation(helperImpl)); + manager.add(createService() + .setInterface(BundleHelper.class.getName(), null) + .setImplementation(helperImpl)); + } + + @Override + public synchronized void destroy(BundleContext context, DependencyManager manager) throws Exception { + // Nothing to do + } + +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/bundle/impl/BundleHelperImpl.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/bundle/impl/BundleHelperImpl.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/bundle/impl/BundleHelperImpl.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/bundle/impl/BundleHelperImpl.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,264 @@ +package net.luminis.liq.client.repository.helper.bundle.impl; + +import java.io.IOException; +import java.net.URL; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; + +import net.luminis.liq.client.repository.RepositoryUtil; +import net.luminis.liq.client.repository.helper.ArtifactHelper; +import net.luminis.liq.client.repository.helper.ArtifactPreprocessor; +import net.luminis.liq.client.repository.helper.ArtifactRecognizer; +import net.luminis.liq.client.repository.helper.bundle.BundleHelper; +import net.luminis.liq.client.repository.object.ArtifactObject; +import net.luminis.liq.util.VersionRange; + +import org.osgi.framework.Version; + +/** + * BundleHelperImpl provides the Artifact Repository with Helper and Recognizer services. + */ +public class BundleHelperImpl implements ArtifactRecognizer, BundleHelper { + /** A custom Comparator, used to sort bundles in increasing version */ + private static final Comparator BUNDLE_COMPARATOR = new Comparator() { + public int compare(ArtifactObject left, ArtifactObject right) { + Version vLeft = new Version(left.getAttribute(BundleHelper.KEY_VERSION)); + Version vRight = new Version(right.getAttribute(BundleHelper.KEY_VERSION)); + return vRight.compareTo(vLeft); + } + }; + + /* + * From ArtifactHelper + */ + + public boolean canUse(ArtifactObject object) { + if (object == null) { + return false; + } + return (object.getMimetype().equals(MIMETYPE)); + } + + public String getAssociationFilter(TYPE obj, Map properties) { + /* + * Creates an endpoint filter for an association. If there is a KEY_ASSOCIATION_VERSIONSTATEMENT, a filter + * will be created that matches exactly the given range. + */ + if ((properties != null) && properties.containsKey(KEY_ASSOCIATION_VERSIONSTATEMENT)) { + String versions = properties.get(KEY_ASSOCIATION_VERSIONSTATEMENT); + VersionRange versionRange = null; + try { + versionRange = VersionRange.parse(versions); + } + catch (IllegalArgumentException iae) { + throw new IllegalArgumentException("version " + ((versions != null) ? versions + " " : "(null) ") + "cannot be parsed into a valid version range statement."); + } + + StringBuilder bundleStatement = new StringBuilder("(&(" + KEY_SYMBOLICNAME + "=" + RepositoryUtil.escapeFilterValue(obj.getAttribute(KEY_SYMBOLICNAME)) + ")"); + + bundleStatement.append("(" + KEY_VERSION + ">=" + versionRange.getLow() + ")"); + if (!versionRange.isLowInclusive()) { + bundleStatement.append("(!(" + KEY_VERSION + "=" + versionRange.getLow() + "))"); + } + + if (versionRange.getHigh() != null) { + bundleStatement.append("(" + KEY_VERSION + "<=" + versionRange.getHigh() + ")"); + if (!versionRange.isHighInclusive()) { + bundleStatement.append("(!(" + KEY_VERSION + "=" + versionRange.getHigh() + "))"); + } + } + + bundleStatement.append(")"); + + return bundleStatement.toString(); + } + else + { + if (obj.getAttribute(KEY_VERSION) != null) { + return "(&(" + KEY_SYMBOLICNAME + "=" + RepositoryUtil.escapeFilterValue(obj.getAttribute(KEY_SYMBOLICNAME)) + ")(" + KEY_VERSION + "=" + RepositoryUtil.escapeFilterValue(obj.getAttribute(KEY_VERSION)) + "))"; + } + else { + return "(&(" + KEY_SYMBOLICNAME + "=" + RepositoryUtil.escapeFilterValue(obj.getAttribute(KEY_SYMBOLICNAME)) + ")(!(" + KEY_VERSION + "=*)))"; + } + } + } + + public int getCardinality(TYPE obj, Map properties) { + /* Normally, all objects that match the filter given by the previous version should be part of the + * association. However, when a version statement has been given, only one should be used. */ + if ((properties != null) && properties.containsKey(BundleHelper.KEY_ASSOCIATION_VERSIONSTATEMENT)) { + return 1; + } + else { + return Integer.MAX_VALUE; + } + } + + public Comparator getComparator() { + return BUNDLE_COMPARATOR; + } + + public Map checkAttributes(Map attributes) { + return normalizeVersion(attributes); + } + + /** + * For the filter to work correctly, we need to make sure the version statement is an + * OSGi version. + */ + private static Map normalizeVersion(Map input) { + String version = input.get(KEY_VERSION); + if (version != null) { + try { + Version theVersion = new Version(version); + input.put(KEY_VERSION, theVersion.toString()); + } + catch (IllegalArgumentException iae) { + throw new IllegalArgumentException("The version statement in the bundle cannot be parsed to a valid version.", iae); + } + } + + return input; + } + + /* + * From BundleHelper + */ + + public String[] getDefiningKeys() { + return new String[] {KEY_SYMBOLICNAME, KEY_VERSION}; + } + + public String[] getMandatoryAttributes() { + return new String[] {KEY_SYMBOLICNAME}; + } + + public String getResourceProcessorPIDs(ArtifactObject object) { + ensureBundle(object); + return object.getAttribute(KEY_RESOURCE_PROCESSOR_PID); + } + + public String getSymbolicName(ArtifactObject object) { + ensureBundle(object); + return object.getAttribute(KEY_SYMBOLICNAME); + } + + public String getName(ArtifactObject object) { + ensureBundle(object); + return object.getAttribute(KEY_NAME); + } + + public String getVersion(ArtifactObject object) { + ensureBundle(object); + return object.getAttribute(KEY_VERSION); + } + + public String getVendor(ArtifactObject object) { + ensureBundle(object); + return object.getAttribute(KEY_VENDOR); + } + + public boolean isResourceProcessor(ArtifactObject object) { + ensureBundle(object); + return object.getAttribute(KEY_RESOURCE_PROCESSOR_PID) != null; + } + + private void ensureBundle(ArtifactObject object) { + if ((object == null) || !object.getMimetype().equals(MIMETYPE)) { + throw new IllegalArgumentException("This ArtifactObject cannot be handled by a BundleHelper."); + } + } + + /* + * From ArtifactRecognizer + */ + + public boolean canHandle(String mimetype) { + return MIMETYPE.equals(mimetype); + } + + public Map extractMetaData(URL artifact) throws IllegalArgumentException { + /* + * Opens the URL as a Jar input stream, gets the manifest, and extracts headers from there. + */ + JarInputStream jis = null; + try { + jis = new JarInputStream(artifact.openStream()); + + Attributes manifestAttributes = jis.getManifest().getMainAttributes(); + Map result = new HashMap(); + + for (String key : new String[] {KEY_NAME, KEY_SYMBOLICNAME, KEY_VERSION, KEY_VENDOR, KEY_RESOURCE_PROCESSOR_PID}) { + String value = manifestAttributes.getValue(key); + if (value != null) { + result.put(key, value); + } + } + + if (result.get(KEY_VERSION) == null) { + result.put(KEY_VERSION, "0.0.0"); + } + + result.put(ArtifactHelper.KEY_MIMETYPE, MIMETYPE); + result.put(ArtifactObject.KEY_PROCESSOR_PID, ""); + String name = manifestAttributes.getValue(KEY_NAME); + String version = manifestAttributes.getValue(KEY_VERSION); + if (name == null) { + name = manifestAttributes.getValue(KEY_SYMBOLICNAME); + } + result.put(ArtifactObject.KEY_ARTIFACT_NAME, name + (version == null ? "" : "-" + version)); + + return result; + } + catch (Exception e) { + throw new IllegalArgumentException("Error extracting metadata from artifact.", e); + } + finally { + try { + jis.close(); + } + catch (IOException e) { + // Too bad. + } + } + } + + public String recognize(URL artifact) { + /* + * Tries to find out whether this artifact is a bundle by (a) trying to open it as a + * jar, (b) trying to extract the manifest, and (c) checking whether that manifest + * contains a Bundle-SymbolicName header. + */ + JarInputStream jis = null; + try { + jis = new JarInputStream(artifact.openStream()); + + Manifest manifest = jis.getManifest(); + + Attributes mainAttributes = manifest.getMainAttributes(); + if (mainAttributes.getValue(KEY_SYMBOLICNAME) != null) { + return MIMETYPE; + } + } + catch (Exception e) { + return null; + } + finally { + try { + jis.close(); + } + catch (Exception e) { + // Too bad. + } + } + return null; + } + + public ArtifactPreprocessor getPreprocessor() { + return null; + } +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/ConfigurationHelper.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/ConfigurationHelper.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/ConfigurationHelper.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/ConfigurationHelper.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,13 @@ +package net.luminis.liq.client.repository.helper.configuration; + +import net.luminis.liq.client.repository.helper.ArtifactHelper; + +/** + * Definitions for ConfigurationHelper,used to treat an artifact as an AutoConf file. + */ +public interface ConfigurationHelper extends ArtifactHelper { + public static final String KEY_FILENAME = "filename"; + + public static final String MIMETYPE = "application/xml:osgi-autoconf"; + public static final String PROCESSOR = "org.osgi.deployment.rp.autoconf"; +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/impl/Activator.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/impl/Activator.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/impl/Activator.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/impl/Activator.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,38 @@ +package net.luminis.liq.client.repository.helper.configuration.impl; + +import java.util.Dictionary; +import java.util.Hashtable; + +import net.luminis.liq.client.repository.helper.ArtifactHelper; +import net.luminis.liq.client.repository.helper.ArtifactRecognizer; +import net.luminis.liq.client.repository.helper.configuration.ConfigurationHelper; +import net.luminis.liq.client.repository.object.ArtifactObject; + +import org.apache.felix.dependencymanager.DependencyActivatorBase; +import org.apache.felix.dependencymanager.DependencyManager; +import org.osgi.framework.BundleContext; + +/** + * Activator class for the Configuration ArtifactHelper. + */ +public class Activator extends DependencyActivatorBase { + + @Override + public synchronized void init(BundleContext context, DependencyManager manager) throws Exception { + Dictionary props = new Hashtable(); + props.put(ArtifactObject.KEY_MIMETYPE, ConfigurationHelper.MIMETYPE); + ConfigurationHelperImpl helperImpl = new ConfigurationHelperImpl(); + manager.add(createService() + .setInterface(ArtifactHelper.class.getName(), props) + .setImplementation(helperImpl)); + manager.add(createService() + .setInterface(ArtifactRecognizer.class.getName(), null) + .setImplementation(helperImpl)); + } + + @Override + public synchronized void destroy(BundleContext context, DependencyManager manager) throws Exception { + // Nothing to do + } + +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/impl/ConfigurationHelperImpl.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/impl/ConfigurationHelperImpl.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/impl/ConfigurationHelperImpl.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/configuration/impl/ConfigurationHelperImpl.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,89 @@ +package net.luminis.liq.client.repository.helper.configuration.impl; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilderFactory; + +import net.luminis.liq.client.repository.helper.ArtifactPreprocessor; +import net.luminis.liq.client.repository.helper.ArtifactRecognizer; +import net.luminis.liq.client.repository.helper.base.VelocityArtifactPreprocessor; +import net.luminis.liq.client.repository.helper.configuration.ConfigurationHelper; +import net.luminis.liq.client.repository.object.ArtifactObject; + +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +public class ConfigurationHelperImpl implements ArtifactRecognizer, ConfigurationHelper { + + public boolean canHandle(String mimetype) { + return MIMETYPE.equals(mimetype); + } + + public Map extractMetaData(URL artifact) throws IllegalArgumentException { + Map result = new HashMap(); + result.put(KEY_FILENAME, new File(artifact.getFile()).getName()); + result.put(ArtifactObject.KEY_PROCESSOR_PID, PROCESSOR); + result.put(ArtifactObject.KEY_MIMETYPE, MIMETYPE); + result.put(ArtifactObject.KEY_ARTIFACT_NAME, result.get(KEY_FILENAME)); + return result; + } + + public String recognize(URL artifact) { + try { + InputStream in = artifact.openStream(); + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in); + Node first = doc.getFirstChild(); + NamedNodeMap attributes = first.getAttributes(); + Node metatype = attributes.getNamedItem("xmlns:metatype"); + if (new String("http://www.osgi.org/xmlns/metatype/v1.0.0").equals(metatype.getTextContent())) { + return MIMETYPE; + } + } + catch (Exception e) { + // Does not matter. + } + + return null; + } + + public boolean canUse(ArtifactObject object) { + return MIMETYPE.equals(object.getMimetype()); + } + + public Map checkAttributes(Map attributes) { + // All necessary checks will be done by the constructor using getMandatoryAttributes. + return attributes; + } + + public String getAssociationFilter(TYPE obj, Map properties) { + return "(" + KEY_FILENAME + "=" + obj.getAttribute(KEY_FILENAME) + ")"; + } + + public int getCardinality(TYPE obj, Map properties) { + return Integer.MAX_VALUE; + } + + public Comparator getComparator() { + return null; + } + + public String[] getDefiningKeys() { + return new String[] {KEY_FILENAME}; + } + + public String[] getMandatoryAttributes() { + return new String[] {KEY_FILENAME}; + } + + private final static VelocityArtifactPreprocessor VELOCITY_ARTIFACT_PREPROCESSOR = new VelocityArtifactPreprocessor(); + public ArtifactPreprocessor getPreprocessor() { + return VELOCITY_ARTIFACT_PREPROCESSOR; + } + +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/UserAdminHelper.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/UserAdminHelper.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/UserAdminHelper.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/UserAdminHelper.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,11 @@ +package net.luminis.liq.client.repository.helper.user; + +import net.luminis.liq.client.repository.helper.ArtifactHelper; + +/** + * Definitions for the UserAdminHelper artifact helper. + */ +public interface UserAdminHelper extends ArtifactHelper { + public static final String MIMETYPE = "application/vnd.luminis.useradmin"; + public static final String PROCESSOR = "net.luminis.liq.resourceprocessor.useradmin"; +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/impl/Activator.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/impl/Activator.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/impl/Activator.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/impl/Activator.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,40 @@ +package net.luminis.liq.client.repository.helper.user.impl; + +import java.util.Properties; + +import net.luminis.liq.client.repository.helper.ArtifactHelper; +import net.luminis.liq.client.repository.helper.ArtifactRecognizer; +import net.luminis.liq.client.repository.helper.user.UserAdminHelper; +import net.luminis.liq.client.repository.object.ArtifactObject; + +import org.apache.felix.dependencymanager.DependencyActivatorBase; +import org.apache.felix.dependencymanager.DependencyManager; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; + +/** + * Activator class for the UserAdmin ArtifactHelper. + */ +public class Activator extends DependencyActivatorBase { + + @Override + public synchronized void init(BundleContext context, DependencyManager manager) throws Exception { + Properties props = new Properties(); + props.put(ArtifactObject.KEY_MIMETYPE, UserAdminHelper.MIMETYPE); + UserHelperImpl helperImpl = new UserHelperImpl(); + manager.add(createService() + .setInterface(ArtifactHelper.class.getName(), props) + .setImplementation(helperImpl)); + props = new Properties(); + props.put(Constants.SERVICE_RANKING, 10); + manager.add(createService() + .setInterface(ArtifactRecognizer.class.getName(), props) + .setImplementation(helperImpl)); + } + + @Override + public synchronized void destroy(BundleContext context, DependencyManager manager) throws Exception { + // Nothing to do + } + +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/impl/UserHelperImpl.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/impl/UserHelperImpl.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/impl/UserHelperImpl.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/helper/user/impl/UserHelperImpl.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,91 @@ +package net.luminis.liq.client.repository.helper.user.impl; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilderFactory; + +import net.luminis.liq.client.repository.helper.ArtifactPreprocessor; +import net.luminis.liq.client.repository.helper.ArtifactRecognizer; +import net.luminis.liq.client.repository.helper.base.VelocityArtifactPreprocessor; +import net.luminis.liq.client.repository.helper.user.UserAdminHelper; +import net.luminis.liq.client.repository.object.ArtifactObject; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +public class UserHelperImpl implements ArtifactRecognizer, UserAdminHelper { + + public boolean canHandle(String mimetype) { + return MIMETYPE.equals(mimetype); + } + + public Map extractMetaData(URL artifact) throws IllegalArgumentException { + Map result = new HashMap(); + result.put(ArtifactObject.KEY_PROCESSOR_PID, PROCESSOR); + result.put(ArtifactObject.KEY_MIMETYPE, MIMETYPE); + result.put(ArtifactObject.KEY_ARTIFACT_NAME, new File(artifact.getFile()).getName()); + return result; + } + + public String recognize(URL artifact) { + try { + InputStream in = artifact.openStream(); + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in); + Node root = doc.getFirstChild(); + if (!root.getNodeName().equals("roles")) { + return null; + } + for (Node node = root.getFirstChild(); root != null; root = root.getNextSibling()) { + if (!node.getNodeName().equals("group") && !node.getNodeName().equals("user") && !node.getNodeName().equals("#text")) { + return null; + } + } + return MIMETYPE; + } + catch (Exception e) { + // Does not matter. + } + + return null; + } + + public boolean canUse(ArtifactObject object) { + return MIMETYPE.equals(object.getMimetype()); + } + + public Map checkAttributes(Map attributes) { + // All necessary checks will be done by the constructor using getMandatoryAttributes. + return attributes; + } + + public String getAssociationFilter(TYPE obj, Map properties) { + return "(" + ArtifactObject.KEY_ARTIFACT_NAME + "=" + obj.getAttribute(ArtifactObject.KEY_ARTIFACT_NAME) + ")"; + } + + public int getCardinality(TYPE obj, Map properties) { + return Integer.MAX_VALUE; + } + + public Comparator getComparator() { + return null; + } + + public String[] getDefiningKeys() { + return new String[] {ArtifactObject.KEY_ARTIFACT_NAME}; + } + + public String[] getMandatoryAttributes() { + return new String[] {ArtifactObject.KEY_ARTIFACT_NAME}; + } + + private final static VelocityArtifactPreprocessor VELOCITY_ARTIFACT_PREPROCESSOR = new VelocityArtifactPreprocessor(); + public ArtifactPreprocessor getPreprocessor() { + return VELOCITY_ARTIFACT_PREPROCESSOR; + } + +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Activator.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Activator.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Activator.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Activator.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,191 @@ +package net.luminis.liq.client.repository.impl; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import net.luminis.liq.client.repository.ObjectRepository; +import net.luminis.liq.client.repository.RepositoryAdmin; +import net.luminis.liq.client.repository.RepositoryObject; +import net.luminis.liq.client.repository.helper.ArtifactHelper; +import net.luminis.liq.client.repository.object.Artifact2GroupAssociation; +import net.luminis.liq.client.repository.object.ArtifactObject; +import net.luminis.liq.client.repository.object.DeploymentVersionObject; +import net.luminis.liq.client.repository.object.GatewayObject; +import net.luminis.liq.client.repository.object.Group2LicenseAssociation; +import net.luminis.liq.client.repository.object.GroupObject; +import net.luminis.liq.client.repository.object.License2GatewayAssociation; +import net.luminis.liq.client.repository.object.LicenseObject; +import net.luminis.liq.client.repository.repository.Artifact2GroupAssociationRepository; +import net.luminis.liq.client.repository.repository.ArtifactRepository; +import net.luminis.liq.client.repository.repository.DeploymentVersionRepository; +import net.luminis.liq.client.repository.repository.GatewayRepository; +import net.luminis.liq.client.repository.repository.Group2LicenseAssociationRepository; +import net.luminis.liq.client.repository.repository.GroupRepository; +import net.luminis.liq.client.repository.repository.License2GatewayAssociationRepository; +import net.luminis.liq.client.repository.repository.LicenseRepository; + +import org.apache.felix.dependencymanager.DependencyActivatorBase; +import org.apache.felix.dependencymanager.DependencyManager; +import org.apache.felix.dependencymanager.Service; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.event.EventAdmin; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; +import org.osgi.service.log.LogService; +import org.osgi.service.prefs.PreferencesService; + +/** + * Activator for the RepositoryAdmin bundle. Creates and registers the necessary repositories, + * plus the repository admin. + */ +public class Activator extends DependencyActivatorBase { + private DependencyManager m_manager; + List m_services; + + private RepositoryAdminImpl m_repositoryAdminImpl; + private ChangeNotifierManager m_changeNotifierManager; + + private ArtifactRepositoryImpl m_artifactRepositoryImpl; + private GroupRepositoryImpl m_groupRepositoryImpl; + private Artifact2GroupAssociationRepositoryImpl m_artifact2GroupAssociationRepositoryImpl; + private LicenseRepositoryImpl m_licenseRepositoryImpl; + private Group2LicenseAssociationRepositoryImpl m_group2LicenseAssociationRepositoryImpl; + private GatewayRepositoryImpl m_gatewayRepositoryImpl; + private License2GatewayAssociationRepositoryImpl m_license2GatewayAssociationRepositoryImpl; + private DeploymentVersionRepositoryImpl m_deploymentVersionRepositoryImpl; + + @Override + public synchronized void init(BundleContext context, DependencyManager manager) throws Exception { + m_manager = manager; + + m_changeNotifierManager = new ChangeNotifierManager(); + manager.add(createService() + .setImplementation(m_changeNotifierManager) + .add(createServiceDependency().setService(EventAdmin.class).setRequired(true))); + + m_repositoryAdminImpl = new RepositoryAdminImpl(this, m_changeNotifierManager.getConfiguredNotifier(RepositoryAdmin.PRIVATE_TOPIC_ROOT, RepositoryAdmin.PUBLIC_TOPIC_ROOT, RepositoryAdmin.TOPIC_ENTITY_ROOT)); + manager.add(createService() + .setInterface(RepositoryAdmin.class.getName(), null) + .setImplementation(m_repositoryAdminImpl) + .add(createServiceDependency().setService(PreferencesService.class).setRequired(true)) + .add(createServiceDependency().setService(LogService.class).setRequired(false))); + } + + private Service[] registerRepository(Class> iface, ObjectRepositoryImpl imp, String[] topics) { + Service repositoryService = createService() + .setInterface(iface.getName(), null) + .setImplementation(imp) + .add(createServiceDependency().setService(LogService.class).setRequired(false)); + Dictionary topic = new Hashtable(); + topic.put(EventConstants.EVENT_TOPIC, topics); + Service handlerService = createService() + .setInterface(EventHandler.class.getName(), topic) + .setImplementation(imp); + + m_manager.add(repositoryService); + m_manager.add(handlerService); + return new Service[] {repositoryService, handlerService}; + } + + @SuppressWarnings("unchecked") + synchronized Map, ObjectRepositoryImpl> publishRepositories() { + // create the repository objects, if this is the first time this method is called. + if (m_artifactRepositoryImpl == null) { + m_artifactRepositoryImpl = new ArtifactRepositoryImpl(m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, ArtifactObject.TOPIC_ENTITY_ROOT)); + m_groupRepositoryImpl = new GroupRepositoryImpl(m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, GroupObject.TOPIC_ENTITY_ROOT)); + m_artifact2GroupAssociationRepositoryImpl = new Artifact2GroupAssociationRepositoryImpl(m_artifactRepositoryImpl, m_groupRepositoryImpl, m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, Artifact2GroupAssociation.TOPIC_ENTITY_ROOT)); + m_licenseRepositoryImpl = new LicenseRepositoryImpl(m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, LicenseObject.TOPIC_ENTITY_ROOT)); + m_group2LicenseAssociationRepositoryImpl = new Group2LicenseAssociationRepositoryImpl(m_groupRepositoryImpl, m_licenseRepositoryImpl, m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, Group2LicenseAssociation.TOPIC_ENTITY_ROOT)); + m_gatewayRepositoryImpl = new GatewayRepositoryImpl(m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, GatewayObject.TOPIC_ENTITY_ROOT)); + m_license2GatewayAssociationRepositoryImpl = new License2GatewayAssociationRepositoryImpl(m_licenseRepositoryImpl, m_gatewayRepositoryImpl, m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, License2GatewayAssociation.TOPIC_ENTITY_ROOT)); + m_deploymentVersionRepositoryImpl = new DeploymentVersionRepositoryImpl(m_changeNotifierManager.getConfiguredNotifier(RepositoryObject.PRIVATE_TOPIC_ROOT, RepositoryObject.PUBLIC_TOPIC_ROOT, DeploymentVersionObject.TOPIC_ENTITY_ROOT)); + } + // first, register the artifact repository manually; it needs some special care. + Service artifactRepoService = createService() + .setInterface(ArtifactRepository.class.getName(), null) + .setImplementation(m_artifactRepositoryImpl) + .add(createServiceDependency().setService(LogService.class).setRequired(false)) + .add(createServiceDependency().setService(ArtifactHelper.class).setRequired(false).setAutoConfig(false).setCallbacks(this, "addArtifactHelper", "removeArtifactHelper")); + Dictionary topic = new Hashtable(); + topic.put(EventConstants.EVENT_TOPIC, new String[] {}); + Service artifactHandlerService = createService() + .setInterface(EventHandler.class.getName(), topic) + .setImplementation(m_artifactRepositoryImpl); + m_manager.add(artifactRepoService); + m_manager.add(artifactHandlerService); + + m_services = new ArrayList(); + m_services.add(new Service[] {artifactRepoService, artifactHandlerService}); + + // register all repositories are services. Keep the service objects around, we need them to pull the services later. + m_services.add(registerRepository(Artifact2GroupAssociationRepository.class, m_artifact2GroupAssociationRepositoryImpl, new String[] {createPrivateObjectTopic(ArtifactObject.TOPIC_ENTITY_ROOT), createPrivateObjectTopic(GroupObject.TOPIC_ENTITY_ROOT)})); + m_services.add(registerRepository(GroupRepository.class, m_groupRepositoryImpl, new String[] {})); + m_services.add(registerRepository(Group2LicenseAssociationRepository.class, m_group2LicenseAssociationRepositoryImpl, new String[] {createPrivateObjectTopic(GroupObject.TOPIC_ENTITY_ROOT), createPrivateObjectTopic(LicenseObject.TOPIC_ENTITY_ROOT)})); + m_services.add(registerRepository(LicenseRepository.class, m_licenseRepositoryImpl, new String[] {})); + m_services.add(registerRepository(License2GatewayAssociationRepository.class, m_license2GatewayAssociationRepositoryImpl, new String[] {createPrivateObjectTopic(LicenseObject.TOPIC_ENTITY_ROOT), createPrivateObjectTopic(GatewayObject.TOPIC_ENTITY_ROOT)})); + m_services.add(registerRepository(GatewayRepository.class, m_gatewayRepositoryImpl, new String[] {})); + m_services.add(registerRepository(DeploymentVersionRepository.class, m_deploymentVersionRepositoryImpl, new String[] {})); + + // prepare the results. + Map, ObjectRepositoryImpl> result = new HashMap, ObjectRepositoryImpl>(); + + result.put(ArtifactRepository.class, m_artifactRepositoryImpl); + result.put(Artifact2GroupAssociationRepository.class, m_artifact2GroupAssociationRepositoryImpl); + result.put(GroupRepository.class, m_groupRepositoryImpl); + result.put(Group2LicenseAssociationRepository.class, m_group2LicenseAssociationRepositoryImpl); + result.put(LicenseRepository.class, m_licenseRepositoryImpl); + result.put(License2GatewayAssociationRepository.class, m_license2GatewayAssociationRepositoryImpl); + result.put(GatewayRepository.class, m_gatewayRepositoryImpl); + result.put(DeploymentVersionRepository.class, m_deploymentVersionRepositoryImpl); + + return result; + } + + /** + * Helper method for use in publishRepositories + */ + private static String createPrivateObjectTopic(String entityRoot) { + return RepositoryObject.PRIVATE_TOPIC_ROOT + entityRoot + RepositoryObject.TOPIC_ALL_SUFFIX; + } + + /** + * Pulls all repository services; is used to make sure the repositories go away before the RepositoryAdmin does. + */ + synchronized void pullRepositories() { + for (Service[] services : m_services) { + for (Service service : services) { + m_manager.remove(service); + } + } + } + + @Override + public synchronized void destroy(BundleContext context, DependencyManager manager) throws Exception { + if (m_repositoryAdminImpl.loggedIn()) { + try { + m_repositoryAdminImpl.logout(true); + } + catch (IOException ioe) { + // Not much to do about this. We could log it? + } + } + m_repositoryAdminImpl = null; + } + + public void addArtifactHelper(ServiceReference ref, ArtifactHelper helper) { + String mimetype = (String) ref.getProperty(ArtifactHelper.KEY_MIMETYPE); + m_artifactRepositoryImpl.addHelper(mimetype, helper); + } + + public synchronized void removeArtifactHelper(ServiceReference ref, ArtifactHelper helper) { + String mimetype = (String) ref.getProperty(ArtifactHelper.KEY_MIMETYPE); + m_artifactRepositoryImpl.removeHelper(mimetype, helper); + } + +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Artifact2GroupAssociationImpl.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Artifact2GroupAssociationImpl.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Artifact2GroupAssociationImpl.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Artifact2GroupAssociationImpl.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,32 @@ +package net.luminis.liq.client.repository.impl; + +import java.util.Map; + +import net.luminis.liq.client.repository.object.Artifact2GroupAssociation; +import net.luminis.liq.client.repository.object.ArtifactObject; +import net.luminis.liq.client.repository.object.GroupObject; + +import org.osgi.framework.InvalidSyntaxException; + +import com.thoughtworks.xstream.io.HierarchicalStreamReader; + +/** + * Implementation class for the Artifact2GroupAssociation. For 'what it does', see Artifact2GroupAssociation, + * for 'how it works', see AssociationImpl. + */ +public class Artifact2GroupAssociationImpl extends AssociationImpl implements Artifact2GroupAssociation { + private final static String XML_NODE = "artifact2group"; + + public Artifact2GroupAssociationImpl(Map attributes, ChangeNotifier notifier, ArtifactRepositoryImpl artifactRepository, GroupRepositoryImpl groupRepository) throws InvalidSyntaxException { + super(attributes, notifier, ArtifactObject.class, GroupObject.class, artifactRepository, groupRepository, XML_NODE); + } + + public Artifact2GroupAssociationImpl(Map attributes, Map tags, ChangeNotifier notifier, ArtifactRepositoryImpl artifactRepository, GroupRepositoryImpl groupRepository) throws InvalidSyntaxException { + super(attributes, tags, notifier, ArtifactObject.class, GroupObject.class, artifactRepository, groupRepository, XML_NODE); + } + + public Artifact2GroupAssociationImpl(HierarchicalStreamReader reader, ChangeNotifier notifier, ArtifactRepositoryImpl artifactRepository, GroupRepositoryImpl groupRepository) throws InvalidSyntaxException { + super(reader, notifier, ArtifactObject.class, GroupObject.class, null, null, artifactRepository, groupRepository, XML_NODE); + } + +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Artifact2GroupAssociationRepositoryImpl.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Artifact2GroupAssociationRepositoryImpl.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Artifact2GroupAssociationRepositoryImpl.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Artifact2GroupAssociationRepositoryImpl.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,59 @@ +package net.luminis.liq.client.repository.impl; + +import java.util.Map; + +import net.luminis.liq.client.repository.object.Artifact2GroupAssociation; +import net.luminis.liq.client.repository.object.ArtifactObject; +import net.luminis.liq.client.repository.object.GroupObject; +import net.luminis.liq.client.repository.repository.Artifact2GroupAssociationRepository; + +import org.osgi.framework.InvalidSyntaxException; + +import com.thoughtworks.xstream.io.HierarchicalStreamReader; + +/** + * Implementation class for the Artifact2GroupAssociationRepository. For 'what it does', see Artifact2GroupAssociationRepository, + * for 'how it works', see AssociationRepositoryImpl. + */ +public class Artifact2GroupAssociationRepositoryImpl extends AssociationRepositoryImpl implements Artifact2GroupAssociationRepository { + private final static String XML_NODE = "artifacts2groups"; + + private final ArtifactRepositoryImpl m_bundleRepository; + private final GroupRepositoryImpl m_groupRepository; + + public Artifact2GroupAssociationRepositoryImpl(ArtifactRepositoryImpl bundleRepository, GroupRepositoryImpl groupRepository, ChangeNotifier notifier) { + super(notifier, XML_NODE); + m_bundleRepository = bundleRepository; + m_groupRepository = groupRepository; + } + + @Override + Artifact2GroupAssociationImpl createNewInhabitant(Map attributes) { + try { + return new Artifact2GroupAssociationImpl(attributes, this, m_bundleRepository, m_groupRepository); + } + catch (InvalidSyntaxException e) { + throw new IllegalArgumentException("Unable to create association: ", e); + } + } + + @Override + Artifact2GroupAssociationImpl createNewInhabitant(Map attributes, Map tags) { + try { + return new Artifact2GroupAssociationImpl(attributes, tags, this, m_bundleRepository, m_groupRepository); + } + catch (InvalidSyntaxException e) { + throw new IllegalArgumentException("Unable to create association: ", e); + } + } + + @Override + Artifact2GroupAssociationImpl createNewInhabitant(HierarchicalStreamReader reader) { + try { + return new Artifact2GroupAssociationImpl(reader, this, m_bundleRepository, m_groupRepository); + } + catch (InvalidSyntaxException e) { + throw new IllegalArgumentException("Unable to create association: ", e); + } + } +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ArtifactObjectImpl.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ArtifactObjectImpl.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ArtifactObjectImpl.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ArtifactObjectImpl.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,121 @@ +package net.luminis.liq.client.repository.impl; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import net.luminis.liq.client.repository.helper.ArtifactHelper; +import net.luminis.liq.client.repository.object.Artifact2GroupAssociation; +import net.luminis.liq.client.repository.object.ArtifactObject; +import net.luminis.liq.client.repository.object.GroupObject; + +import com.thoughtworks.xstream.io.HierarchicalStreamReader; + +/** + * Implementation class for the ArtifactObject. For 'what it does', see ArtifactObject, + * for 'how it works', see RepositoryObjectImpl.
+ *
+ * Some functionality of this class is delegated to implementers of {@link ArtifactHelper}. + */ +public class ArtifactObjectImpl extends RepositoryObjectImpl implements ArtifactObject { + private final static String XML_NODE = "artifact"; + + /* + * As a general rule, RepositoryObjects do not know about their repository. However, since the Helper + * to be used is dictated by the repository, this rule is broken for this class. + */ + private ArtifactRepositoryImpl m_repo; + + ArtifactObjectImpl(Map attributes, String[] mandatoryAttributes, ChangeNotifier notifier, ArtifactRepositoryImpl repo) { + super(checkAttributes(attributes, completeMandatoryAttributes(mandatoryAttributes)), notifier, XML_NODE); + m_repo = repo; + } + + ArtifactObjectImpl(Map attributes, String[] mandatoryAttributes, Map tags, ChangeNotifier notifier, ArtifactRepositoryImpl repo) { + super(checkAttributes(attributes, completeMandatoryAttributes(mandatoryAttributes)), tags, notifier, XML_NODE); + m_repo = repo; + } + + private static String[] completeMandatoryAttributes(String[] mandatory) { + String[] result = new String[mandatory.length + 2]; + for (int i = 0; i < mandatory.length; i++) { + result[i] = mandatory[i]; + } + result[mandatory.length] = KEY_MIMETYPE; + result[mandatory.length + 1 ] = KEY_URL; + return result; + } + + ArtifactObjectImpl(HierarchicalStreamReader reader, ChangeNotifier notifier, ArtifactRepositoryImpl repo) { + super(reader, notifier, XML_NODE); + m_repo = repo; + } + + public List getGroups() { + return getAssociations(GroupObject.class); + } + + public List getAssociationsWith(GroupObject group) { + return getAssociationsWith(group, GroupObject.class, Artifact2GroupAssociation.class); + } + + @Override + public String getAssociationFilter(Map properties) { + return getHelper().getAssociationFilter(this, properties); + } + + @Override + public int getCardinality(Map properties) { + return getHelper().getCardinality(this, properties); + } + + @Override + public Comparator getComparator() { + return getHelper().getComparator(); + } + + @Override + String[] getDefiningKeys() { + String[] fromHelper = getHelper().getDefiningKeys(); + + String[] result = new String[fromHelper.length + 1]; + for (int i = 0; i < fromHelper.length; i++) { + result[i] = fromHelper[i]; + } + result[fromHelper.length] = KEY_URL; + + return result; + } + + public String getURL() { + return getAttribute(KEY_URL); + } + + public String getMimetype() { + return getAttribute(KEY_MIMETYPE); + } + + public String getProcessorPID() { + return getAttribute(KEY_PROCESSOR_PID); + } + + public void setProcessorPID(String processorPID) { + addAttribute(KEY_PROCESSOR_PID, processorPID); + } + + public String getName() { + return getAttribute(KEY_ARTIFACT_NAME); + } + + public String getDescription() { + return getAttribute(KEY_ARTIFACT_DESCRIPTION); + } + + private synchronized ArtifactHelper getHelper() { + return m_repo.getHelper(getMimetype()); + } + + public void setDescription(String value) { + addAttribute(KEY_ARTIFACT_DESCRIPTION, value); + } +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ArtifactRepositoryImpl.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ArtifactRepositoryImpl.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ArtifactRepositoryImpl.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ArtifactRepositoryImpl.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,550 @@ +package net.luminis.liq.client.repository.impl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.luminis.liq.client.repository.RepositoryObject; +import net.luminis.liq.client.repository.RepositoryUtil; +import net.luminis.liq.client.repository.helper.ArtifactHelper; +import net.luminis.liq.client.repository.helper.ArtifactPreprocessor; +import net.luminis.liq.client.repository.helper.ArtifactRecognizer; +import net.luminis.liq.client.repository.helper.PropertyResolver; +import net.luminis.liq.client.repository.helper.bundle.BundleHelper; +import net.luminis.liq.client.repository.object.ArtifactObject; +import net.luminis.liq.client.repository.object.GatewayObject; +import net.luminis.liq.client.repository.object.GroupObject; +import net.luminis.liq.client.repository.object.LicenseObject; +import net.luminis.liq.client.repository.repository.ArtifactRepository; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogService; + +import com.thoughtworks.xstream.io.HierarchicalStreamReader; + +/** + * Implementation class for the ArtifactRepository. For 'what it does', see ArtifactRepository, + * for 'how it works', see ObjectRepositoryImpl.
+ *
+ * This class has some extended functionality when compared to ObjectRepositoryImpl, + *
    + *
  • it keeps track of all ArtifactHelpers, and serves them to its inhabitants. + *
  • it handles importing of artifacts. + *
+ */ +public class ArtifactRepositoryImpl extends ObjectRepositoryImpl implements ArtifactRepository { + private final static String XML_NODE = "artifacts"; + private volatile BundleContext m_context; /*Injected by dependency manager*/ + private volatile LogService m_log; /*Injected by dependency manager*/ + private Map m_helpers = new HashMap(); + private URL m_obrBase; + + /** + * Custom comparator which sorts service references by service rank, highest rank first. + */ + private static Comparator SERVICE_RANK_COMPARATOR = new Comparator() { + public int compare(ServiceReference o1, ServiceReference o2) { + int rank1 = 0; + int rank2 = 0; + try { + Object rankObject1 = o1.getProperty(Constants.SERVICE_RANKING); + rank1 = (rankObject1 == null) ? 0 : ((Integer) rankObject1).intValue(); + } + catch (ClassCastException cce) { + // No problem. + } + try { + Object rankObject2 = o2.getProperty(Constants.SERVICE_RANKING); + rank1 = (rankObject2 == null) ? 0 : ((Integer) rankObject2).intValue(); + } + catch (ClassCastException cce) { + // No problem. + } + + return rank1 - rank2; + } + }; + + + public ArtifactRepositoryImpl(ChangeNotifier notifier) { + super(notifier, XML_NODE); + } + + public List getResourceProcessors() { + try { + return super.get(createFilter("(" + BundleHelper.KEY_RESOURCE_PROCESSOR_PID + "=*)")); + } + catch (InvalidSyntaxException e) { + m_log.log(LogService.LOG_ERROR, "getResourceProcessors' filter returned an InvalidSyntaxException.", e); + } + return new ArrayList(); + } + + @Override + public List get(Filter filter) { + // Note that this excludes any Bundle artifacts which are resource processors. + try { + Filter extendedFilter = createFilter("(&" + filter.toString() + "(!(" + BundleHelper.KEY_RESOURCE_PROCESSOR_PID + "=*)))"); + return super.get(extendedFilter); + } + catch (InvalidSyntaxException e) { + m_log.log(LogService.LOG_ERROR, "Extending " + filter.toString() + " resulted in an InvalidSyntaxException.", e); + } + return new ArrayList(); + } + + @Override + public List get() { + // Note that this excludes any Bundle artifacts which are resource processors. + try { + return super.get(createFilter("(!(" + RepositoryUtil.escapeFilterValue(BundleHelper.KEY_RESOURCE_PROCESSOR_PID) + "=*))")); + } + catch (InvalidSyntaxException e) { + m_log.log(LogService.LOG_ERROR, "get's filter returned an InvalidSyntaxException.", e); + } + return new ArrayList(); + } + + @Override + ArtifactObjectImpl createNewInhabitant(Map attributes) { + ArtifactHelper helper = getHelper(attributes.get(ArtifactObject.KEY_MIMETYPE)); + return new ArtifactObjectImpl(helper.checkAttributes(attributes), helper.getMandatoryAttributes(), this, this); + } + + @Override + ArtifactObjectImpl createNewInhabitant(Map attributes, Map tags) { + ArtifactHelper helper = getHelper(attributes.get(ArtifactObject.KEY_MIMETYPE)); + return new ArtifactObjectImpl(helper.checkAttributes(attributes), helper.getMandatoryAttributes(), tags, this, this); + } + + @Override + ArtifactObjectImpl createNewInhabitant(HierarchicalStreamReader reader) { + return new ArtifactObjectImpl(reader, this, this); + } + + /** + * Helper method for this repository's inhabitants, which finds the necessary helpers. + * @param mimetype The mimetype for which a helper should be found. + * @return An artifact helper for the given mimetype. + * @throws IllegalArgumentException when the mimetype is invalid, or no helpers are available. + */ + ArtifactHelper getHelper(String mimetype) { + synchronized(m_helpers) { + if ((mimetype == null) || (mimetype.length() == 0)) { + throw new IllegalArgumentException("Without a mimetype, we cannot find a helper."); + } + + ArtifactHelper helper = m_helpers.get(mimetype.toLowerCase()); + + if (helper == null) { + throw new IllegalArgumentException("There are no ArtifactHelpers known for type '" + mimetype + "'."); + } + + return helper; + } + } + + /** + * Method intended for adding artifact helpers by the bundle's activator. + */ + void addHelper(String mimetype, ArtifactHelper helper) { + synchronized(m_helpers) { + if ((mimetype == null) || (mimetype.length() == 0)) { + m_log.log(LogService.LOG_WARNING, "An ArtifactHelper has been published without a proper mimetype."); + } + else { + m_helpers.put(mimetype.toLowerCase(), helper); + } + } + } + + /** + * Method intended for removing artifact helpers by the bundle's activator. + */ + void removeHelper(String mimetype, ArtifactHelper helper) { + synchronized(m_helpers) { + if ((mimetype == null) || (mimetype.length() == 0)) { + m_log.log(LogService.LOG_WARNING, "An ArtifactHelper is being removed without a proper mimetype."); + } + else { + m_helpers.remove(mimetype.toLowerCase()); + } + } + } + + /** + * Utility function that takes either a URL or a String representing a mimetype, + * and returns the corresponding ArtifactHelper, ArtifactRecognizer + * and, if not specified, the mimetype. + * @param input Either a URL pointing to a physical artifact, or a String + * representing a mime type. + * @return A mapping from a class (ArtifactRecognizer, ArtifactHelper or + * String to an instance of that class as a result. + */ + protected Map, Object> findRecognizerAndHelper(Object input) throws IllegalArgumentException { + // check input. + URL url = null; + String mimetype = null; + if (input instanceof URL) { + url = (URL) input; + } + else if (input instanceof String) { + mimetype = (String) input; + } + else { + throw new IllegalArgumentException("findRecognizer received an unrecognized input."); + } + + // Get all published ArtifactRecognizers. + ServiceReference[] refs = null; + try { + refs = m_context.getServiceReferences(ArtifactRecognizer.class.getName(), null); + } + catch (InvalidSyntaxException e) { + // We do not pass in a filter, so this should not happen. + m_log.log(LogService.LOG_WARNING, "A null filter resulted in an InvalidSyntaxException from getServiceReferences."); + } + + if (refs == null) { + throw new IllegalArgumentException("There are no artifact recognizers available."); + } + + // If available, sort the references by service ranking. + Arrays.sort(refs, SERVICE_RANK_COMPARATOR); + + // Check all referenced services to find one that matches our input. + ArtifactRecognizer recognizer = null; + String foundMimetype = null; + for (ServiceReference ref : refs) { + ArtifactRecognizer candidate = (ArtifactRecognizer) m_context.getService(ref); + if (mimetype != null) { + if (candidate.canHandle(mimetype)) { + recognizer = candidate; + break; + } + } + else { + String candidateMime = candidate.recognize(url); + if (candidateMime != null) { + foundMimetype = candidateMime; + recognizer = candidate; + break; + } + } + } + + if (recognizer == null) { + throw new IllegalArgumentException("There is no artifact recognizer that recognizes artifact " + ((url == null) ? "(null)" : url.toString())); + } + + // Package the results in the map. + Map, Object> result = new HashMap, Object>(); + result.put(ArtifactRecognizer.class, recognizer); + if (mimetype == null) { + result.put(ArtifactHelper.class, getHelper(foundMimetype)); + result.put(String.class, foundMimetype); + } + else { + result.put(ArtifactHelper.class, getHelper(mimetype)); + } + + return result; + } + + public boolean recognizeArtifact(URL artifact) { + try { + Map, Object> fromArtifact = findRecognizerAndHelper(artifact); + String mimetype = (String) fromArtifact.get(String.class); + return mimetype != null; + } + catch (Exception e) { + //too bad... Nothing to do now. + return false; + } + } + + public ArtifactObject importArtifact(URL artifact, boolean upload) throws IllegalArgumentException, IOException { + try { + if ((artifact == null) || (artifact.toString().length() == 0)) { + throw new IllegalArgumentException("The URL to import cannot be null or empty."); + } + checkURL(artifact); + + Map, Object> fromArtifact = findRecognizerAndHelper(artifact); + ArtifactRecognizer recognizer = (ArtifactRecognizer) fromArtifact.get(ArtifactRecognizer.class); + ArtifactHelper helper = (ArtifactHelper) fromArtifact.get(ArtifactHelper.class); + String mimetype = (String) fromArtifact.get(String.class); + + return importArtifact(artifact, recognizer, helper, mimetype, false, upload); + } + catch (IllegalArgumentException iae) { + m_log.log(LogService.LOG_INFO, "Error importing artifact: " + iae.getMessage()); + throw iae; + } + catch (IOException ioe) { + m_log.log(LogService.LOG_INFO, "Error storing artifact: " + ioe.getMessage()); + throw ioe; + } + } + + public ArtifactObject importArtifact(URL artifact, String mimetype, boolean upload) throws IllegalArgumentException, IOException { + try { + if ((artifact == null) || (artifact.toString().length() == 0)) { + throw new IllegalArgumentException("The URL to import cannot be null or empty."); + } + if ((mimetype == null) || (mimetype.length() == 0)) { + throw new IllegalArgumentException("The mimetype of the artifact to import cannot be null or empty."); + } + + checkURL(artifact); + + Map, Object> fromMimetype = findRecognizerAndHelper(mimetype); + ArtifactRecognizer recognizer = (ArtifactRecognizer) fromMimetype.get(ArtifactRecognizer.class); + ArtifactHelper helper = (ArtifactHelper) fromMimetype.get(ArtifactHelper.class); + + return importArtifact(artifact, recognizer, helper, mimetype, true, upload); + } + catch (IllegalArgumentException iae) { + m_log.log(LogService.LOG_INFO, "Error importing artifact: " + iae.getMessage()); + throw iae; + } + catch (IOException ioe) { + m_log.log(LogService.LOG_INFO, "Error storing artifact: " + ioe.getMessage()); + throw ioe; + } + } + + private ArtifactObject importArtifact(URL artifact, ArtifactRecognizer recognizer, ArtifactHelper helper, String mimetype, boolean overwrite, boolean upload) throws IOException { + Map attributes = recognizer.extractMetaData(artifact); + Map tags = new HashMap(); + + helper.checkAttributes(attributes); + + URL location; + if (upload) { + location = upload(artifact, mimetype); + } + else { + location = artifact; + } + + attributes.put(ArtifactObject.KEY_URL, location.toString()); + attributes.put(ArtifactObject.KEY_ARTIFACT_DESCRIPTION, ""); + if (overwrite) { + attributes.put(ArtifactObject.KEY_MIMETYPE, mimetype); + } + + return create(attributes, tags); + } + + /** + * Helper method which checks a given URL for 'validity', that is, does this URL point + * to something that can be read. + * @param artifact A URL pointing to an artifact. + * @throws IllegalArgumentException when the URL does not point to a valid file. + */ + + private void checkURL(URL artifact) throws IllegalArgumentException { + // First, check whether we can actually reach something from this URL. + InputStream is = null; + try { + is = artifact.openStream(); + } + catch (IOException ioe) { + throw new IllegalArgumentException("Artifact " + artifact.toString() + "does not point to a valid file."); + } + finally { + if (is != null) { + try { + is.close(); + } + catch (IOException ioe) { + // Too bad, nothing to do. + } + } + } + + // Then, check whether the name is legal. + for (byte b : artifact.toString().substring(artifact.toString().lastIndexOf('/') + 1).getBytes()) { + if (!(((b >= 'A') && (b <= 'Z')) || ((b >= 'a') && (b <= 'z')) || ((b >= '0') && (b <= '9')) || (b == '.') || (b == '-') || (b == '_'))) { + throw new IllegalArgumentException("Artifact " + artifact.toString() + "'s name contains an illegal character '" + new String(new byte[] {b}) + "'"); + } + } + + } + + /** + * Uploads an artifact to the OBR. + * @param artifact URL pointing to the local artifact. + * @param mimetype The mimetype of this artifact. + * @return The persistent URL of this artifact. + * @throws IOException for any problem uploading the artifact. + */ + private URL upload(URL artifact, String mimetype) throws IOException { + if (m_obrBase == null) { + throw new IOException("There is no storage available for this artifact."); + } + + InputStream input = null; + OutputStream output = null; + URL url = null; + try { + input = artifact.openStream(); + url = new URL(m_obrBase, new File(artifact.getFile()).getName()); + URLConnection connection = url.openConnection(); + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setRequestProperty("Content-Type", mimetype); + output = connection.getOutputStream(); + byte[] buffer = new byte[4 * 1024]; + for (int count = input.read(buffer); count != -1; count = input.read(buffer)) { + output.write(buffer, 0, count); + } + output.close(); + if (connection instanceof HttpURLConnection) { + int responseCode = ((HttpURLConnection) connection).getResponseCode(); + switch (responseCode) { + case HttpURLConnection.HTTP_OK : + break; + case HttpURLConnection.HTTP_CONFLICT: + throw new IOException("Artifact already exists in storage."); + case HttpURLConnection.HTTP_INTERNAL_ERROR: + throw new IOException("The storage server returned an internal server error."); + default: + throw new IOException("The storage server returned code " + responseCode + " writing to " + url.toString()); + } + } + } + catch (IOException ioe) { + throw new IOException("Error importing artifact " + artifact.toString() + ": " + ioe.getMessage()); + } + finally { + if (input != null) { + try { + input.close(); + } + catch (Exception ex) { + // Not much we can do + } + } + if (output != null) { + try { + output.close(); + } + catch (Exception ex) { + // Not much we can do + } + } + } + + return url; + } + + public void setObrBase(URL obrBase) { + m_obrBase = obrBase; + } + + public String preprocessArtifact(ArtifactObject artifact, GatewayObject gateway, String gatewayID, String version) throws IOException { + ArtifactPreprocessor preprocessor = getHelper(artifact.getMimetype()).getPreprocessor(); + if (preprocessor == null) { + return artifact.getURL(); + } + else { + return preprocessor.preprocess(artifact.getURL(), new GatewayPropertyResolver(gateway), gatewayID, version, m_obrBase); + } + } + + public boolean needsNewVersion(ArtifactObject artifact, GatewayObject gateway, String gatewayID, String fromVersion) { + ArtifactPreprocessor preprocessor = getHelper(artifact.getMimetype()).getPreprocessor(); + if (preprocessor == null) { + return false; + } + else { + return preprocessor.needsNewVersion(artifact.getURL(), new GatewayPropertyResolver(gateway), gatewayID, fromVersion); + } + } + + private static class GatewayPropertyResolver implements PropertyResolver { + + private final GatewayObject m_go; + + public GatewayPropertyResolver(GatewayObject go) { + m_go = go; + } + + public String get(String key) { + return get(key, m_go); + } + + private String get(String key, RepositoryObject ro) { + // Is it in this object? + String result = findKeyInObject(ro, key); + if (result != null) { + return result; + } + + // Is it in one of the children? + List children = getChildren(ro); + for (RepositoryObject child : children) { + result = findKeyInObject(child, key); + if (result != null) { + return result; + } + } + + // Not found yet? then continue to the next level recursively. + for (RepositoryObject child : children) { + result = get(key, child); + if (result != null) { + return result; + } + } + return result; + } + + private List getChildren(RepositoryObject ob) { + if (ob instanceof GatewayObject) { + return ((GatewayObject) ob).getLicenses(); + } + else if (ob instanceof LicenseObject) { + return ((LicenseObject) ob).getGroups(); + } + else if (ob instanceof GroupObject) { + return ((GroupObject) ob).getArtifacts(); + } + return new ArrayList(); + } + + private String findKeyInObject(RepositoryObject ro, String key) { + String result; + if ((result = ro.getAttribute(key)) != null) { + return result; + } + if ((result = ro.getTag(key)) != null) { + return result; + } + return null; + } + + } + + public URL getObrBase() { + return m_obrBase; + } + +} Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/AssociationImpl.java URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/AssociationImpl.java?rev=788992&view=auto ============================================================================== --- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/AssociationImpl.java (added) +++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/AssociationImpl.java Sat Jun 27 15:53:04 2009 @@ -0,0 +1,234 @@ +package net.luminis.liq.client.repository.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import net.luminis.liq.client.repository.Associatable; +import net.luminis.liq.client.repository.Association; +import net.luminis.liq.client.repository.RepositoryObject; + +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.service.event.Event; + +import com.thoughtworks.xstream.io.HierarchicalStreamReader; + +/** + * A basic implementation of the Association interface. Implements 'common' behavior for all associations. + * + * The association allows up to m-to-n associations. Each end of the association gets + * a filter to match objects, possibly a cardinality (if no cardinality is specified, + * 1 is assumed), and, if there can be more matches to the filter than the given cardinality, + * a comparator should be provided in the constructor. + * + * @param The type of the RepositoryObject on the left side of this association + * @param The type of the RepositoryObject on the right side of this association + * @param The non-generic Association interface this object should use. + */ +public class AssociationImpl> extends RepositoryObjectImpl implements Association { + + /* These lists are volatile, since we use copy-on-write semantics for + * updating them. + */ + private volatile List m_left = new ArrayList(); + private volatile List m_right = new ArrayList(); + private final Object m_lock = new Object(); + + private final Filter m_filterLeft; + private final Filter m_filterRight; + + private final ObjectRepositoryImpl m_leftRepository; + private final ObjectRepositoryImpl m_rightRepository; + private final Class m_leftClass; + private final Class m_rightClass; + + /** + * Constructor intended for deserialization. For most parameters, see below. + * @param reader a stream reader which contains an XML representation of this object's contents. + */ + public AssociationImpl(HierarchicalStreamReader reader, ChangeNotifier notifier, Class leftClass, Class rightClass, Comparator leftComparator, Comparator rightComparator, ObjectRepositoryImpl leftRepository, ObjectRepositoryImpl rightRepository, String xmlNode) throws InvalidSyntaxException { + this(readMap(reader), notifier, leftClass, rightClass, leftRepository, rightRepository, xmlNode); + } + + /** + * Basic constructor for AssociationImpl. + * @param attributes A map of attributes. This should at least contain Association.LEFT_ENDPOINT and Association.RIGHT_ENDPOINT, + * and optionally Association.LEFT_CARDINALITY and Association.RIGHT_CARDINALITY. + * @param notifier An instance of the event admin + * @param leftClass The class on the left side of this association. + * @param rightClass The class on the right side of this association. + * @param leftRepository The repository which holds object of leftClass. + * @param rightRepository The repository which holds object of rightClass. + * @param xmlNode The tag by which this object is known in the XML representation. + * @throws InvalidSyntaxException Thrown when the attributes contain an invalidly constructed filter string. + */ + public AssociationImpl(Map attributes, ChangeNotifier notifier, Class leftClass, Class rightClass, ObjectRepositoryImpl leftRepository, ObjectRepositoryImpl rightRepository, String xmlNode) throws InvalidSyntaxException { + super(attributes, notifier, xmlNode); + + if ((getAttribute(LEFT_CARDINALITY) != null) && (Integer.parseInt(getAttribute(LEFT_CARDINALITY)) < 1)) { + throw new IllegalArgumentException("The left cardinality should be 1 or greater."); + } + if ((getAttribute(RIGHT_CARDINALITY) != null) && (Integer.parseInt(getAttribute(RIGHT_CARDINALITY)) < 1)) { + throw new IllegalArgumentException("The right cardinality should be 1 or greater."); + } + + m_leftClass = leftClass; + m_rightClass = rightClass; + m_leftRepository = leftRepository; + m_rightRepository = rightRepository; + + m_filterLeft = m_leftRepository.createFilter(getAttribute(Association.LEFT_ENDPOINT)); + m_filterRight = m_rightRepository.createFilter(getAttribute(Association.RIGHT_ENDPOINT)); + + locateLeftEndpoint(false); + locateRightEndpoint(false); + } + + public AssociationImpl(Map attributes, Map tags, ChangeNotifier notifier, Class leftClass, Class rightClass, ObjectRepositoryImpl leftRepository, ObjectRepositoryImpl rightRepository, String xmlNode) throws InvalidSyntaxException { + super(attributes, tags, notifier, xmlNode); + + if ((getAttribute(LEFT_CARDINALITY) != null) && (Integer.parseInt(getAttribute(LEFT_CARDINALITY)) < 1)) { + throw new IllegalArgumentException("The left cardinality should be 1 or greater."); + } + if ((getAttribute(RIGHT_CARDINALITY) != null) && (Integer.parseInt(getAttribute(RIGHT_CARDINALITY)) < 1)) { + throw new IllegalArgumentException("The right cardinality should be 1 or greater."); + } + + m_leftClass = leftClass; + m_rightClass = rightClass; + m_leftRepository = leftRepository; + m_rightRepository = rightRepository; + + m_filterLeft = m_leftRepository.createFilter(getAttribute(Association.LEFT_ENDPOINT)); + m_filterRight = m_rightRepository.createFilter(getAttribute(Association.RIGHT_ENDPOINT)); + + locateLeftEndpoint(false); + locateRightEndpoint(false); + } + + public List getTo(Associatable from) { + if (m_left.contains(from)) { + return new ArrayList(m_right); + } + if (m_right.contains(from)) { + return new ArrayList(m_left); + } + return null; + } + + public List getLeft() { + return new ArrayList(m_left); + } + + public List getRight() { + return new ArrayList(m_right); + } + + public void remove() { + for (L l : m_left) { + l.remove(this, m_rightClass); + } + for (R r : m_right) { + r.remove(this, m_leftClass); + } + } + + /** + * Locates the most suited endpoint of one side of this association. If the corresponding filter + * matches multiple objects, the comparator will be used to find the most suited one. + * The association will register itself with a new endpoint, and remove itself from the old one. + * @param (only used for type matching). + * @param objectRepositoryImpl The repository where the endpoint should come from. + * @param filter A filter string, used to get candidate-endpoints from objectRepositoryImpl. + * @param endpoint The current endpoint. + * @param comparator A comparator, used when there are multiple potential endpoints. + * @param clazz The class of the 'other side' of this association. + * @return The most suited endpoint; this could be equal to endpoint. + */ + @SuppressWarnings("unchecked") + private List locateEndpoint(ObjectRepositoryImpl objectRepositoryImpl, Filter filter, List endpoints, int cardinality, Class clazz, boolean notify) { + List candidates = objectRepositoryImpl.get(filter); + List newEndpoints = new ArrayList(); + List oldEndpoints = new ArrayList(endpoints); + + if (candidates.size() > cardinality) { + Comparator comparator = candidates.get(0).getComparator(); + if (comparator != null) { + Collections.sort(candidates, comparator); + } + else { + throw new NullPointerException("Filter '" + filter.toString() + "' has resulted in multiple candidates, so the RepositoryObject descendents should have provide a comparator, which they do not."); + } + } + + for (int i = 0; (i < cardinality) && !candidates.isEmpty(); i++) { + TYPE current = candidates.remove(0); + newEndpoints.add(current); + + if (!oldEndpoints.remove(current)) { + current.add(this, clazz); + } + } + + for (TYPE e : oldEndpoints) { + e.remove(this, clazz); + } + + if (!endpoints.equals(newEndpoints)) { + Properties props = new Properties(); + props.put(EVENT_OLD, new ArrayList(endpoints)); + props.put(EVENT_NEW, new ArrayList(newEndpoints)); + if (notify) { + notifyChanged(props); + } + } + + return newEndpoints; + } + + /** + * Locates the left endpoint by using the generic locateEndpoint and notifies + * listeners of changes, if any. + * @param notify Indicates whether notifications should be sent out. + */ + private void locateLeftEndpoint(boolean notify) { + synchronized (m_lock) { + m_left = locateEndpoint(m_leftRepository, m_filterLeft, m_left, (getAttribute(LEFT_CARDINALITY) == null ? 1 : Integer.parseInt(getAttribute(LEFT_CARDINALITY))), m_rightClass, notify); + } + } + + /** + * Locates the right endpoint by using the generic locateEndpoint and notifies + * listeners of changes, if any. + * @param notify Indicates whether notifications should be sent out. + */ + private void locateRightEndpoint(boolean notify) { + synchronized (m_lock) { + m_right = locateEndpoint(m_rightRepository, m_filterRight, m_right, (getAttribute(RIGHT_CARDINALITY) == null ? 1 : Integer.parseInt(getAttribute(RIGHT_CARDINALITY))), m_leftClass, notify); + } + } + + public boolean isSatisfied() { + return (!m_left.isEmpty()) && (!m_right.isEmpty()); + } + + @Override + public void handleEvent(Event event) { + // We get a topic which ends in '/*', but the event contains a specialized topic. + // for now, we chop of the star, and check whether the topic starts with that. + RepositoryObject entity = (RepositoryObject) event.getProperty(RepositoryObject.EVENT_ENTITY); + if ((event.getTopic().endsWith("ADDED")) || event.getTopic().endsWith("REMOVED")) { + if (m_leftClass.isInstance(entity) && m_filterLeft.match(entity.getDictionary())) { + locateLeftEndpoint(true); + } + if (m_rightClass.isInstance(entity) && m_filterRight.match(entity.getDictionary())) { + locateRightEndpoint(true); + } + } + } + +}