org.apache.ignite.internal.managers.deployment.GridDeploymentClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ignite.internal.managers.deployment.GridDeploymentClassLoader.java

Source

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

package org.apache.ignite.internal.managers.deployment;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.configuration.DeploymentMode;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.util.GridBoundedLinkedHashSet;
import org.apache.ignite.internal.util.GridByteArrayList;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteUuid;
import org.jetbrains.annotations.Nullable;
import org.jsr166.ConcurrentHashMap8;

/**
 * Class loader that is able to resolve task subclasses and resources
 * by requesting remote node. Every class that could not be resolved
 * by system class loader will be downloaded from given remote node
 * by task deployment identifier. If identifier has been changed on
 * remote node this class will throw exception.
 */
@SuppressWarnings({ "CustomClassloader" })
class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInfo {
    /** Class loader ID. */
    private final IgniteUuid id;

    /** {@code True} for single node deployment. */
    private final boolean singleNode;

    /** Manager registry. */
    @GridToStringExclude
    private final GridKernalContext ctx;

    /** */
    @GridToStringExclude
    private final IgniteLogger log;

    /** Registered nodes. */
    @GridToStringExclude
    private LinkedList<UUID> nodeList;

    /** Node ID -> Loader ID. */
    @GridToStringInclude
    private Map<UUID, IgniteUuid> nodeLdrMap;

    /** */
    @GridToStringExclude
    private final GridDeploymentCommunication comm;

    /** */
    private final String[] p2pExclude;

    /** P2P timeout. */
    private final long p2pTimeout;

    /** Cache of missed resources names. */
    @GridToStringExclude
    private final GridBoundedLinkedHashSet<String> missedRsrcs;

    /** Map of class byte code if it's not available locally. */
    @GridToStringExclude
    private final ConcurrentMap<String, byte[]> byteMap;

    /** User version. */
    private final String usrVer;

    /** Deployment mode. */
    private final DeploymentMode depMode;

    /** {@code True} to omit any output to INFO or higher. */
    private boolean quiet;

    /** */
    private final Object mux = new Object();

    /**
     * Creates a new peer class loader.
     * <p>
     * If there is a security manager, its
     * {@link SecurityManager#checkCreateClassLoader()}
     * method is invoked. This may result in a security exception.
     *
     * @param id Class loader ID.
     * @param usrVer User version.
     * @param depMode Deployment mode.
     * @param singleNode {@code True} for single node.
     * @param ctx Kernal context.
     * @param parent Parent class loader.
     * @param clsLdrId Remote class loader identifier.
     * @param nodeId ID of node that have initiated task.
     * @param comm Communication manager loader will work through.
     * @param p2pTimeout Timeout for class-loading requests.
     * @param log Logger.
     * @param p2pExclude List of P2P loaded packages.
     * @param missedResourcesCacheSize Size of the missed resources cache.
     * @param clsBytesCacheEnabled Flag to enable class byte cache.
     * @param quiet {@code True} to omit output to log.
     * @throws SecurityException If a security manager exists and its
     *      {@code checkCreateClassLoader} method doesn't allow creation
     *      of a new class loader.
     */
    GridDeploymentClassLoader(IgniteUuid id, String usrVer, DeploymentMode depMode, boolean singleNode,
            GridKernalContext ctx, ClassLoader parent, IgniteUuid clsLdrId, UUID nodeId,
            GridDeploymentCommunication comm, long p2pTimeout, IgniteLogger log, String[] p2pExclude,
            int missedResourcesCacheSize, boolean clsBytesCacheEnabled, boolean quiet) throws SecurityException {
        super(parent);

        assert id != null;
        assert depMode != null;
        assert ctx != null;
        assert comm != null;
        assert p2pTimeout > 0;
        assert log != null;
        assert clsLdrId != null;
        assert nodeId.equals(clsLdrId.globalId());

        this.id = id;
        this.usrVer = usrVer;
        this.depMode = depMode;
        this.singleNode = singleNode;
        this.ctx = ctx;
        this.comm = comm;
        this.p2pTimeout = p2pTimeout;
        this.log = log;
        this.p2pExclude = p2pExclude;

        nodeList = new LinkedList<>();

        nodeList.add(nodeId);

        Map<UUID, IgniteUuid> map = U.newHashMap(1);

        map.put(nodeId, clsLdrId);

        nodeLdrMap = singleNode ? Collections.unmodifiableMap(map) : map;

        missedRsrcs = missedResourcesCacheSize > 0 ? new GridBoundedLinkedHashSet<String>(missedResourcesCacheSize)
                : null;

        byteMap = clsBytesCacheEnabled ? new ConcurrentHashMap8<String, byte[]>() : null;

        this.quiet = quiet;
    }

