Package org.apache.muse.ws.resource.properties.impl

Source Code of org.apache.muse.ws.resource.properties.impl.SimpleResourcePropertyCollection

/*=============================================================================*
*  Copyright 2006 The Apache Software Foundation
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*=============================================================================*/

package org.apache.muse.ws.resource.properties.impl;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.xml.namespace.QName;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


import org.apache.muse.util.MultiMap;
import org.apache.muse.util.messages.Messages;
import org.apache.muse.util.messages.MessagesFactory;
import org.apache.muse.util.xml.XmlUtils;
import org.apache.muse.ws.resource.WsResourceCapability;
import org.apache.muse.ws.resource.basefaults.BaseFault;
import org.apache.muse.ws.resource.metadata.MetadataDescriptor;
import org.apache.muse.ws.resource.metadata.faults.MetadataValidationFault;
import org.apache.muse.ws.resource.metadata.impl.ExternalChangeApprover;
import org.apache.muse.ws.resource.metadata.impl.InsertOnlyApprover;
import org.apache.muse.ws.resource.metadata.impl.ReadOnlyApprover;
import org.apache.muse.ws.resource.metadata.impl.StaticValuesApprover;
import org.apache.muse.ws.resource.metadata.impl.ValidValuesApprover;
import org.apache.muse.ws.resource.properties.ResourcePropertyCollection;
import org.apache.muse.ws.resource.properties.get.faults.InvalidResourcePropertyQNameFault;
import org.apache.muse.ws.resource.properties.listeners.PropertyChangeApprover;
import org.apache.muse.ws.resource.properties.listeners.PropertyChangeListener;
import org.apache.muse.ws.resource.properties.listeners.PropertyReadListener;
import org.apache.muse.ws.resource.properties.schema.ResourcePropertiesSchema;
import org.apache.muse.ws.resource.properties.schema.faults.SchemaValidationFault;
import org.apache.muse.ws.resource.properties.set.SetRequest;
import org.apache.muse.ws.resource.properties.set.SetRequestComponent;
import org.apache.muse.ws.resource.properties.set.faults.UnableToModifyResourcePropertyFault;
import org.apache.muse.ws.resource.properties.set.faults.UnableToPutResourcePropertyDocumentFault;
import org.apache.muse.ws.resource.properties.set.impl.InsertRequest;
import org.apache.muse.ws.resource.properties.set.impl.SimpleSetRequest;
import org.apache.muse.ws.resource.properties.set.impl.UpdateRequest;

/**
*
* SimpleResourcePropertyCollection is Muse's default implementation of the
* WS-RF state model. It delegates the handling of actual WS-RP methods to
* capabilities that define the resource properties, serving only to enforce
* WS-RP schema and metadata constraints and the formatting of WS-RP faults.
* It also provides the implementation of the listener framework that allows
* other components to hook into various parts of the WS-RP operations.
*
* @author Dan Jemiolo (danj)
*
*/

public class SimpleResourcePropertyCollection implements ResourcePropertyCollection
{
    //
    // Used to lookup all exception messages
    //
    private static Messages _MESSAGES =
        MessagesFactory.get(SimpleResourcePropertyCollection.class);
   
    //
    // All PropertyChangeApprovers by property name
    //
    private MultiMap _approversByQName = new MultiMap();
   
    //
    // All PropertyChangeListeners by property name
    //
    private MultiMap _listenersByQName = new MultiMap();
   
    //
    // The RMD file that holds the metadata for these properties.
    //
    private MetadataDescriptor _metadata = null;
   
    //
    // All PropertyReadListeners by property name
    //
    private MultiMap _readersByQName = new MultiMap();
   
    //
    // The schema that describes the property types and their constraints.
    //
    private ResourcePropertiesSchema _schema = null;
   
    //
    // The security token allows other components in the application to
    // modify read-only properties that are not accessible by clients.
    // Properties that are read-only may still be mutable (or appendable),
    // which means that they may change, but cannot be explicitly modified
    // by consumers.
    //
    // Our security token is just a unique reference (which cannot be
    // duplicated within the JVM).
    //
    private Object _securityToken = new Object();
   
    public synchronized void addCapability(WsResourceCapability capability)
    {
        QName[] names = capability.getPropertyNames();
        ResourcePropertiesSchema schema = getSchema();
       
        for (int n = 0; n < names.length; ++n)
            schema.setCapability(names[n], capability);
    }
   
