net.unicon.civis.fac.AbstractCivisFactory.java Source code

Java tutorial

Introduction

Here is the source code for net.unicon.civis.fac.AbstractCivisFactory.java

Source

/*
 * Copyright (C) 2007 Unicon, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this distribution.  It is also available here:
 * http://www.fsf.org/licensing/licenses/gpl.html
 *
 * As a special exception to the terms and conditions of version
 * 2 of the GPL, you may redistribute this Program in connection
 * with Free/Libre and Open Source Software ("FLOSS") applications
 * as described in the GPL FLOSS exception.  You should have received
 * a copy of the text describing the FLOSS exception along with this
 * distribution.
 */

package net.unicon.civis.fac;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.dom4j.Attribute;
import org.dom4j.Element;

import net.unicon.penelope.IDecisionCollection;

import net.unicon.civis.CivisRuntimeException;
import net.unicon.civis.Event;
import net.unicon.civis.ICivisEntity;
import net.unicon.civis.ICivisFactory;
import net.unicon.civis.IEventListener;
import net.unicon.civis.IGroup;
import net.unicon.civis.IPerson;
import net.unicon.civis.ITransaction;
import net.unicon.civis.ITransactionContext;
import net.unicon.civis.ITransactionRegistry;

public abstract class AbstractCivisFactory implements ICivisFactory {

    // Instance Members.
    private final Map listeners = Collections.synchronizedMap(new HashMap());

    /*
     * Public API.
     */

    public static ICivisFactory parse(Element e) {

        // Assertions.
        if (e == null) {
            String msg = "Argument 'e [Element]' cannot be null.";
            throw new IllegalArgumentException(msg);
        }
        if (!e.getName().equals("civis-factory")) {
            String msg = "Argument 'e [Element]' must be a <civis-factory> " + "element.";
            throw new IllegalArgumentException(msg);
        }

        // Impl.
        Attribute p = e.attribute("impl");
        if (p == null) {
            String msg = "Element <civis-factory> is missing required " + "attribute 'impl'.";
            throw new IllegalArgumentException(msg);
        }
        String impl = p.getValue();

        ICivisFactory rslt = null;
        try {

            Class c = Class.forName(impl);
            Method m = c.getDeclaredMethod("parse", new Class[] { Element.class });
            rslt = (ICivisFactory) m.invoke(null, new Object[] { e });

        } catch (Throwable t) {
            String msg = "Unable to evaluate the specified factory:  " + impl;
            throw new RuntimeException(msg, t);
        }

        return rslt;

    }