    /**
     * Creates a new peer class loader.
     * <p>
     * If there is a security manager, its
     * {@link SecurityManager#checkCreateClassLoader()}
     * method is invoked. This may result in a security exception.
     *
     * @param id Class loader ID.
     * @param usrVer User version.
     * @param depMode Deployment mode.
     * @param singleNode {@code True} for single node.
     * @param ctx Kernal context.
     * @param parent Parent class loader.
     * @param participants Participating nodes class loader map.
     * @param comm Communication manager loader will work through.
     * @param p2pTimeout Timeout for class-loading requests.
     * @param log Logger.
     * @param p2pExclude List of P2P loaded packages.
     * @param missedResourcesCacheSize Size of the missed resources cache.
     * @param clsBytesCacheEnabled Flag to enable class byte cache.
     * @param quiet {@code True} to omit output to log.
     * @throws SecurityException If a security manager exists and its
     *      {@code checkCreateClassLoader} method doesn't allow creation
     *      of a new class loader.
     */
    GridDeploymentClassLoader(IgniteUuid id, String usrVer, DeploymentMode depMode, boolean singleNode,
            GridKernalContext ctx, ClassLoader parent, Map<UUID, IgniteUuid> participants,
            GridDeploymentCommunication comm, long p2pTimeout, IgniteLogger log, String[] p2pExclude,
            int missedResourcesCacheSize, boolean clsBytesCacheEnabled, boolean quiet) throws SecurityException {
        super(parent);

        assert id != null;
        assert depMode != null;
        assert ctx != null;
        assert comm != null;
        assert p2pTimeout > 0;
        assert log != null;
        assert participants != null;

        this.id = id;
        this.usrVer = usrVer;
        this.depMode = depMode;
        this.singleNode = singleNode;
        this.ctx = ctx;
        this.comm = comm;
        this.p2pTimeout = p2pTimeout;
        this.log = log;
        this.p2pExclude = p2pExclude;

        nodeList = new LinkedList<>(participants.keySet());

        nodeLdrMap = new HashMap<>(participants);

        missedRsrcs = missedResourcesCacheSize > 0 ? new GridBoundedLinkedHashSet<String>(missedResourcesCacheSize)
                : null;

        byteMap = clsBytesCacheEnabled ? new ConcurrentHashMap8<String, byte[]>() : null;

        this.quiet = quiet;
    }

    /** {@inheritDoc} */
    @Override
    public IgniteUuid classLoaderId() {
        return id;
    }

    /** {@inheritDoc} */
    @Override
    public DeploymentMode deployMode() {
        return depMode;
    }

    /** {@inheritDoc} */
    @Override
    public String userVersion() {
        return usrVer;
    }

    /** {@inheritDoc} */
    @Override
    public boolean localDeploymentOwner() {
        return false;
    }

    /** {@inheritDoc} */
    @Override
    public long sequenceNumber() {
        return -1;
    }

    /** {@inheritDoc} */
    @Override
    public Map<UUID, IgniteUuid> participants() {
        synchronized (mux) {
            return new HashMap<>(nodeLdrMap);
        }
    }

