All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.dev.component.ChainStorage Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.dev.component;


import com.tangosol.dev.assembler.ClassFile;

import com.tangosol.util.Base;
import com.tangosol.util.ErrorList;
import com.tangosol.util.StringTable;
import com.tangosol.util.WrapperException;

import java.io.IOException;

import java.util.Arrays;

/**
* ChainStorage is a Storage implementation backed by two other Storage
* implementations.
* In the case of "overriding" chain, the delta is used for first-chance
* reads and all writes.  The base is used when an item is not present in the delta.
* In the case of "modifying" chain, the delta is used to read and store the
* component modifications.
*
* @version 1.00, 04/29/99
* @author  Cameron Purdy
*/
public class ChainStorage
        extends    Base
        implements Storage
    {
    // ----- constructors ---------------------------------------------------

    /**
    * Create a chained storage object backed by two storage objects.
    *
    * @param base      the base (secondary) storage
    * @param delta     the delta (primary)  storage
    */
    public ChainStorage(Storage base, Storage delta)
        {
        this(base, delta, false);
        }

    /**
    * Create a chained storage object backed by two storage objects.
    *
    * @param base      the base (secondary) storage
    * @param delta     the delta (primary)  storage
    * @param fOverride true if the delta "overrides" the base;
    *                  false if the delta "modifies" the base
    */
    public ChainStorage(Storage base, Storage delta, boolean fOverride)
        {
        if (base == null || delta == null)
            {
            throw new IllegalArgumentException(CLASS +
                    "Storage implementations must be non-null!");
            }

        m_base      = base;
        m_delta     = delta;
        m_fOverride = fOverride;
        }


    // ----- Components -----------------------------------------------------

    /**
    * Load the specified Component.
    *
    * @param sName      fully qualified Component Definition name
    * @param fReadOnly  true if the loaded component will be read-only
    * @param errlist    the ErrorList object to log any derivation/
    *                   modification errors to
    *
    * @return the specified Component Definition or null
    *
    * @exception ComponentException  if an unrecoverable error occurs
    */
    public Component loadComponent(String sName, boolean fReadOnly, ErrorList errlist)
            throws ComponentException
        {
        if (Trait.DEBUG)
            {
            out();
            out("***ChainStorage*** loading: " + sName + " @" + toString());
            }

        Component cdBase  = m_base .loadComponent(sName, true, errlist);
        Component cdDelta = m_delta.loadComponent(sName, true, errlist);
        Component cdResult;

        if (m_fOverride || cdBase == null)
            {
            cdResult = cdDelta == null ? cdBase : cdDelta;
            }
        else
            {
            if (cdDelta == null)
                {
                cdDelta = (Component) cdBase.getNullDerivedTrait(null, MODIFICATION);
                }

            int nModeBase  = cdBase .getMode();
            int nModeDelta = cdDelta.getMode();
            switch (nModeDelta)
                {
                case DERIVATION:
                    if (nModeBase == RESOLVED)
                        {
                        break;
                        }
                    if (nModeBase == DERIVATION)
                        {
                        // both base and delta exist, meaning that
                        // a) the component [derivation] exists in more than one storage.
                        // b) there is a diamond shaped tree of storages
                        //                   S0
                        //                S1    S2
                        //                   S3
                        // and the component originates in S0.
                        //
                        // For Signatures we will ignore the collision; for the Components
                        // we will ignore the collision if the components are equal and
                        // issue a warning otherwise
                        if (cdDelta.isComponent() && !cdDelta.equals(cdBase))
                            {
                            // there is at least one scenario where the components
                            // appear to be unequal because finalizeResolve has not
                            // yet been issued; verify that is _not_ the case
                            Component cdBaseClone;
                            Component cdDeltaClone;
                            try
                                {
                                cdBaseClone  = (Component) cdBase .clone();
                                cdDeltaClone = (Component) cdDelta.clone();
                                }
                            catch (CloneNotSupportedException e)
                                {
                                // never happens for global components
                                throw new WrapperException(e);
                                }

                            cdBaseClone .finalizeResolve(this, null);
                            cdDeltaClone.finalizeResolve(this, null);
                            if (!cdDeltaClone.equals(cdBaseClone))
                                {
                                // TODO: use cdDelta.logError
                                String sMsg = "During resolution it was necessary to discard " +
                                    "the base Derivation information for \"" + sName +
                                    "\" from " + getRightmostStorage(m_base) + " in favor of " +
                                    "Derivation information from " + getRightmostStorage(m_delta);
                                if (errlist == null)
                                    {
                                    out(sMsg);
                                    }
                                else
                                    {
                                    errlist.addWarning(sMsg);
                                    }
                                }
                            }

                        cdBase = null;
                        break;
                        }
                    // fall through
                case RESOLVED:
                    // the only resolved components are "Root" or "java.lang.Object"
                    if (nModeBase == RESOLVED)
                        {
                        cdBase = null;
                        break;
                        }
                    // TODO: cdDelta.logError()
                    String sMsg = "During resolution it was necessary to discard " +
                        "the base Derivation information for \"" + sName +
                        "\" from " + getRightmostStorage(m_base) + " in favor of " +
                        (nModeDelta == DERIVATION ? "Derivation" : "Modification") +
                        "  information from " + getRightmostStorage(m_delta);
                    if (errlist == null)
                        {
                        out(sMsg);
                        }
                    else
                        {
                        errlist.addWarning(sMsg);
                        }
                    cdBase = null;
                }

            if (Trait.DEBUG)
                {
                out();
                out("***ChainStorage*** Component Base before resolve:");
                cdBase.dump();
                out();
                out("***ChainStorage*** Component Delta before resolve:");
                cdDelta.dump();
                }

            cdResult = cdBase == null ? cdDelta : cdBase.resolve(cdDelta, this, errlist);

            if (Trait.DEBUG)
                {
                out();
                out("***ChainStorage*** Component Result after resolve:");
                cdResult.dump();
                }
            }

        if (cdResult != null)
            {
            cdResult.setModifiable(!fReadOnly);
            }

        return cdResult;
        }

    /**
    * Store the specified Component.
    *
    * @param cd       the Component Definition to be stored
    * @param errlist  the ErrorList object to log any derivation/modification
    *                 errors to
    *
    * @exception ComponentException  if an unrecoverable error occurs
    */
    public void storeComponent(Component cd, ErrorList errlist)
            throws ComponentException
        {
        if (Trait.DEBUG)
            {
            out();
            out("***ChainStorage*** storing: " + cd + " @" + toString());
            }

        String    sName  = cd.getQualifiedName();
        Component cdBase = m_base.loadComponent(sName, true, errlist);
        Component cdDelta;

        if (m_fOverride || cdBase == null)
            {
            cdDelta = cd;

            if (cdBase != null)
                {
                // Delta storage is on override, meaning that it is
                // a persistent storage and no other extracts will follow
                // However, since persistent storage is responsible
                // for calling finalizeExtrace itself, we will do it
                // on a clone instead of the actual component

                try
                    {
                    cd = (Component) cd.clone();
                    }
                catch (CloneNotSupportedException e) {}

                cd.finalizeExtract(this, errlist);

                if (cd.equals(cdBase))
                    {
                    try
                        {
                        m_delta.removeComponent(sName);
                        return;
                        }
                    catch (ComponentException e)
                        {
                        }
                    }
                }
            }
        else
            {
            if (Trait.DEBUG)
                {
                out();
                out("***ChainStorage*** Component Base before extract:");
                cdBase.dump();
                out();
                out("***ChainStorage*** Component Passed before extract:");
                cd.dump();
                }

            cdDelta = cd.extract(cdBase, this, errlist);
            if (Trait.DEBUG)
                {
                out();
                out("***ChainStorage*** Component Delta after extract:");
                cdDelta.dump();
                }
            }

        m_delta.storeComponent(cdDelta, errlist);
        }

    /**
    * Remove the specified Component.
    *
    * @param sName fully qualified Component Definition name
    *
    * @exception ComponentException  if an unrecoverable error occurs
    */
    public void removeComponent(String sName)
            throws ComponentException
        {
        m_delta.removeComponent(sName);
        }


    // ----- Java Class Signatures ------------------------------------------

    /**
    * Load the specified Class Signature.
    *
    * @param sName    qualified Java Class Signature (JCS) name
    *
    * @return the specified Class Signature
    *
    * @exception ComponentException  if an unrecoverable error occurs
    */
    public Component loadSignature(String sName)
            throws ComponentException
        {
        Component cdJCS = m_delta.loadSignature(sName);
        if (cdJCS == null)
            {
            cdJCS = m_base.loadSignature(sName);
            }
        return cdJCS;
        }

    /**
    * Store the specified generated Java Class Signature.
    *
    * @param cdJCS  the Java Class Signature
    *
    * @exception ComponentException  if an unrecoverable error occurs
    */
    public void storeSignature(Component cdJCS)
            throws ComponentException
        {
        String    sName     = cdJCS.getQualifiedName();
        Component cdJCSBase = null;
        try
            {
            cdJCSBase = m_base.loadSignature(sName);
            }
        catch (ComponentException e)
            {
            }

        if (cdJCSBase != null && cdJCSBase.equals(cdJCS))
            {
            // Signatures are persisted exremely rarely (in-house testing only),
            // so there is no reason to complicate the API with "removeSignature".
            // Just make sure that unnecessary duplicate doesn't get created
            try
                {
                if (m_delta.loadSignature(sName) == null)
                    {
                    // Don't create a duplicate
                    return;
                    }
                }
            catch (ComponentException e)
                {
                }
            }

        m_delta.storeSignature(cdJCS);
        }


    // ----- Classes --------------------------------------------------------

    /**
    * Load the original (before any customization takes place) Java Class.
    *
    * @param sName  fully qualified Java Class name
    *
    * @return the specified Class structure
    *
    * @exception ComponentException  if an unrecoverable error occurs
    */
    public ClassFile loadOriginalClass(String sName)
            throws ComponentException
        {
        ClassFile clz = m_delta.loadOriginalClass(sName);
        return    clz == null ? m_base.loadOriginalClass(sName) : clz;
        }

    /**
    * Load the specified generated Java Class.
    *
    * @param sName  fully qualified Java Class name
    *
    * @return the specified Class structure
    *
    * @exception ComponentException  if an unrecoverable error occurs
    */
    public ClassFile loadClass(String sName)
            throws ComponentException
        {
        ClassFile clz = m_delta.loadClass(sName);
        return    clz == null ? m_base.loadClass(sName) : clz;
        }

    /**
    * Store the specified generated Java Class along with its listing
    *
    * @param clz       the generated Class structure to store
    * @param sListing  (optional) the java listing of the class
    *
    * @exception ComponentException  if an unrecoverable error occurs
    */
    public void storeClass(ClassFile clz, String sListing)
            throws ComponentException
        {
        String    sName   = clz.getName();
        ClassFile clzBase = null;
        try
            {
            clzBase = m_base.loadClass(sName);
            }
        catch (ComponentException e)
            {
            }

        if (clzBase != null && clzBase.equals(clz))
            {
            // TODO: remove from the delta storage

            try
                {
                if (m_delta.loadClass(sName) == null)
                    {
                    // Don't create a new duplicate
                    return;
                    }
                }
            catch (ComponentException e)
                {
                }
            }

        m_delta.storeClass(clz, sListing);
        }


    // ----- Java -------------------------------------------------------------

    /**
    * Load the source code for the specified (original) Java class.
    *
    * @param sName  fully qualified Java Class name
    *
    * @return the specified Java source code as a String
    *
    * @exception IOException  if an unrecoverable error occurs
    */
    public String loadJava(String sName)
            throws IOException
        {
        String sScript = m_delta.loadJava(sName);
        return sScript == null ? m_base.loadJava(sName) : sScript;
        }


    // ----- Resources --------------------------------------------------------

    /**
    * Load the original (before any customization takes place) resource.
    *
    * @param sName  fully qualified resource name
    *
    * @return the specified resource as a byte array
    *
    * @exception IOException  if an unrecoverable error occurs
    */
    public byte[] loadOriginalResource(String sName)
            throws IOException
        {
        byte[] ab = m_delta.loadOriginalResource(sName);
        return ab == null ? m_base.loadOriginalResource(sName) : ab;
        }

    /**
    * Load the Resource Signature.
    *
    * @param sName  fully qualified resource name
    *
    * @return the specified Resource Signature as a byte array
    *
    * @exception IOException  if an unrecoverable error occurs
    */
    public byte[] loadResourceSignature(String sName)
            throws IOException
        {
        byte[] abBase  = m_base .loadResourceSignature(sName);
        byte[] abDelta = m_delta.loadResourceSignature(sName);
        byte[] abResult;

        if (m_fOverride || abBase == null)
            {
            abResult = abDelta == null ? abBase : abDelta;
            }
        else
            {
            abResult = getResourceHandler(sName).resolve(abBase, abDelta);
            }
        return abResult;
        }

    /**
    * Store the specified Resource Signature.
    *
    * @param sName  fully qualified resource name
    * @param abData the specified Resource Signature as a byte array
    *
    * @exception IOException  if an unrecoverable error occurs
    */
    public void storeResourceSignature(String sName, byte[] abData)
            throws IOException
        {
        byte[] abBase = m_base.loadResourceSignature(sName);
        byte[] abDelta;

        if (m_fOverride || abBase == null)
            {
            if (Arrays.equals(abData, abBase))
                {
                try
                    {
                    m_delta.removeResourceSignature(sName);
                    return;
                    }
                catch (IOException e)
                    {
                    }
                }
            abDelta = abData;
            }
        else
            {
            abDelta = getResourceHandler(sName).extract(abData, abBase);
            }

        m_delta.storeResourceSignature(sName, abDelta);
        }

    /**
    * Remove the specified Resource Signature
    *
    * @param sName fully qualified resource name
    *
    * @exception IOException  if an unrecoverable error occurs
    */
    public void removeResourceSignature(String sName)
            throws IOException
        {
        m_delta.removeResourceSignature(sName);
        }

    /**
    * Load the generated resource.
    *
    * @param sName  fully qualified resource name
    *
    * @return the specified resource as a byte array
    *
    * @exception IOException  if an unrecoverable error occurs
    *
    */
    public byte[] loadResource(String sName)
            throws IOException
        {
        byte[] ab = m_delta.loadResource(sName);
        return ab == null ? m_base.loadResource(sName) : ab;
        }

    /**
    * Store the specified resource.
    *
    * @param sName  fully qualified resource name
    * @param abData the specified resource as a byte array
    *
    * @exception IOException  if an unrecoverable error occurs
    *
    */
    public void storeResource(String sName, byte[] abData)
            throws IOException
        {
        // We need to store the resource regardless of the base
        // since it's going to be used by an external tool

        /*
        byte[] abBase = null;
        try
            {
            abBase = m_base.loadResource(sName);
            }
        catch (IOException e)
            {
            }

        if (abBase != null && Arrays.equals(abBase, abData))
            {
            // TODO: remove from the delta storage

            try
                {
                if (m_delta.loadResource(sName) == null)
                    {
                    // Don't create a new duplicate
                    return;
                    }
                }
            catch (IOException e)
                {
                }
            }
        */
        m_delta.storeResource(sName, abData);
        }

    // ---- component management --------------------------------------------

    /**
    * Return a StringTable which contains the names of Component Definitions
    * (CD) that derive from the specified Component Definition
    *
    * @param  sComponent  the qualified CD name
    * @param  fQualify    if set to true, return fully qualified CD names;
    *                     otherwise -- non-qualified names
    *
    * @return StringTable object with Component Definition names as keys
    */
    public StringTable getSubComponents(String sComponent, boolean fQualify)
        {
        StringTable tblBase  = m_base .getSubComponents(sComponent, fQualify);
        StringTable tblDelta = m_delta.getSubComponents(sComponent, fQualify);
        return merge(tblBase, tblDelta);
        }

    /**
    * Return a StringTable which contains the names of Component Definitions
    * (CD) that belong to the specified package
    *
    * @param  sPackage  the qualified package name
    * @param  fQualify  if set to true, return fully qualified CD names;
    *                   otherwise -- non-qualified names
    *
    * @return StringTable object with CD names as keys
    */
    public StringTable getPackageComponents(String sPackage, boolean fQualify)
        {
        StringTable tblBase  = m_base .getPackageComponents(sPackage, fQualify);
        StringTable tblDelta = m_delta.getPackageComponents(sPackage, fQualify);
        return merge(tblBase, tblDelta);
        }

    /**
    * Return a StringTable which contains the names of sub-packages in
    * the specified component package
    *
    * @param sPackage   the qualified package name; pass null to retrieve
    *                   the top level component packages
    * @param fQualify   if set to true, return fully qualified package names;
    *                   otherwise -- non-qualified names
    * @param fSubs      if set to true, returns the entire tree of sub-packages
    *
    * @return StringTable object with package names as keys
    */
    public StringTable getComponentPackages(String sPackage, boolean fQualify, boolean fSubs)
        {
        if (fSubs)
            {
            fQualify = true;
            }

        StringTable tblBase  = m_base .getComponentPackages(sPackage, fQualify, fSubs);
        StringTable tblDelta = m_delta.getComponentPackages(sPackage, fQualify, fSubs);
        return merge(tblBase, tblDelta);
        }

    /**
    * Return a StringTable which contains the names of Java Class Signature
    * (JCS) names that belong to the specified java class package
    * (i.e. "javax.swing")
    *
    * @param sPackage   the qualified package name
    * @param fQualify   if set to true, return fully qualified JCS names;
    *                   otherwise -- non-qualified names
    *
    * @return StringTable object with JCS names as keys
    */
    public StringTable getPackageSignatures(String sPackage, boolean fQualify)
        {
        StringTable tblBase  = m_base .getPackageSignatures(sPackage, fQualify);
        StringTable tblDelta = m_delta.getPackageSignatures(sPackage, fQualify);
        return merge(tblBase, tblDelta);
        }

    /**
    * Return a StringTable which contains the names of sub-packages in
    * the specified java class package
    * (i.e. "javax.swing" is a sub-package of "javax" package)
    *
    * @param sPackage   the qualified package name; pass null to retrieve
    *                   the top level java class packages
    * @param fQualify   if set to true, return fully qualified JCS names;
    *                   otherwise -- non-qualified names
    * @param fSubs      if set to true, returns the entire tree of sub-packages
    *
    * @return StringTable object with package names as keys
    */
    public StringTable getSignaturePackages(String sPackage, boolean fQualify, boolean fSubs)
        {
        if (fSubs)
            {
            fQualify = true;
            }

        StringTable tblBase  = m_base .getSignaturePackages(sPackage, fQualify, fSubs);
        StringTable tblDelta = m_delta.getSignaturePackages(sPackage, fQualify, fSubs);
        return merge(tblBase, tblDelta);
        }

    /**
    * Return a StringTable which contains the names of resource
    * names that belong to the specified package
    * (i.e. "img/tde")
    *
    * @param sPackage   the qualified package name
    * @param fQualify   if set to true, return fully qualified resource names;
    *                   otherwise -- non-qualified names
    *
    * @return StringTable object with resource names as keys
    */
    public StringTable getPackageResources(String sPackage, boolean fQualify)
        {
        StringTable tblBase  = m_base .getPackageResources(sPackage, fQualify);
        StringTable tblDelta = m_delta.getPackageResources(sPackage, fQualify);
        return merge(tblBase, tblDelta);
        }


    /**
    * Return a StringTable which contains the names of sub-packages in
    * the specified resource package
    * (i.e. "img/tde" is a sub-package of "img" package)
    *
    * @param sPackage   the qualified package name; pass null to retrieve
    *                   the top level resource packages
    * @param fQualify   if set to true, return fully qualified package names;
    *                   otherwise -- non-qualified names
    * @param fSubs      if set to true, returns the entire tree of sub-packages
    *
    * @return StringTable object with package names as keys
    */
    public StringTable getResourcePackages(String sPackage, boolean fQualify, boolean fSubs)
        {
        if (fSubs)
            {
            fQualify = true;
            }

        StringTable tblBase  = m_base .getResourcePackages(sPackage, fQualify, fSubs);
        StringTable tblDelta = m_delta.getResourcePackages(sPackage, fQualify, fSubs);
        return merge(tblBase, tblDelta);
        }

    /**
    * Provide a short human-readable description of the trait.
    *
    * @return a human-readable description of this trait
    */
    public String toString()
        {
        return CLASS +
            '(' + m_base + (m_fOverride ? "," : ":") + m_delta + ')';
        }

    // ----- helpers

    protected StringTable merge(StringTable tblBase, StringTable tblDelta)
        {
        // since the StringTables carry the locator objects, we have to ensure
        // that the delta locators override the base locators

        if (tblDelta.isEmpty())
            {
            return tblBase;
            }
        if (tblBase.isEmpty())
            {
            return tblDelta;
            }
        else if (tblBase.getSize() > tblDelta.getSize())
            {
            tblBase.putAll(tblDelta);
            return tblBase;
            }
        else
            {
            tblDelta.addAll(tblBase);
            return tblDelta;
            }
        }


    /**
    * Return the rightmost terminal storage in a tree of storages
    * startin at the specified storage
    */
    private Storage getRightmostStorage(Storage storage)
        {
        if (storage instanceof ChainStorage)
            {
            ChainStorage chain = (ChainStorage) storage;

            storage = chain.getRightmostStorage(chain.m_delta);
            }
        return storage;
        }

    // ---- resource resolution support ------------------------------------

    public interface ResourceHandler
        {
        /**
        * Resolve the resource using the specified base and delta
        */
        public byte[] resolve(byte[] abBase, byte[] abDelta)
                throws IOException;

        /**
        * Extract the delta for a resource using the resulting resource and the base
        */
        public byte[] extract(byte[] abResult, byte[] abBase)
                throws IOException;
        }

    public class SimpleHandler implements ResourceHandler
        {
        public byte[] resolve(byte[] abBase, byte[] abDelta)
            {
            return abDelta == null ? abBase : abDelta;
            }
        public byte[] extract(byte[] abResult, byte[] abBase)
            {
            return Arrays.equals(abResult, abBase) ? null : abResult;
            }
        }

    /**
    * Return a ResourceHandler for the specified resource name
    */
    protected ResourceHandler getResourceHandler(String sName)
        {
        return new SimpleHandler();
        }

    // ---- data members ---------------------------------------------------

    /**
    * The name of this class.
    */
    private static final String CLASS = "ChainStorage";

    /**
    * The storage which provides the base component definitions to be modified.
    */
    private Storage m_base;

    /**
    * The storage which provided the delta component definitions.
    */
    private Storage m_delta;

    /**
    * The flag that specifies whether this chain is an "override" or
    * a "supplement" (i.e. "version" versus "customization")
    */
    private boolean m_fOverride;
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy