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

com.jme3.asset.ImplHandler Maven / Gradle / Ivy

There is a newer version: 3.7.0-stable
Show newest version
/*
 * Copyright (c) 2009-2021 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.asset;

import com.jme3.asset.cache.AssetCache;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * ImplHandler manages the asset loader and asset locator
 * implementations in a thread safe way. This allows implementations
 * which store local persistent data to operate with a multi-threaded system.
 * This is done by keeping an instance of each asset loader and asset
 * locator object in a thread local.
 */
final class ImplHandler {

    private static final Logger logger = Logger.getLogger(ImplHandler.class.getName());

    private final AssetManager assetManager;

    private final ThreadLocal parentAssetKey
            = new ThreadLocal<>();

    private final CopyOnWriteArrayList> locatorsList =
            new CopyOnWriteArrayList<>();

    private final HashMap, ImplThreadLocal> classToLoaderMap =
            new HashMap<>();

    private final ConcurrentHashMap> extensionToLoaderMap =
            new ConcurrentHashMap<>();

    private final ConcurrentHashMap, AssetProcessor> classToProcMap =
            new ConcurrentHashMap<>();

    private final ConcurrentHashMap, AssetCache> classToCacheMap =
            new ConcurrentHashMap<>();

    public ImplHandler(AssetManager assetManager) {
        this.assetManager = assetManager;
    }

    protected static class ImplThreadLocal extends ThreadLocal {

        private final Class type;
        private final String path;
        private final String[] extensions;

        public ImplThreadLocal(Class type, String[] extensions) {
            this.type = type;
            this.extensions = extensions.clone();
            this.path = null;
        }

        public ImplThreadLocal(Class type, String path) {
            this.type = type;
            this.path = path;
            this.extensions = null;
        }

        public ImplThreadLocal(Class type) {
            this.type = type;
            this.path = null;
            this.extensions = null;
        }

        public String getPath() {
            return path;
        }

        public String[] getExtensions() {
            return extensions;
        }

        public Class getTypeClass() {
            return type;
        }

        @Override
        protected T initialValue() {
            try {
                T obj = type.newInstance();
                if (path != null) {
                    ((AssetLocator) obj).setRootPath(path);
                }
                return obj;
            } catch (InstantiationException | IllegalAccessException ex) {
                logger.log(Level.SEVERE, "Cannot create locator of type {0}, does"
                        + " the class have an empty and publicly accessible"
                        + " constructor?", type.getName());
                logger.throwing(type.getName(), "", ex);
            }
            return null;
        }
    }

    /**
     * Establishes the asset key that is used for tracking dependent assets
     * that have failed to load. When set, the {@link DesktopAssetManager}
     * gets a hint that it should suppress {@link AssetNotFoundException}s
     * and instead call the listener callback (if set).
     *
     * @param parentKey The parent key
     */
    public void establishParentKey(AssetKey parentKey) {
        if (parentAssetKey.get() == null) {
            parentAssetKey.set(parentKey);
        }
    }

    public void releaseParentKey(AssetKey parentKey) {
        if (parentAssetKey.get() == parentKey) {
            parentAssetKey.set(null);
        }
    }

    public AssetKey getParentKey() {
        return parentAssetKey.get();
    }

    /**
     * Attempts to locate the given resource name.
     * @param key The full name of the resource.
     * @return The AssetInfo containing resource information required for
     * access, or null if not found.
     */
    public AssetInfo tryLocate(AssetKey key) {
        if (locatorsList.isEmpty()) {
            logger.warning("There are no locators currently"
                    + " registered. Use AssetManager."
                    + "registerLocator() to register a"
                    + " locator.");
            return null;
        }

        for (ImplThreadLocal local : locatorsList) {
            AssetInfo info = local.get().locate(assetManager, key);
            if (info != null) {
                return info;
            }
        }

        return null;
    }

    public int getLocatorCount() {
        return locatorsList.size();
    }