    public static ICivisFactory fromUrl(String url) {

        // Assertions.
        if (url == null) {
            String msg = "Argument 'url' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        ICivisFactory rslt = null;
        try {

            String[] tokens = url.split("/");
            String className = tokens[2];
            Class c = Class.forName(className);
            Method m = c.getDeclaredMethod("fromUrl", new Class[] { String.class });
            rslt = (ICivisFactory) m.invoke(null, new Object[] { url });

        } catch (Throwable t) {
            String msg = "Unable to evaluate the specified factory:  " + url;
            throw new RuntimeException(msg, t);
        }

        return rslt;

    }

    public static ICivisEntity entityFromUrl(String url) {

        // Assertions.
        if (url == null) {
            String msg = "Argument 'url' cannot be null.";
            throw new IllegalArgumentException(msg);
        }

        ICivisEntity rslt = null;
        try {

            ICivisFactory fac = fromUrl(url);
            String[] tokens = url.split("/");
            tokens = tokens[3].split("@");
            if (tokens.length == 2) {
                if (tokens[0].indexOf("Person") > -1)
                    rslt = fac.getPerson(tokens[1]);
                else
                    rslt = fac.getGroupById(tokens[1]);
            } else {
                throw new Exception();
            }
        } catch (Throwable t) {
            String msg = "Unable to evaluate the specified entity:  " + url;
            throw new RuntimeException(msg, t);
        }

        return rslt;

    }

    public void startListening(Event e, IEventListener listener) {

        // Assertions.
        if (e == null) {
            String msg = "Argument 'e [Event]' cannot be null";
        }
        if (listener == null) {
            String msg = "Argument 'listener' cannot be null";
        }

        // NB:  Using a synchronized set instance here,
        // so no need to micro-manage thread safety.

        Set s = (Set) listeners.get(e);
        if (s == null) {
            s = Collections.synchronizedSet(new HashSet());
            listeners.put(e, s);
        }
        s.add(e);

    }

    public void stopListening(Event e, IEventListener listener) {

        // Assertions.
        if (e == null) {
            String msg = "Argument 'e [Event]' cannot be null";
        }
        if (listener == null) {
            String msg = "Argument 'listener' cannot be null";
        }

        // NB:  Using a synchronized set instance here,
        // so no need to micro-manage thread safety.

        Set s = (Set) listeners.get(e);
        if (s == null) {
            // This is a no-op...
            return;
        }
        s.remove(e);
        if (s.isEmpty()) {
            // Perform a little cleanup...
            listeners.remove(e);
        }

    }

    public ITransaction beginTransaction() {
        return new TransactionImpl();
    }

    public IPerson createPerson(String name, IDecisionCollection attr) {

        // Assertions.
        // NB:  Handled by the overload...

        ITransaction trans = new TransactionImpl();
        IPerson rslt = createPerson(trans, name, attr);
        trans.commit();
        return rslt;

    }

    public IPerson createPerson(ITransaction trans, String name, IDecisionCollection attr) {

        // Assertions.
        if (trans == null) {
            String msg = "Argument 'trans' cannot be null";
        }
        if (name == null) {
            String msg = "Argument 'name' cannot be null";
        }
        if (attr == null) {
            String msg = "Argument 'attr' cannot be null";
        }

        IPerson rslt = this.doCreatePerson(trans, name, attr);

        // Trigger events...
        invokeEvent(Event.PERSON_CREATED, trans, rslt);

        return rslt;

    }

    public void rename(IPerson p, String name) {

        // Assertions.
        // NB:  Handled by the overload...

        ITransaction trans = new TransactionImpl();
        rename(trans, p, name);
        trans.commit();

    }

    public void rename(ITransaction trans, IPerson p, String name) {

        // Assertions.
        if (trans == null) {
            String msg = "Argument 'trans' cannot be null";
        }
        if (p == null) {
            String msg = "Argument 'p [IPerson]' cannot be null";
        }
        if (name == null) {
            String msg = "Argument 'name' cannot be null";
        }

        this.doRename(trans, p, name);

        // Trigger events...
        invokeEvent(Event.PERSON_MODIFIED, trans, p);

    }

    public void updateAttributes(IPerson p, IDecisionCollection attr) {

        // Assertions.
        // NB:  Handled by the overload...

        ITransaction trans = new TransactionImpl();
        updateAttributes(trans, p, attr);
        trans.commit();

    }

    public void updateAttributes(ITransaction trans, IPerson p, IDecisionCollection attr) {

        // Assertions.
        if (trans == null) {
            String msg = "Argument 'trans' cannot be null";
        }
        if (p == null) {
            String msg = "Argument 'p [IPerson]' cannot be null";
        }
        if (attr == null) {
            String msg = "Argument 'attr' cannot be null";
        }

        this.doUpdateAttributes(trans, p, attr);

        // Trigger events...
        invokeEvent(Event.PERSON_MODIFIED, trans, p);

    }

    public void delete(IPerson p) {

        // Assertions.
        // NB:  Handled by the overload...

        ITransaction trans = new TransactionImpl();
        delete(trans, p);
        trans.commit();

    }

    public void delete(ITransaction trans, IPerson p) {

        // Assertions.
        if (trans == null) {
            String msg = "Argument 'trans' cannot be null";
        }
        if (p == null) {
            String msg = "Argument 'p [IPerson]' cannot be null";
        }

        this.doDelete(trans, p);

        // Trigger events...
        invokeEvent(Event.PERSON_DELETED, trans, p);

    }

    public IGroup createGroup(IGroup parent, String name, IDecisionCollection attr) {

        // Assertions.
        // NB:  Handled by the overload...

        ITransaction trans = new TransactionImpl();
        IGroup rslt = createGroup(trans, parent, name, attr);
        trans.commit();
        return rslt;

    }

    public IGroup createGroup(ITransaction trans, IGroup parent, String name, IDecisionCollection attr) {

        // Assertions.
        if (trans == null) {
            String msg = "Argument 'trans' cannot be null";
        }
        if (parent == null) {
            String msg = "Argument 'parent' cannot be null";
        }
        if (name == null) {
            String msg = "Argument 'name' cannot be null";
        }
        if (attr == null) {
            String msg = "Argument 'attr' cannot be null";
        }

        IGroup rslt = this.doCreateGroup(trans, parent, name, attr);

        // Trigger events...
        invokeEvent(Event.GROUP_CREATED, trans, rslt);

        return rslt;

    }

    public void rename(IGroup g, String name) {

        // Assertions.
        // NB:  Handled by the overload...

        ITransaction trans = new TransactionImpl();
        rename(trans, g, name);
        trans.commit();

    }

    public void rename(ITransaction trans, IGroup g, String name) {

        // Assertions.
        if (trans == null) {
            String msg = "Argument 'trans' cannot be null";
        }
        if (g == null) {
            String msg = "Argument 'g [IGroup]' cannot be null";
        }
        if (name == null) {
            String msg = "Argument 'name' cannot be null";
        }

        this.doRename(trans, g, name);

        // Trigger events...
        invokeEvent(Event.GROUP_MODIFIED, trans, g);

    }

    public void updateAttributes(IGroup g, IDecisionCollection attr) {

        // Assertions.
        // NB:  Handled by the overload...

        ITransaction trans = new TransactionImpl();
        updateAttributes(trans, g, attr);
        trans.commit();

    }

    public void updateAttributes(ITransaction trans, IGroup g, IDecisionCollection attr) {

        // Assertions.
        if (trans == null) {
            String msg = "Argument 'trans' cannot be null";
        }
        if (g == null) {
            String msg = "Argument 'g [IGroup]' cannot be null";
        }
        if (attr == null) {
            String msg = "Argument 'attr' cannot be null";
        }

        this.doUpdateAttributes(trans, g, attr);

        // Trigger events...
        invokeEvent(Event.GROUP_MODIFIED, trans, g);

    }

    public void delete(IGroup g) {

        // Assertions.
        // NB:  Handled by the overload...

        ITransaction trans = new TransactionImpl();
        delete(trans, g);
        trans.commit();

    }

    public void delete(ITransaction trans, IGroup g) {

        // Assertions.
        if (trans == null) {
            String msg = "Argument 'trans' cannot be null";
        }
        if (g == null) {
            String msg = "Argument 'g [IGroup]' cannot be null";
        }

        this.doDelete(trans, g);

        // Trigger events...
        invokeEvent(Event.GROUP_DELETED, trans, g);

    }

    public boolean equals(Object o) {
        if (!(o instanceof ICivisFactory))
            return false;
        if (((ICivisFactory) o).getUrl().equals(this.getUrl()))
            return true;
        return false;
    }

    /*
     * Protected API.
     */

    protected abstract IGroup[] getGroups(IPerson p);

    protected abstract IPerson[] getMembers(IGroup g, boolean deep);

    protected abstract IGroup[] getSubgroups(IGroup g);

    protected abstract IGroup[] getDescendentGroups(IGroup g);

    protected abstract IPerson doCreatePerson(ITransaction trans, String name, IDecisionCollection attr);

    protected abstract void doRename(ITransaction trans, IPerson p, String name);

    protected abstract void doUpdateAttributes(ITransaction trans, IPerson p, IDecisionCollection attr);

    protected abstract void doDelete(ITransaction trans, IPerson p);

    protected abstract IGroup doCreateGroup(ITransaction trans, IGroup parent, String name,
            IDecisionCollection attr);

    protected abstract void doRename(ITransaction trans, IGroup g, String name);

    protected abstract void doUpdateAttributes(ITransaction trans, IGroup g, IDecisionCollection attr);

    protected abstract void doDelete(ITransaction trans, IGroup g);

    /*
     * Implementation.
     */

    private void invokeEvent(Event e, ITransaction trans, ICivisEntity target) {

        // Assertions.
        if (e == null) {
            String msg = "Argument 'e [Event]' cannot be null";
        }
        if (trans == null) {
            String msg = "Argument 'trans' cannot be null";
        }
        if (target == null) {
            String msg = "Argument 'target' cannot be null";
        }

        // Trigger events...
        Set s = (Set) listeners.get(e);
        if (s != null) {
            try {
                Iterator it = s.iterator();
                while (it.hasNext()) {
                    IEventListener el = (IEventListener) it.next();
                    el.invoke(((TransactionImpl) trans).getRegistry(), target);
                }
            } catch (Throwable t) {
                String msg = "Civis transaction aborted due to exception in " + "event listener.";
                throw new CivisRuntimeException(msg, t);
            }
        }

    }

    /*
     * Nested Types.
     */

    private static final class TransactionRegistryImpl implements ITransactionRegistry {

        // Instance Members.
        private final Set contexts;

        /*
         * Public API.
         */

        public TransactionRegistryImpl() {

            // Instance Members.
            this.contexts = Collections.synchronizedSet(new HashSet());

        }

        public void registerContext(ITransactionContext ctx) {

            // Assertions.
            if (ctx == null) {
                String msg = "Argument 'ctx' cannot be null";
            }

            contexts.add(ctx);

        }

        public Iterator iterator() {
            return contexts.iterator();
        }

    }

    private static final class TransactionImpl implements ITransaction {

        // Instance Members.
        private final TransactionRegistryImpl registry;
        private boolean completed;

        /*
         * Public API.
         */

        public TransactionImpl() {

            // Instance Members.
            this.registry = new TransactionRegistryImpl();
            this.completed = false;

        }

        public TransactionRegistryImpl getRegistry() {
            return registry;
        }

        public void commit() {

            // Assertions.
            if (completed) {
                String msg = "This transaction is already completed.";
                throw new IllegalStateException(msg);
            }

            Iterator it = registry.iterator();
            while (it.hasNext()) {
                ITransactionContext ctx = (ITransactionContext) it.next();
                ctx.commit();
            }
            completed = true;

        }

        public void rollback() {

            // Assertions.
            if (completed) {
                String msg = "This transaction is already completed.";
                throw new IllegalStateException(msg);
            }

            Iterator it = registry.iterator();
            while (it.hasNext()) {
                ITransactionContext ctx = (ITransactionContext) it.next();
                ctx.rollback();
            }
            completed = true;

        }

    }

    protected static abstract class BaseCivisEntity implements ICivisEntity {

        // Instance Members.
        private final String id;
        private final ICivisFactory owner;
        private String name;
        private IDecisionCollection attributes;

        /*
         * Public API.
         */

        public final String getId() {
            return id;
        }

        public final ICivisFactory getOwner() {
            return owner;
        }

        public final String getName() {
            return name;
        }

        public final IDecisionCollection getAttributes() {
            return attributes;
        }

        public boolean equals(Object o) {
            if (!(o instanceof BaseCivisEntity))
                return false;
            if (((BaseCivisEntity) o).getOwner().equals(this.owner) && ((BaseCivisEntity) o).getId() == this.id)
                return true;
            return false;
        }

        public int compareTo(Object o) {

            int rslt = 0;

            if (!this.equals(o)) {
                if (o instanceof IGroup) {
                    if (this instanceof IGroup) {
                        String[] gPath1 = ((IGroup) this).getPath().split(IGroup.GROUP_PATH_SEPARATOR);
                        String[] gPath2 = ((IGroup) o).getPath().split(IGroup.GROUP_PATH_SEPARATOR);

                        if (gPath1.length == gPath2.length) {
                            rslt = 0;
                        } else if (gPath1.length < gPath2.length) {
                            rslt = 1;
                        } else {
                            rslt = -1;
                        }
                    } else {
                        return -1;
                    }
                } else {
                    if (this instanceof IGroup) {
                        rslt = 1;
                    }
                }
            }
            return rslt;
        }

        /*
         * Protected API.
         */

        protected BaseCivisEntity(String id, ICivisFactory owner, String name, IDecisionCollection attributes) {

            // Assertions.
            if (id == null) {
                String msg = "Argument 'id' cannot be null.";
            }
            if (owner == null) {
                String msg = "Argument 'owner' cannot be null";
            }
            if (name == null) {
                String msg = "Argument 'name' cannot be less than zero.";
            }
            if (attributes == null) {
                String msg = "Argument 'attributes' cannot be less than zero.";
            }

            // Instance Members.
            this.id = id;
            this.owner = owner;
            this.name = name;
            this.attributes = attributes;

        }

    }

    protected static final class PersonImpl extends BaseCivisEntity implements IPerson {

        /*
         * Public API.
         */

        public PersonImpl(String id, ICivisFactory owner, String name, IDecisionCollection attributes) {
            super(id, owner, name, attributes);
        }

        public IGroup[] getGroups() {
            return ((AbstractCivisFactory) getOwner()).getGroups(this);
        }

        public String toXml() {

            // Begin.
            StringBuffer rslt = new StringBuffer();
            rslt.append("<person ");

            // Id.
            rslt.append("id=\"").append(String.valueOf(getId())).append("\">");

            // Label.
            rslt.append("<name>").append(getName()).append("</name>");

            // Attributes.
            rslt.append(getAttributes().toXml());

            // End.
            rslt.append("</person>");
            return rslt.toString();

        }

        public boolean equals(Object o) {
            if (!(o instanceof IPerson))
                return false;
            if (((IPerson) o).getOwner().equals(this.getOwner()) && ((IPerson) o).getName().equals(this.getName()))
                return true;
            return false;
        }

        public String getUrl() {
            StringBuffer buf = new StringBuffer(this.getOwner().getUrl());
            buf.append("/").append(this.getClass().getName());
            buf.append("@").append(this.getName());
            return buf.toString();
        }

    }

    protected static final class GroupImpl extends BaseCivisEntity implements IGroup {

        // instance members
        private final String path;

        /*
         * Public API.
         */

        public GroupImpl(String id, ICivisFactory owner, String name, IDecisionCollection attributes, String path) {
            super(id, owner, name, attributes);
            this.path = path;
        }

        public IPerson[] getMembers(boolean deep) {
            return ((AbstractCivisFactory) getOwner()).getMembers(this, deep);
        }

        public IGroup[] getSubgroups() {
            return ((AbstractCivisFactory) getOwner()).getSubgroups(this);
        }

        public IGroup[] getDescendentGroups() {
            return ((AbstractCivisFactory) getOwner()).getDescendentGroups(this);
        }

        public String toString() {
            return path;
        }

        public boolean equals(Object o) {
            if (!(o instanceof IGroup))
                return false;
            return (((IGroup) o).getOwner().equals(this.getOwner())
                    && ((IGroup) o).getPathAndId().equals(this.getPathAndId()));
        }

        public String toXml() {

            // Begin.
            StringBuffer rslt = new StringBuffer();
            rslt.append("<group ");

            // Id.
            rslt.append("id=\"").append(getId()).append("\">");

            // Label.
            rslt.append("<name>").append(getName()).append("</name>");

            // Attributes.
            rslt.append(getAttributes().toXml());

            // End.
            rslt.append("</group>");
            return rslt.toString();

        }

        public String getPath() {
            return path;
        }

        public String getUrl() {
            StringBuffer buf = new StringBuffer(this.getOwner().getUrl());
            buf.append("/").append(this.getClass().getName());
            buf.append("@").append(this.getId());
            return buf.toString();
        }

        public String getPathAndId() {
            return path + "[" + getId() + "]";
        }

    }

}