    public synchronized final void addChangeApprover(PropertyChangeApprover approver)
    {
        if (approver == null)
            throw new NullPointerException(_MESSAGES.get("NullPCA"));
       
        QName qname = approver.getPropertyName();
       
        if (!hasPropertyDefinition(qname))
        {
            Object[] filler = { qname };
            throw new IllegalArgumentException(_MESSAGES.get("PropertyNotInSchema", filler));
        }
       
        _approversByQName.put(qname, approver);
    }
   
    public synchronized final void addChangeListener(PropertyChangeListener listener)
    {
        if (listener == null)
            throw new NullPointerException(_MESSAGES.get("NullPCL"));

        QName qname = listener.getPropertyName();
       
        if (!hasPropertyDefinition(qname))
        {
            Object[] filler = { qname };
            throw new IllegalArgumentException(_MESSAGES.get("PropertyNotInSchema", filler));
        }
       
        _listenersByQName.put(qname, listener);
    }
   
    protected void addInitialValues(QName qname)
    {
        Collection initialValues = getMetadata().getInitialValues(qname);
        Iterator i = initialValues.iterator();
       
        //
        // insert all the initial values (if any)
        //
        try
        {
            while (i.hasNext())
                insertResourceProperty(qname, new Object[]{ i.next() });
        }
       
        //
        // if we get an error inserting a "required" value,
        // something is totally awry - throw unchecked
        //
        catch (BaseFault fault)
        {
            throw new RuntimeException(fault.getMessage(), fault);
        }
    }
   
    protected void addPermissions(QName qname)
    {
        MetadataDescriptor metadata = getMetadata();
       
        //
        // make sure read-only properties aren't modified from the
        // outside (can still be mutable inside)
        //
        boolean readOnly = metadata.isReadOnlyExternal(qname);
        PropertyChangeApprover security = new ExternalChangeApprover(qname, readOnly);
        security.setSecurityToken(getSecurityToken());
        addChangeApprover(security);
       
        //
        // if we can't update, we're either appendable (insert only)
        // or constant (read only). we need an enforcer!
        //
        if (!metadata.canUpdate(qname))
        {
            PropertyChangeApprover approver = null;
           
            //
            // insert only
            //
            if (metadata.canInsert(qname))
                approver = new InsertOnlyApprover(qname);
           
            //
            // read only
            //
            else
                approver = new ReadOnlyApprover(qname);
           
            addChangeApprover(approver);
        }
    }

    public synchronized final void addReadListener(PropertyReadListener listener)
    {
        if (listener == null)
            throw new NullPointerException(_MESSAGES.get("NullPRL"));

        QName qname = listener.getPropertyName();
       
        if (!hasPropertyDefinition(qname))
        {
            Object[] filler = { qname };
            throw new IllegalArgumentException(_MESSAGES.get("PropertyNotInSchema", filler));
        }
       
        _readersByQName.put(qname, listener);
    }

    protected void addStaticValues(QName qname)
    {
        Collection staticValues = getMetadata().getStaticValues(qname);
       
        //
        // make sure instances that are StaticValues aren't deleted
        //
        if (!staticValues.isEmpty())
        {
            PropertyChangeApprover staticValuesListener =
                new StaticValuesApprover(qname, _metadata);
            addChangeApprover(staticValuesListener);
                   
            Iterator i = staticValues.iterator();
           
            //
            // insert all the static (required) values
            //
            try
            {
                while (i.hasNext())
                    insertResourceProperty(qname, new Object[]{ i.next() });
            }
           
            //
            // if we get an error inserting a "required" value,
            // something is totally awry - throw unchecked
            //
            catch (BaseFault fault)
            {
                throw new RuntimeException(fault.getMessage(), fault);
            }
        }
    }

    protected void addValidValues(QName qname)
    {
        //
        // make sure only ValidValues are inserted (if any)
        //
        // NOTE: this does NOT handle ValidValueRange. the user will
        //       need to provide a PCA for that, because it is highly-
        //       dependent on the semantics of the property type.
        //
        addChangeApprover(new ValidValuesApprover(qname, getMetadata()));
    }

