001/*
002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Jean-Marc Orliaguet, Chalmers
018 *
019 * $Id$
020 */
021
022package org.nuxeo.theme.resources;
023
024import java.net.MalformedURLException;
025import java.net.URL;
026import java.util.ArrayList;
027import java.util.HashMap;
028import java.util.List;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.runtime.api.Framework;
033import org.nuxeo.runtime.services.event.Event;
034import org.nuxeo.runtime.services.event.EventService;
035import org.nuxeo.theme.Manager;
036import org.nuxeo.theme.Registrable;
037import org.nuxeo.theme.themes.ThemeException;
038import org.nuxeo.theme.themes.ThemeManager;
039import org.nuxeo.theme.types.TypeFamily;
040import org.nuxeo.theme.types.TypeRegistry;
041
042public final class ResourceManager implements Registrable {
043
044    private static final Log log = LogFactory.getLog(ResourceManager.class);
045
046    public static final String GLOBAL_RESOURCES_REGISTERED_EVENT = "themeGlobalResourcesRegistered";
047
048    private final HashMap<URL, List<String>> globalCache = new HashMap<URL, List<String>>();
049
050    private final ThreadLocal<List<String>> localCache = new ThreadLocal<List<String>>() {
051        @Override
052        protected List<String> initialValue() {
053            return new ArrayList<String>();
054        }
055    };
056
057    private final TypeRegistry typeRegistry = Manager.getTypeRegistry();
058
059    public void addResource(String name, URL themeUrl) {
060        addResource(name, themeUrl, false);
061    }
062
063    public void addResource(String name, URL themeUrl, boolean local) {
064        if (local) {
065            log.debug("Added local resource: " + name);
066        } else {
067            log.debug("Added theme resource: " + name);
068        }
069        ResourceType resourceType = (ResourceType) typeRegistry.lookup(TypeFamily.RESOURCE, name);
070        if (resourceType != null) {
071            for (String dependency : resourceType.getDependencies()) {
072                log.debug("  Subresource dependency: " + name + " -> " + dependency);
073                addResource(dependency, themeUrl, local);
074            }
075            List<String> scripts;
076            if (local) {
077                scripts = getLocalResources();
078            } else {
079                scripts = getGlobalResourcesFor(themeUrl);
080            }
081            if (!scripts.contains(name)) {
082                scripts.add(name);
083            }
084        } else {
085            log.warn("Resource not found: " + name);
086        }
087    }
088
089    public void flush() {
090        getLocalResources().clear();
091    }
092
093    private List<String> getLocalResources() {
094        return localCache.get();
095    }
096
097    public List<String> getResourcesFor(String themeUrl) {
098        List<String> resources = new ArrayList<String>();
099        resources.addAll(getGlobalResourcesFor(themeUrl));
100        for (String localResource : getLocalResources()) {
101            if (!resources.contains(localResource)) {
102                resources.add(localResource);
103            }
104        }
105        return resources;
106    }
107
108    public List<String> getGlobalResourcesFor(String themeUrl) {
109        try {
110            return getGlobalResourcesFor(new URL(themeUrl));
111        } catch (MalformedURLException e) {
112            log.warn(e);
113            return null;
114        }
115    }
116
117    public synchronized List<String> getGlobalResourcesFor(URL themeUrl) {
118        if (!globalCache.containsKey(themeUrl)) {
119            globalCache.put(themeUrl, new ArrayList<String>());
120            // hook to notify potential listeners that the resources for given
121            // theme url need to be built
122            EventService eventService = Framework.getLocalService(EventService.class);
123            eventService.sendEvent(new Event(ThemeManager.THEME_TOPIC, GLOBAL_RESOURCES_REGISTERED_EVENT, this,
124                    themeUrl));
125        }
126        return globalCache.get(themeUrl);
127    }
128
129    public void clearGlobalCache(String themeName) {
130        List<URL> toRemove = new ArrayList<URL>();
131        for (URL themeUrl : globalCache.keySet()) {
132            String name = ThemeManager.getThemeNameByUrl(themeUrl);
133            if (themeName.equals(name)) {
134                toRemove.add(themeUrl);
135
136            }
137        }
138        for (URL themeUrl : toRemove) {
139            globalCache.remove(themeUrl);
140        }
141    }
142
143    public static byte[] getBinaryBankResource(String resourceBankName, String collectionName, String typeName,
144            String resourceName) throws ThemeException {
145        byte[] data = null;
146        ResourceBank resourceBank = ThemeManager.getResourceBank(resourceBankName);
147        data = resourceBank.getResourceContent(collectionName, typeName, resourceName);
148        if (data == null) {
149            throw new ThemeException("Resource bank content could not be read: " + resourceName);
150        }
151        return data;
152    }
153
154    public static String getBankResource(String resourceBankName, String collectionName, String typeName,
155            String resourceName) throws ThemeException {
156        byte[] data = getBinaryBankResource(resourceBankName, collectionName, typeName, resourceName);
157        if (data == null) {
158            throw new ThemeException("Could not get bank resource: " + resourceName);
159        }
160        return new String(data);
161    }
162
163    @Override
164    public void clear() {
165        globalCache.clear();
166    }
167
168}