    /**
     * Adds new node and remote class loader id to this class loader.
     * Class loader will ask all associated nodes for the class/resource
     * until find it.
     *
     * @param nodeId Participating node ID.
     * @param ldrId Participating class loader id.
     */
    void register(UUID nodeId, IgniteUuid ldrId) {
        assert nodeId != null;
        assert ldrId != null;
        assert nodeId.equals(ldrId.globalId());
        assert !singleNode;

        synchronized (mux) {
            if (missedRsrcs != null)
                missedRsrcs.clear();

            /*
             * We need to put passed in node into the
             * first position in the list.
             */

            // 1. Remove passed in node if any.
            nodeList.remove(nodeId);

            // 2. Add passed in node to the first position.
            nodeList.addFirst(nodeId);

            // 3. Put to map.
            nodeLdrMap.put(nodeId, ldrId);
        }
    }

    /**
     * Remove remote node and remote class loader id associated with it from
     * internal map.
     *
     * @param nodeId Participating node ID.
     * @return Removed class loader ID.
     */
    @Nullable
    IgniteUuid unregister(UUID nodeId) {
        assert nodeId != null;

        synchronized (mux) {
            nodeList.remove(nodeId);

            return nodeLdrMap.remove(nodeId);
        }
    }

    /**
     * @return Registered nodes.
     */
    Collection<UUID> registeredNodeIds() {
        synchronized (mux) {
            return new ArrayList<>(nodeList);
        }
    }

    /**
     * @return Registered class loader IDs.
     */
    Collection<IgniteUuid> registeredClassLoaderIds() {
        Collection<IgniteUuid> ldrIds = new LinkedList<>();

        synchronized (mux) {
            for (IgniteUuid ldrId : nodeLdrMap.values())
                ldrIds.add(ldrId);
        }

        return ldrIds;
    }

    /**
     * @param nodeId Node ID.
     * @return Class loader ID for node ID.
     */
    IgniteUuid registeredClassLoaderId(UUID nodeId) {
        synchronized (mux) {
            return nodeLdrMap.get(nodeId);
        }
    }

    /**
     * Checks if node is participating in deployment.
     *
     * @param nodeId Node ID to check.
     * @param ldrId Class loader ID.
     * @return {@code True} if node is participating in deployment.
     */
    boolean hasRegisteredNode(UUID nodeId, IgniteUuid ldrId) {
        assert nodeId != null;
        assert ldrId != null;

        IgniteUuid ldrId0;

        synchronized (mux) {
            ldrId0 = nodeLdrMap.get(nodeId);
        }

        return ldrId0 != null && ldrId0.equals(ldrId);
    }

    /**
     * @return {@code True} if class loader has registered nodes.
     */
    boolean hasRegisteredNodes() {
        synchronized (mux) {
            return !nodeList.isEmpty();
        }
    }

    /**
     * @param name Name of the class.
     * @return {@code True} if locally excluded.
     */
    private boolean isLocallyExcluded(String name) {
        if (p2pExclude != null) {
            for (String path : p2pExclude) {
                // Remove star (*) at the end.
                if (path.endsWith("*"))
                    path = path.substring(0, path.length() - 1);

                if (name.startsWith(path))
                    return true;
            }
        }

        return false;
    }

    /** {@inheritDoc} */
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        assert !Thread.holdsLock(mux);

        // Check if we have package name on list of P2P loaded.
        // ComputeJob must be always loaded locally to avoid
        // any possible class casting issues.
        Class<?> cls = null;

        try {
            if (!"org.apache.ignite.compute.ComputeJob".equals(name)) {
                if (isLocallyExcluded(name))
                    // P2P loaded class.
                    cls = p2pLoadClass(name, true);
            }

            if (cls == null)
                cls = loadClass(name, true);
        } catch (ClassNotFoundException e) {
            throw e;
        }
        // Catch Throwable to secure against any errors resulted from
        // corrupted class definitions or other user errors.
        catch (Exception e) {
            throw new ClassNotFoundException("Failed to load class due to unexpected error: " + name, e);
        }