    public synchronized void applyMetadata()
    {
        if (getMetadata() == null)
            throw new IllegalStateException(_MESSAGES.get("NoMetadata"));
       
        Iterator i = getMetadata().getPropertyNames().iterator();
       
        //
        // for each property in the RMD, read its constraints and add
        // approvers that enforce those constraints
        //
        while (i.hasNext())
        {
            QName qname = (QName)i.next();
           
            addInitialValues(qname);
            addStaticValues(qname);
            addValidValues(qname);
            addPermissions(qname);
        }
    }
   
    /**
     *
     * Reports the completed property change to all PropertyChangeListeners.
     * If one of the listeners fails and throws an exception, the method is
     * over - it will not report the change to all remaining listeners
     *
     * @param qname
     * @param oldValue
     * @param newValue
     *
     * @throws BaseFault
     *         <ul>
     *         <li>If one of the listeners fails while responding to the
     *         change; this does not indicate that the change was invalid,
     *         just that the listener's reaction failed.</li>
     *         </ul>
     *
     */
    protected final void changeCompleted(QName qname, Element oldValue, Element newValue)
        throws BaseFault
    {
        if (qname == null)
            throw new NullPointerException(_MESSAGES.get("NullQName"));
       
        Collection listeners = (Collection)_listenersByQName.get(qname);
       
        if (listeners != null)
        {
            Iterator i = listeners.iterator();
           
            while (i.hasNext())
            {
                PropertyChangeListener listener = (PropertyChangeListener)i.next();
                listener.propertyChanged(oldValue, newValue);
            }
        }
    }
   
    /**
     *
     * Reports the pending property change to all PropertyChangeApprovers.
     * As soon as one approver cancels the change by throwing an exception,
     * the method is over - it does not report to all approvers once one
     * has objected.
     *
     * @param qname
     * @param oldValue
     * @param newValue
     * @param securityToken
     *
     * @throws BaseFault
     *         <ul>
     *         <li>If one of the approvers does not approve!</li>
     *         </ul>
     *
     */
    protected final void changeRequested(QName qname,
                                         Element oldValue,
                                         Element newValue,
                                         Object securityToken)
        throws BaseFault
    {
        if (qname == null)
            throw new NullPointerException(_MESSAGES.get("NullQName"));
       
        Collection approvers = (Collection)_approversByQName.get(qname);
       
        if (approvers != null)
        {
            Iterator i = approvers.iterator();
           
            while (i.hasNext())
            {
                PropertyChangeApprover approver =
                    (PropertyChangeApprover)i.next();
                approver.validateChange(oldValue, newValue, securityToken);
            }
        }
    }
   
    protected SetRequest createInsertRequests(Element newDoc)
        throws UnableToPutResourcePropertyDocumentFault, BaseFault
    {
        //
        // Process each top level property from root of doc
        //
        Element[] properties = XmlUtils.getAllElements(newDoc);
        SetRequest set = new SimpleSetRequest();

        for (int i = 0; i < properties.length; i++)
        {
            QName propQName = XmlUtils.getElementQName(properties[i]);
           
            if (getMetadata().isReadOnlyExternal(propQName))
            {
                Object[] filler = { propQName };
                throw new UnableToPutResourcePropertyDocumentFault(_MESSAGES.get("PutRPDocReadOnlyError", filler));
            }
           
            SetRequestComponent insertComp = new InsertRequest(propQName, properties[i]);
            set.addRequestComponent(insertComp);        
        }
       
        return set;
    }
   
    protected void deleteMutableProperties()
        throws BaseFault
    {
        Iterator i = getPropertyNames().iterator();
        MetadataDescriptor metadata = getMetadata();
       
        while (i.hasNext())
        {
            QName propName = (QName)i.next();
           
            if (metadata.canDelete(propName) && !metadata.isReadOnlyExternal(propName))
            {
                //
                // check if there are any instances to delete
                //
                Element[] instances = getResourceProperty(propName);
               
                if (instances.length > 0)               
                    deleteResourceProperty(propName, getSecurityToken());
            }
        }      
    }
   
    public void deleteResourceProperty(QName qname)
        throws BaseFault
    {
        deleteResourceProperty(qname, getSecurityToken());
    }
   