    /**
     * Returns the AssetLoader registered for the given extension
     * of the current thread.
     * @return AssetLoader registered with addLoader.
     */
    public AssetLoader aquireLoader(AssetKey key) {
        // No need to synchronize() against map, its concurrent
        ImplThreadLocal local = extensionToLoaderMap.get(key.getExtension());
        if (local == null) {
            throw new AssetLoadException("No loader registered for type \""
                    + key.getExtension() + "\"");

        }
        return (AssetLoader) local.get();
    }

    public void clearCache() {
        // The iterator over the values collection is thread-safe.
        synchronized (classToCacheMap) {
            for (AssetCache cache : classToCacheMap.values()) {
                cache.clearCache();
            }
        }
    }

    @SuppressWarnings("unchecked")
    public  T getCache(Class cacheClass) {
        if (cacheClass == null) {
            return null;
        }

        T cache = (T) classToCacheMap.get(cacheClass);
        if (cache == null) {
            synchronized (classToCacheMap) {
                cache = (T) classToCacheMap.get(cacheClass);
                if (cache == null) {
                    try {
                        cache = cacheClass.newInstance();
                        classToCacheMap.put(cacheClass, cache);
                    } catch (InstantiationException ex) {
                        throw new IllegalArgumentException("The cache class cannot"
                                + " be created, ensure it has empty constructor", ex);
                    } catch (IllegalAccessException ex) {
                        throw new IllegalArgumentException("The cache class cannot "
                                + "be accessed", ex);
                    }
                }
            }
        }
        return cache;
    }

    @SuppressWarnings("unchecked")
    public  T getProcessor(Class procClass) {
        if (procClass == null)
            return null;

        T proc = (T) classToProcMap.get(procClass);
        if (proc == null) {
            synchronized (classToProcMap) {
                proc = (T) classToProcMap.get(procClass);
                if (proc == null) {
                    try {
                        proc = procClass.newInstance();
                        classToProcMap.put(procClass, proc);
                    } catch (InstantiationException ex) {
                        throw new IllegalArgumentException("The processor class cannot"
                                + " be created, ensure it has empty constructor", ex);
                    } catch (IllegalAccessException ex) {
                        throw new IllegalArgumentException("The processor class cannot "
                                + "be accessed", ex);
                    }
                }
            }
        }
        return proc;
    }

    @SuppressWarnings("unchecked")
    public void addLoader(final Class loaderType, String ... extensions) {
        // Synchronized access must be used for any ops on classToLoaderMap
        ImplThreadLocal local = new ImplThreadLocal(loaderType, extensions);
        for (String extension : extensions) {
            extension = extension.toLowerCase();
            synchronized (classToLoaderMap) {
                classToLoaderMap.put(loaderType, local);
                extensionToLoaderMap.put(extension, local);
            }
        }
    }

    public void removeLoader(final Class loaderType) {
        // Synchronized access must be used for any ops on classToLoaderMap
        // Find the loader ImplThreadLocal for this class
        synchronized (classToLoaderMap) {
            // Remove it from the class->loader map
            ImplThreadLocal local = classToLoaderMap.remove(loaderType);
            if (local == null)
                return;
            // Remove it from the extension->loader map
            for (String extension : local.getExtensions()) {
                extensionToLoaderMap.remove(extension);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public void addLocator(final Class locatorType, String rootPath) {
        locatorsList.add(new ImplThreadLocal(locatorType, rootPath));
    }

    public void removeLocator(final Class locatorType, String rootPath) {
        ArrayList> locatorsToRemove = new ArrayList<>();
        Iterator> it = locatorsList.iterator();

        while (it.hasNext()) {
            ImplThreadLocal locator = it.next();
            if (locator.getPath().equals(rootPath)
                    && locator.getTypeClass().equals(locatorType)) {
                //it.remove();
                // copy on write list doesn't support iterator remove,
                // must use temporary list
                locatorsToRemove.add(locator);
            }
        }

        locatorsList.removeAll(locatorsToRemove);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy