1 /* 2 * Copyright (c) 2009-2012 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.jme3.asset; 34 35 import java.util.ArrayList; 36 import java.util.HashMap; 37 import java.util.Iterator; 38 import java.util.logging.Level; 39 import java.util.logging.Logger; 40 41 /** 42 * <code>ImplHandler</code> manages the asset loader and asset locator 43 * implementations in a thread safe way. This allows implementations 44 * which store local persistent data to operate with a multi-threaded system. 45 * This is done by keeping an instance of each asset loader and asset 46 * locator object in a thread local. 47 */ 48 public class ImplHandler { 49 50 private static final Logger logger = Logger.getLogger(ImplHandler.class.getName()); 51 52 private final AssetManager owner; 53 54 private final ThreadLocal<AssetKey> parentAssetKey 55 = new ThreadLocal<AssetKey>(); 56 57 private final ArrayList<ImplThreadLocal> genericLocators = 58 new ArrayList<ImplThreadLocal>(); 59 60 private final HashMap<String, ImplThreadLocal> loaders = 61 new HashMap<String, ImplThreadLocal>(); 62 ImplHandler(AssetManager owner)63 public ImplHandler(AssetManager owner){ 64 this.owner = owner; 65 } 66 67 protected class ImplThreadLocal extends ThreadLocal { 68 69 private final Class<?> type; 70 private final String path; 71 ImplThreadLocal(Class<?> type)72 public ImplThreadLocal(Class<?> type){ 73 this.type = type; 74 path = null; 75 } 76 ImplThreadLocal(Class<?> type, String path)77 public ImplThreadLocal(Class<?> type, String path){ 78 this.type = type; 79 this.path = path; 80 } 81 getPath()82 public String getPath() { 83 return path; 84 } 85 getTypeClass()86 public Class<?> getTypeClass(){ 87 return type; 88 } 89 90 @Override initialValue()91 protected Object initialValue(){ 92 try { 93 return type.newInstance(); 94 } catch (InstantiationException ex) { 95 logger.log(Level.SEVERE,"Cannot create locator of type {0}, does" 96 + " the class have an empty and publically accessible"+ 97 " constructor?", type.getName()); 98 logger.throwing(type.getName(), "<init>", ex); 99 } catch (IllegalAccessException ex) { 100 logger.log(Level.SEVERE,"Cannot create locator of type {0}, " 101 + "does the class have an empty and publically " 102 + "accessible constructor?", type.getName()); 103 logger.throwing(type.getName(), "<init>", ex); 104 } 105 return null; 106 } 107 } 108 109 /** 110 * Establishes the asset key that is used for tracking dependent assets 111 * that have failed to load. When set, the {@link DesktopAssetManager} 112 * gets a hint that it should suppress {@link AssetNotFoundException}s 113 * and instead call the listener callback (if set). 114 * 115 * @param parentKey The parent key 116 */ establishParentKey(AssetKey parentKey)117 public void establishParentKey(AssetKey parentKey){ 118 if (parentAssetKey.get() == null){ 119 parentAssetKey.set(parentKey); 120 } 121 } 122 releaseParentKey(AssetKey parentKey)123 public void releaseParentKey(AssetKey parentKey){ 124 if (parentAssetKey.get() == parentKey){ 125 parentAssetKey.set(null); 126 } 127 } 128 getParentKey()129 public AssetKey getParentKey(){ 130 return parentAssetKey.get(); 131 } 132 133 /** 134 * Attempts to locate the given resource name. 135 * @param key The full name of the resource. 136 * @return The AssetInfo containing resource information required for 137 * access, or null if not found. 138 */ tryLocate(AssetKey key)139 public AssetInfo tryLocate(AssetKey key){ 140 synchronized (genericLocators){ 141 if (genericLocators.isEmpty()) 142 return null; 143 144 for (ImplThreadLocal local : genericLocators){ 145 AssetLocator locator = (AssetLocator) local.get(); 146 if (local.getPath() != null){ 147 locator.setRootPath((String) local.getPath()); 148 } 149 AssetInfo info = locator.locate(owner, key); 150 if (info != null) 151 return info; 152 } 153 } 154 return null; 155 } 156 getLocatorCount()157 public int getLocatorCount(){ 158 synchronized (genericLocators){ 159 return genericLocators.size(); 160 } 161 } 162 163 /** 164 * Returns the AssetLoader registered for the given extension 165 * of the current thread. 166 * @return AssetLoader registered with addLoader. 167 */ aquireLoader(AssetKey key)168 public AssetLoader aquireLoader(AssetKey key){ 169 synchronized (loaders){ 170 ImplThreadLocal local = loaders.get(key.getExtension()); 171 if (local != null){ 172 AssetLoader loader = (AssetLoader) local.get(); 173 return loader; 174 } 175 return null; 176 } 177 } 178 addLoader(final Class<?> loaderType, String ... extensions)179 public void addLoader(final Class<?> loaderType, String ... extensions){ 180 ImplThreadLocal local = new ImplThreadLocal(loaderType); 181 for (String extension : extensions){ 182 extension = extension.toLowerCase(); 183 synchronized (loaders){ 184 loaders.put(extension, local); 185 } 186 } 187 } 188 addLocator(final Class<?> locatorType, String rootPath)189 public void addLocator(final Class<?> locatorType, String rootPath){ 190 ImplThreadLocal local = new ImplThreadLocal(locatorType, rootPath); 191 synchronized (genericLocators){ 192 genericLocators.add(local); 193 } 194 } 195 removeLocator(final Class<?> locatorType, String rootPath)196 public void removeLocator(final Class<?> locatorType, String rootPath){ 197 synchronized (genericLocators){ 198 Iterator<ImplThreadLocal> it = genericLocators.iterator(); 199 while (it.hasNext()){ 200 ImplThreadLocal locator = it.next(); 201 if (locator.getPath().equals(rootPath) && 202 locator.getTypeClass().equals(locatorType)){ 203 it.remove(); 204 } 205 } 206 } 207 } 208 209 } 210