    public synchronized void deleteResourceProperty(QName qname, Object securityToken)
        throws BaseFault
    {
        if (qname == null)
            throw new NullPointerException(_MESSAGES.get("NullQName"));
       
        //
        // find all properties with the given name...
        //
        Element[] results = getResourceProperty(qname);
       
        if (results.length == 0)
        {
            Object[] filler = { qname };
            throw new UnableToModifyResourcePropertyFault(_MESSAGES.get("NoInstancesToDelete", filler));
        }
       
        //
        // make sure deletion is okay from a SCHEMA point-of-view. this
        // does not validate deletion from a read-only/immutable. that
        // is done by PCAs
        //
        validateDelete(qname, results.length, results.length);

        //
        // inform approvers of the pending deletion - this could
        // throw and cancel the deletion
        //
        for (int n = 0; n < results.length; ++n)
            changeRequested(qname, results[n], null, securityToken);
       
        WsResourceCapability capability = getCapability(qname);
        capability.deleteProperty(qname);
       
        //
        // inform listeners of deletion
        //
        for (int n = 0; n < results.length; ++n)
            changeCompleted(qname, results[n], null);
    }
   
    public WsResourceCapability getCapability(QName qname)
    {
        return getSchema().getCapability(qname);
    }
   
    public synchronized final Iterator getChangeApprovers(QName property)
    {
        Collection approvers = (Collection)_approversByQName.get(property);
        return approvers.iterator();
    }
   
    public synchronized final Iterator getChangeListeners(QName property)
    {
        Collection listeners = (Collection)_listenersByQName.get(property);
        return listeners.iterator();
    }
   
    public synchronized final MetadataDescriptor getMetadata()
    {
        return _metadata;
    }
   
    /**
     *
     * {@inheritDoc}
     * <br><br>
     * This method returns the actual Elements that are stored in the
     * underlying DOM Document - modifying them will modify the WS-RP
     * document. Be careful!
     *
     */
    public synchronized Element[] getMultipleResourceProperties(QName[] qnames)
        throws InvalidResourcePropertyQNameFault, BaseFault
    {
        if (qnames == null)
            throw new NullPointerException(_MESSAGES.get("NullQNameArray"));
       
        //
        // first get all instances of each property named...
        //
        Element[][] results = new Element[qnames.length][];
        int total = 0;
       
        for (int n = 0; n < qnames.length; ++n)
        {
            results[n] = getResourceProperty(qnames[n]);
            total += results[n].length;
        }
       
        //
        // ...then copy them into one array to return
        //
        Element[] oneBigArray = new Element[total];
       
        //
        // all property instances are copied in order, so all instances
        // of the same name are together
        //
        for (int n = 0, current = 0; n < results.length; ++n)
        {
            System.arraycopy(results[n], 0, oneBigArray, current, results[n].length);
            current += results[n].length;
        }
       
        return oneBigArray;
    }
   
    /**
     *
     * {@inheritDoc}
     * <br><br>
     * Unlike getResourceProperty(QName), the values returned from this
     * method have been transformed from Elements to POJOs and do <b>not</b>
     * represent the values in the underlying DOM Document. The implementation
     * uses the Serializers registered with Muse to parse the properties.
     *
     * @see #getResourceProperty(QName)
     *
     * @see WsrpUtils#convertToObjects(Element[], Class)
     *
     */
    public synchronized Object getPropertyAsObject(QName qname, Class type)
        throws BaseFault
    {
        Element[] properties = getResourceProperty(qname);
        return WsrpUtils.convertToObjects(properties, type);
    }
   
    public synchronized Collection getPropertyNames()
    {
        return getSchema().getPropertyNames();
    }
   
    public synchronized final Iterator getReadListeners(QName property)
    {
        Collection readers = (Collection)_readersByQName.get(property);
        return readers.iterator();
    }
   
    /**
     *
     * {@inheritDoc}
     * <br><br>
     * This method returns the actual Elements that are stored in the
     * underlying DOM Document - modifying them will modify the WS-RP
     * document. Be careful!
     * <br><br>
     * Users who want more type safety and better encapsulation should
     * use getPropertyAsObject(QName, Class).
     *
     * @see #getPropertyAsObject(QName, Class)
     *
     */
    public synchronized Element[] getResourceProperty(QName qname)
        throws InvalidResourcePropertyQNameFault, BaseFault
    {
        if (qname == null)
            throw new NullPointerException(_MESSAGES.get("NullQName"));
       
        //
        // it's invalid to read a property whose type is undefined
        //
        // NOTE: this is DIFFERENT from reading a property whose
        //       type is undefined but currently has zero instances
        //       
        if (!hasPropertyDefinition(qname))
        {
            Object[] filler = { qname };
            throw new InvalidResourcePropertyQNameFault(_MESSAGES.get("PropertyNotInSchema", filler));
        }
       
        WsResourceCapability capability = getCapability(qname);
        Element[] results = capability.getProperty(qname);
       
        //
        // let listeners/updaters know of the read request - the first
        // listener will have no values to work with
        //
        return readRequested(qname, results);
    }
   