        return cls;
    }

    /**
     * Loads the class with the specified binary name. The
     * default implementation of this method searches for classes in the
     * following order:
     * <p>
     * <ol>
     * <li> Invoke {@link #findLoadedClass(String)} to check if the class
     * has already been loaded. </li>
     * <li>Invoke the {@link #findClass(String)} method to find the class.</li>
     * </ol>
     * <p> If the class was found using the above steps, and the
     * {@code resolve} flag is true, this method will then invoke the {@link
     * #resolveClass(Class)} method on the resulting {@code Class} object.
     *
     * @param name The binary name of the class.
     * @param resolve If {@code true} then resolve the class.
     * @return The resulting {@code Class} object.
     * @throws ClassNotFoundException If the class could not be found
     */
    @Nullable
    private Class<?> p2pLoadClass(String name, boolean resolve) throws ClassNotFoundException {
        assert !Thread.holdsLock(mux);

        // First, check if the class has already been loaded.
        Class<?> cls = findLoadedClass(name);

        if (cls == null)
            cls = findClass(name);

        if (resolve)
            resolveClass(cls);

        return cls;
    }

    /** {@inheritDoc} */
    @Nullable
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        assert !Thread.holdsLock(mux);

        if (!isLocallyExcluded(name)) {
            // This is done for URI deployment in which case the parent loader
            // does not have the requested resource, but it is still locally
            // available.
            GridDeployment dep = ctx.deploy().getLocalDeployment(name);

            if (dep != null) {
                if (log.isDebugEnabled())
                    log.debug("Found class in local deployment [cls=" + name + ", dep=" + dep + ']');

                return dep.deployedClass(name);
            }
        }

        String path = U.classNameToResourceName(name);

        GridByteArrayList byteSrc = sendClassRequest(name, path);

        synchronized (this) {
            Class<?> cls = findLoadedClass(name);

            if (cls == null) {
                if (byteMap != null)
                    byteMap.put(path, byteSrc.array());

                cls = defineClass(name, byteSrc.internalArray(), 0, byteSrc.size());

                /* Define package in classloader. See URLClassLoader.defineClass(). */
                int i = name.lastIndexOf('.');

                if (i != -1) {
                    String pkgName = name.substring(0, i);

                    if (getPackage(pkgName) == null)
                        // Too much nulls is normal because we don't have package's meta info.
                        definePackage(pkgName, null, null, null, null, null, null, null);
                }
            }

            if (log.isDebugEnabled())
                log.debug("Loaded class [cls=" + name + ", ldr=" + this + ']');

            return cls;
        }
    }

    /**
     * Computes end time based on timeout value passed in.
     *
     * @param timeout Timeout.
     * @return End time.
     */
    private long computeEndTime(long timeout) {
        long endTime = U.currentTimeMillis() + timeout;

        // Account for overflow.
        if (endTime < 0)
            endTime = Long.MAX_VALUE;

        return endTime;
    }

    /**
     * Sends class-loading request to all nodes associated with this class loader.
     *
     * @param name Class name.
     * @param path Class path.
     * @return Class byte source.
     * @throws ClassNotFoundException If class was not found.
     */
    private GridByteArrayList sendClassRequest(String name, String path) throws ClassNotFoundException {
        assert !Thread.holdsLock(mux);

        long endTime = computeEndTime(p2pTimeout);

        Collection<UUID> nodeListCp;
        Map<UUID, IgniteUuid> nodeLdrMapCp;

        synchronized (mux) {
            // Skip requests for the previously missed classes.
            if (missedRsrcs != null && missedRsrcs.contains(path))
                throw new ClassNotFoundException("Failed to peer load class [class=" + name + ", nodeClsLdrIds="
                        + nodeLdrMap + ", parentClsLoader=" + getParent() + ']');

            // If single-node mode, then node cannot change and we simply reuse list and map.
            // Otherwise, make copies that can be used outside synchronization.
            nodeListCp = singleNode ? nodeList : new LinkedList<>(nodeList);
            nodeLdrMapCp = singleNode ? nodeLdrMap : new HashMap<>(nodeLdrMap);
        }

        IgniteCheckedException err = null;

        for (UUID nodeId : nodeListCp) {
            if (nodeId.equals(ctx.discovery().localNode().id()))
                // Skip local node as it is already used as parent class loader.
                continue;

            IgniteUuid ldrId = nodeLdrMapCp.get(nodeId);

            ClusterNode node = ctx.discovery().node(nodeId);

            if (node == null) {
                if (log.isDebugEnabled())
                    log.debug("Found inactive node in class loader (will skip): " + nodeId);

                continue;
            }

            try {
                GridDeploymentResponse res = comm.sendResourceRequest(path, ldrId, node, endTime);

                if (res == null) {
                    String msg = "Failed to send class-loading request to node (is node alive?) [node=" + node.id()
                            + ", clsName=" + name + ", clsPath=" + path + ", clsLdrId=" + ldrId + ", parentClsLdr="
                            + getParent() + ']';

                    if (!quiet)
                        U.warn(log, msg);
                    else if (log.isDebugEnabled())
                        log.debug(msg);

                    err = new IgniteCheckedException(msg);

                    continue;
                }

                if (res.success())
                    return res.byteSource();

                // In case of shared resources/classes all nodes should have it.
                if (log.isDebugEnabled())
                    log.debug("Failed to find class on remote node [class=" + name + ", nodeId=" + node.id()
                            + ", clsLdrId=" + ldrId + ", reason=" + res.errorMessage() + ']');

                synchronized (mux) {
                    if (missedRsrcs != null)
                        missedRsrcs.add(path);
                }

                throw new ClassNotFoundException(
                        "Failed to peer load class [class=" + name + ", nodeClsLdrs=" + nodeLdrMapCp
                                + ", parentClsLoader=" + getParent() + ", reason=" + res.errorMessage() + ']');
            } catch (IgniteCheckedException e) {
                // This thread should be interrupted again in communication if it
                // got interrupted. So we assume that thread can be interrupted
                // by processing cancellation request.
                if (Thread.currentThread().isInterrupted()) {
                    if (!quiet)
                        U.error(log, "Failed to find class probably due to task/job cancellation: " + name, e);
                    else if (log.isDebugEnabled())
                        log.debug("Failed to find class probably due to task/job cancellation [name=" + name
                                + ", err=" + e + ']');
                } else {
                    if (!quiet)
                        U.warn(log,
                                "Failed to send class-loading request to node (is node alive?) [node=" + node.id()
                                        + ", clsName=" + name + ", clsPath=" + path + ", clsLdrId=" + ldrId
                                        + ", parentClsLdr=" + getParent() + ", err=" + e + ']');
                    else if (log.isDebugEnabled())
                        log.debug("Failed to send class-loading request to node (is node alive?) [node=" + node.id()
                                + ", clsName=" + name + ", clsPath=" + path + ", clsLdrId=" + ldrId
                                + ", parentClsLdr=" + getParent() + ", err=" + e + ']');

                    err = e;
                }
            }
        }

        throw new ClassNotFoundException("Failed to peer load class [class=" + name + ", nodeClsLdrs="
                + nodeLdrMapCp + ", parentClsLoader=" + getParent() + ']', err);
    }

    /** {@inheritDoc} */
    @Nullable
    @Override
    public InputStream getResourceAsStream(String name) {
        assert !Thread.holdsLock(mux);

        if (byteMap != null && name.endsWith(".class")) {
            byte[] bytes = byteMap.get(name);

            if (bytes != null) {
                if (log.isDebugEnabled())
                    log.debug("Got class definition from byte code cache: " + name);

                return new ByteArrayInputStream(bytes);
            }
        }

        InputStream in = ClassLoader.getSystemResourceAsStream(name);

        if (in == null)
            in = super.getResourceAsStream(name);

        // Most probably, this is initiated by GridUtils.getUserVersion().
        // No point to send request.
        if ("META-INF/services/org.apache.commons.logging.LogFactory".equalsIgnoreCase(name)) {
            if (log.isDebugEnabled())
                log.debug(
                        "Denied sending remote request for META-INF/services/org.apache.commons.logging.LogFactory.");

            return null;
        }

        if (in == null)
            in = sendResourceRequest(name);

        return in;
    }

    /**
     * Sends resource request to all remote nodes associated with this class loader.
     *
     * @param name Resource name.
     * @return InputStream for resource or {@code null} if resource could not be found.
     */
    @Nullable
    private InputStream sendResourceRequest(String name) {
        assert !Thread.holdsLock(mux);

        long endTime = computeEndTime(p2pTimeout);

        Collection<UUID> nodeListCp;
        Map<UUID, IgniteUuid> nodeLdrMapCp;

        synchronized (mux) {
            // Skip requests for the previously missed classes.
            if (missedRsrcs != null && missedRsrcs.contains(name))
                return null;

            // If single-node mode, then node cannot change and we simply reuse list and map.
            // Otherwise, make copies that can be used outside synchronization.
            nodeListCp = singleNode ? nodeList : new LinkedList<>(nodeList);
            nodeLdrMapCp = singleNode ? nodeLdrMap : new HashMap<>(nodeLdrMap);
        }

        for (UUID nodeId : nodeListCp) {
            if (nodeId.equals(ctx.discovery().localNode().id()))
                // Skip local node as it is already used as parent class loader.
                continue;

            IgniteUuid ldrId = nodeLdrMapCp.get(nodeId);

            ClusterNode node = ctx.discovery().node(nodeId);

            if (node == null) {
                if (log.isDebugEnabled())
                    log.debug("Found inactive node in class loader (will skip): " + nodeId);

                continue;
            }

            try {
                // Request is sent with timeout that is why we can use synchronization here.
                GridDeploymentResponse res = comm.sendResourceRequest(name, ldrId, node, endTime);

                if (res == null) {
                    U.warn(log, "Failed to get resource from node (is node alive?) [nodeId=" + node.id()
                            + ", clsLdrId=" + ldrId + ", resName=" + name + ", parentClsLdr=" + getParent() + ']');
                } else if (!res.success()) {
                    synchronized (mux) {
                        // Cache unsuccessfully loaded resource.
                        if (missedRsrcs != null)
                            missedRsrcs.add(name);
                    }

                    // Some frameworks like Spring often ask for the resources
                    // just in case - none will happen if there are no such
                    // resources. So we print out INFO level message.
                    if (!quiet) {
                        if (log.isInfoEnabled())
                            log.info("Failed to get resource from node [nodeId=" + node.id() + ", clsLdrId=" + ldrId
                                    + ", resName=" + name + ", parentClsLdr=" + getParent() + ", msg="
                                    + res.errorMessage() + ']');
                    } else if (log.isDebugEnabled())
                        log.debug("Failed to get resource from node [nodeId=" + node.id() + ", clsLdrId=" + ldrId
                                + ", resName=" + name + ", parentClsLdr=" + getParent() + ", msg="
                                + res.errorMessage() + ']');

                    // Do not ask other nodes in case of shared mode all of them should have the resource.
                    return null;
                } else {
                    return new ByteArrayInputStream(res.byteSource().internalArray(), 0, res.byteSource().size());
                }
            } catch (IgniteCheckedException e) {
                // This thread should be interrupted again in communication if it
                // got interrupted. So we assume that thread can be interrupted
                // by processing cancellation request.
                if (Thread.currentThread().isInterrupted()) {
                    if (!quiet)
                        U.error(log, "Failed to get resource probably due to task/job cancellation: " + name, e);
                    else if (log.isDebugEnabled())
                        log.debug("Failed to get resource probably due to task/job cancellation: " + name);
                } else {
                    if (!quiet)
                        U.warn(log,
                                "Failed to get resource from node (is node alive?) [nodeId=" + node.id()
                                        + ", clsLdrId=" + ldrId + ", resName=" + name + ", parentClsLdr="
                                        + getParent() + ", err=" + e + ']');
                    else if (log.isDebugEnabled())
                        log.debug("Failed to get resource from node (is node alive?) [nodeId=" + node.id()
                                + ", clsLdrId=" + ldrId + ", resName=" + name + ", parentClsLdr=" + getParent()
                                + ", err=" + e + ']');
                }
            }
        }

        return null;
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
        synchronized (mux) {
            return S.toString(GridDeploymentClassLoader.class, this);
        }
    }
}