    public synchronized Element getResourcePropertyDocument()
        throws BaseFault
    {
        Document doc = XmlUtils.createDocument();
        QName rootName = getSchema().getElementName();
        Element root = XmlUtils.createElement(doc, rootName);
        doc.appendChild(root);
       
        Collection names = getPropertyNames();
        QName[] namesAsArray = new QName[names.size()];
        namesAsArray = (QName[])names.toArray(namesAsArray);
       
        Element[] values = getMultipleResourceProperties(namesAsArray);
       
        for (int n = 0; n < values.length; ++n)
        {
            values[n] = (Element)doc.importNode(values[n], true);
            root.appendChild(values[n]);
        }
       
        return root;
    }
   
    public synchronized final ResourcePropertiesSchema getSchema()
    {
        return _schema;
    }

    public Object getSecurityToken()
    {
        return _securityToken;
    }
   
    public boolean hasPropertyDefinition(QName qname)
    {
        if (_schema == null)
            throw new IllegalStateException(_MESSAGES.get("NoSchema"));
       
        return _schema.hasProperty(qname);
    }
   
    public synchronized void insertOrUpdate(QName property, Object value)
        throws BaseFault
    {
        insertOrUpdate(property, new Object[]{ value });
    }
   
    public synchronized void insertOrUpdate(QName property, Object[] values)
        throws BaseFault
    {
        if (getResourceProperty(property).length == 0)
            insertResourceProperty(property, values);
       
        else
            updateResourceProperty(property, values);
    }
   
    public synchronized void insertResourceProperty(QName qname, Object[] values)
        throws BaseFault
    {
        insertResourceProperty(qname, values, getSecurityToken());
    }
   
    public synchronized void insertResourceProperty(QName qname,
                                                    Object[] values,
                                                    Object securityToken)
        throws BaseFault
    {
        if (qname == null)
            throw new NullPointerException(_MESSAGES.get("NullQName"));
       
        if (values == null)
            throw new NullPointerException(_MESSAGES.get("NullValuesArray"));
       
        if (values.length == 0)
            throw new IllegalArgumentException(_MESSAGES.get("EmptyValuesArray"));
       
        //
        // find all current instances of the property...
        //       
        Element[] current = getResourceProperty(qname);

        InsertRequest request = new InsertRequest(qname, values);
        Element[] valuesXML = request.getValues();
       
        //
        // make sure that the insert is valid according to the SCHEMA
        //
        validateInsert(qname, current.length, valuesXML);
       
        for (int n = 0; n < valuesXML.length; ++n)
            changeRequested(qname, null, valuesXML[n], securityToken);

        WsResourceCapability capability = getCapability(qname);
        capability.insertProperty(qname, valuesXML);
       
        for (int n = 0; n < valuesXML.length; ++n)
            changeCompleted(qname, null, valuesXML[n]);
    }
   
    public synchronized Element putResourcePropertyDocument(Element newDoc)
        throws UnableToPutResourcePropertyDocumentFault, BaseFault
    {
        if( newDoc == null)
            throw new NullPointerException(_MESSAGES.get("NullRPDoc"));
       
        try
        {
            //
            // entire rp doc is replaced by this op, therefore all properties,
            // except read-only properties, are deleted first
            //
            deleteMutableProperties()
         
            //
            // break the operation down into multiple set request insert components
            //
            SetRequest insertRequests = createInsertRequests(newDoc);    
            setResourceProperties(insertRequests);
        }
       
        catch (BaseFault fault)
        {
            throw new UnableToPutResourcePropertyDocumentFault(_MESSAGES.get("PutRPDocError", new Object[] {fault.getMessage()}));
        }
       
        //
        // if final representation and intended representation are not the same,
        // return the document on response
        //
        Element finalDoc = getResourcePropertyDocument();
       
        //
        // strip all white space text nodes directly under root of new doc before doing comparison, as
        // in WS-RP we don't care about white space under the root.
        //
        Element wsFreeNewDoc = stripWhiteSpaceChildren(newDoc);
       
        return XmlUtils.equals(wsFreeNewDoc, finalDoc) ? null : finalDoc;
    }
   
       
    /**
     *
     * Reports all property read requests to the PropertyReadListeners. If
     * one of the listeners fails and throws an exception, the method is
     * over - it will not report to all remaining listeners once one has
     * failed. Listeners will receive the actual values that will be given
     * to the caller, so they can modify them if desired.
     *
     * @param qname
     *        The name of the property being read.
     *
     * @param properties
     *        The current values of the property, which will be given to
     *        the caller once all listeners have been invoked.
     *
     * @throws BaseFault
     *         <ul>
     *         <li>If one of the listeners fails.</li>
     *         </ul>
     *
     */
    protected final Element[] readRequested(QName qname, Element[] properties)
        throws BaseFault
    {
        if (qname == null)
            throw new NullPointerException(_MESSAGES.get("NullQName"));
       
        Collection readers = (Collection)_readersByQName.get(qname);
       
        if (readers != null)
        {
            Iterator i = readers.iterator();
           
            while (i.hasNext())
            {
                PropertyReadListener reader = (PropertyReadListener)i.next();
                properties = reader.readRequested(properties);
            }
        }
       
        return properties;
    }
   
    public synchronized final void removeChangeApprover(PropertyChangeApprover approver)
    {
        if (approver == null)
            throw new NullPointerException(_MESSAGES.get("NullPCA"));
       
        removeItem(_approversByQName, approver.getPropertyName(), approver);
    }
       
    public synchronized final void removeChangeListener(PropertyChangeListener listener)
    {
        if (listener == null)
            throw new NullPointerException(_MESSAGES.get("NullPCL"));       

        removeItem(_listenersByQName, listener.getPropertyName(), listener);
    }
   
    /**
     *
     * Removes the given value from the collection of values associated with
     * the given key. If removing the value results in the key having no
     * more entries associated with it, the key itself is removed.
     *
     * @param map
     * @param key
     * @param value
     *
     */
    private void removeItem(MultiMap map, Object key, Object value)
    {
        Collection all = (Collection)map.get(key);
        all.remove(value);
       
        if (all.isEmpty())
            map.remove(key);
    }
   
    public synchronized final void removeReadListener(PropertyReadListener listener)
    {
        if (listener == null)
            throw new NullPointerException(_MESSAGES.get("NullPRL"));
       
        removeItem(_readersByQName, listener.getPropertyName(), listener);
    }
   
    public synchronized void setMetadata(MetadataDescriptor metadata)
    {
        if (metadata == null)
            throw new NullPointerException(_MESSAGES.get("NullMetadataDescriptor"));
       
        _metadata = metadata;
    }
   
    public synchronized void setResourceProperties(SetRequest request)
        throws BaseFault
    {
        List ops = request.getRequestComponents();
       
        //
        // we must have at least one set operation
        //
        if (ops.isEmpty())
            throw new IllegalArgumentException(_MESSAGES.get("EmptySetRequest"));
       
        //
        // execute each operation in order
        //
        // NOTE: This is not a transaction system, so if one operation
        //       fails in the middle, the operations that have been
        //       performed to that point are not rolled back. This is
        //       consistent with the WS-RP spec but may not be adequate
        //       for building manageable resources.
        //
        Iterator i = ops.iterator();
       
        while (i.hasNext())
        {
            SetRequestComponent next = (SetRequestComponent)i.next();
            next.execute(this);
        }
    }

    public synchronized void setSchema(ResourcePropertiesSchema schema)
    {
        if (schema == null)
            throw new NullPointerException(_MESSAGES.get("NullSchema"));
       
        _schema = schema;
    }

    /**
     *
     * Given an element, removes all direct white space children nodes
     *
     */
    private Element stripWhiteSpaceChildren(Element root)
    {
        NodeList children = root.getChildNodes();

        for (int i = 0; i < children.getLength(); i++)
        {
            Node child = children.item(i);
           
            if (child.getNodeType() == Node.TEXT_NODE && child.getNodeValue().trim().equals(""))
                child.getParentNode().removeChild(child);
        }

        return root;
    }
   
    /**
     *
     * @return An XML representation of the current WS-RP document, without
     *         the XML header.
     *
     */
    public synchronized String toString()
    {
        return XmlUtils.toString(toXML(), false);
    }
   
    public synchronized Element toXML()
    {
        try
        {
            return getResourcePropertyDocument();
        }
       
        catch (BaseFault error)
        {
            throw new RuntimeException(error.getMessage(), error);
        }
    }
   
    /**
     *
     * @return A <b>copy</b> of the underlying DOM Document that is used to
     *         store the properties.
     *
     */
    public synchronized Element toXML(Document factory)
    {
        try
        {
            Element wsrp = getResourcePropertyDocument();
            return (Element)factory.importNode(wsrp, true);
        }
       
        catch (BaseFault error)
        {
            throw new RuntimeException(error.getMessage(), error);
        }
    }
   
    public synchronized void updateResourceProperty(QName qname, Object[] values)
        throws BaseFault
    {
        updateResourceProperty(qname, values, getSecurityToken());
    }
   
    public synchronized void updateResourceProperty(QName qname,
                                                    Object[] values,
                                                    Object securityToken)
        throws BaseFault
    {
        if (qname == null)
            throw new NullPointerException(_MESSAGES.get("NullQName"));
       
        if (values == null)
            throw new NullPointerException(_MESSAGES.get("NullValuesArray"));
       
        //
        // get all current instances of the property
        //
        Element[] current = getResourceProperty(qname);
       
        //
        // we can't update if there are no instances of the property.
        // callers must insert, THEN update
        //
        if (current.length == 0)
        {
            Object[] filler = { qname };
            throw new UnableToModifyResourcePropertyFault(_MESSAGES.get("PropertyNotFound", filler));
        }
       
        UpdateRequest request = new UpdateRequest(qname, values);
        Element[] valuesXML = request.getValues();
       
        //
        // make sure the update is valid according to the SCHEMA - this
        // test assumes we will remove all current values and insert
        // new ones
        //
        validateInsert(qname, 0, valuesXML);
       
        int lcd = Math.min(current.length, valuesXML.length);

        for (int n = 0; n < lcd; ++n)
            changeRequested(qname, current[n], valuesXML[n], securityToken);
       
        WsResourceCapability capability = getCapability(qname);
        capability.updateProperty(qname, valuesXML);

        for (int n = 0; n < lcd; ++n)
            changeCompleted(qname, current[n], valuesXML[n]);
       
        //
        // if there were more copies before the update than after,
        // we need to send deletion notifications about the extras
        //
        for (int n = lcd; n < current.length; ++n)
            changeCompleted(qname, current[n], null);
       
        //
        // the other possibility is that there are more copies after
        // the update than before, so we still have some values to
        // report that are not "replacements"
        //
        for (int n = lcd; n < valuesXML.length; ++n)
            changeCompleted(qname, null, valuesXML[n]);
    }
   
    /**
     *
     * Confirms that deleting the desired number of property instances will
     * result in a WS-RP document that is valid according to the schema.
     *
     * @param qname
     *        The name of the property whose instances are being deleted.
     *
     * @param currentSize
     *        The current number of instances (before the delete).
     *
     * @param toDelete
     *        The number of instances being deleted.
     *
     * @throws BaseFault
     *         <ul>
     *         <li>If the property name is undefined.</li>
     *         <li>If deleting the instances would put the property below
     *         its minimum value ("minOccurs").</li>
     *         </ul>
     *
     */
    protected void validateDelete(QName qname, int currentSize, int toDelete)
        throws BaseFault
    {
        //
        // make sure that deleting 'toDelete' instances won't put us
        // below the minimum
        //
        int min = _schema.getMinOccurs(qname);
       
        if (currentSize - toDelete < min)
        {
            Object[] filler = {
                qname, new Integer(min), new Integer(currentSize - toDelete)
            };
            throw new SchemaValidationFault(_MESSAGES.get("BelowMinimumPotential", filler));
        }
    }
   
    /**
     *
     * Confirms that inserting the desired property instances will result in
     * a WS-RP document that is valid according to the schema.
     *
     * @param qname
     *        The name of the property that is being inserted.
     *
     * @param currentSize
     *        The current number of instances (before the insertion).
     *
     * @param values
     *        The property values to insert into the document.
     *
     * @throws BaseFault
     *         <ul>
     *         <li>If the property name is undefined.</li>
     *         <li>If inserting the instances would put the property above
     *         its maximum value ("maxOccurs").</li>
     *         <li>If any of the values is null, and the property is not
     *         nillable.</li>
     *         </ul>
     *
     */
    protected void validateInsert(QName qname, int currentSize, Element[] values)
        throws BaseFault
    {
        ResourcePropertiesSchema schema = getSchema();
       
        //
        // make sure that adding value.length instances will not put us
        // above the maximum (if any)
        //
        int max = schema.getMaxOccurs(qname);
        int total = currentSize + values.length;
       
        if (!_schema.isMaxUnbounded(qname) && total > max)
        {
            Object[] filler = { qname, new Integer(max), new Integer(total) };
            throw new SchemaValidationFault(_MESSAGES.get("AboveMaximumPotential", filler));
        }
       
        //
        // are we trying to set to null when we shouldn't be?
        //
        boolean isNillable = schema.isNillable(qname);
       
        for (int n = 0; n < values.length; ++n)
        {
            if (!isNillable && !values[n].hasChildNodes())
            {
                Object[] filler = { qname };
                throw new SchemaValidationFault(_MESSAGES.get("NotNillable", filler));
            }
        }
    }
   
    public synchronized void validateMetadata()
        throws BaseFault
    {
        if (_metadata == null)
            throw new IllegalStateException(_MESSAGES.get("NoMetadata"));
       
        Iterator i = _metadata.getPropertyNames().iterator();
       
        //
        // for each wsrmd:Property, make sure it's in the schema and
        // that all current values are valid
        //
        while (i.hasNext())
        {
            QName qname = (QName)i.next();           
            Element[] current = getResourceProperty(qname);
           
            //
            // no instances OR the RMD does not have any metadata - skip
            //
            if (current.length == 0 || !_metadata.hasProperty(qname))
                continue;
           
            //
            // validate current values
            //
            for (int n = 0; n < current.length; ++n)
            {
                if (!_metadata.isValidValue(qname, current[n]))
                {
                    Object[] filler = { qname, XmlUtils.toString(current[n], false) };
                    throw new MetadataValidationFault(_MESSAGES.get("InvalidValue", filler));
                }
            }
        }
    }
   
    public synchronized void validateSchema()
        throws BaseFault
    {       
        //
        // for every property in the schema, check the size constraints
        //
        ResourcePropertiesSchema schema = getSchema();
        Iterator i = schema.getPropertyNames().iterator();
       
        while (i.hasNext())
        {
            QName qname = (QName)i.next();
           
            if (!schema.hasCapability(qname))
            {
                Object[] filler = { qname };
                throw new IllegalStateException(_MESSAGES.get("NoCapabilityForProperty", filler));
            }
           
            int max = schema.getMaxOccurs(qname);
            boolean maxUnbounded = schema.isMaxUnbounded(qname);
           
            int min = schema.getMinOccurs(qname);
           
            boolean isNillable = schema.isNillable(qname);
           
            //
            // get all current instances
            //
            Element[] instances = getResourceProperty(qname);
           
            //
            // minimum number of instances?
            //
            if (instances.length < min)
            {
                Object[] filler = {
                    qname, new Integer(min), new Integer(instances.length)
                };
                throw new SchemaValidationFault(_MESSAGES.get("BelowMinimum", filler));
            }
           
            //
            // under the max?
            //
            if (!maxUnbounded && instances.length > max)
            {
                Object[] filler = {
                    qname, new Integer(min), new Integer(instances.length)
                };
                throw new SchemaValidationFault(_MESSAGES.get("AboveMaximum", filler));
            }
           
            //
            // are any instances null that shouldn't be?
            //
            String message = _MESSAGES.get("NotNillable", new Object[]{ qname });
           
            if (!isNillable)
            {
                for (int n = 0; n < instances.length; ++n)
                {
                    //
                    // check for child elements, attributes, and text, all of
                    // which qualify an element as "not null"
                    //
                    if (!instances[n].hasChildNodes() &&
                        !instances[n].hasAttributes() &&
                        XmlUtils.extractText(instances[n]) == null)
                        throw new SchemaValidationFault(message);
                }
            }
        }
    }
}
TOP

Related Classes of org.apache.muse.ws.resource.properties.impl.SimpleResourcePropertyCollection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.