📄 markupcache.java
字号:
/* * 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.wicket.markup;import java.io.IOException;import java.util.Collection;import java.util.Iterator;import org.apache.wicket.Application;import org.apache.wicket.MarkupContainer;import org.apache.wicket.WicketRuntimeException;import org.apache.wicket.markup.loader.DefaultMarkupLoader;import org.apache.wicket.markup.loader.IMarkupLoader;import org.apache.wicket.settings.IMarkupSettings;import org.apache.wicket.util.concurrent.ConcurrentHashMap;import org.apache.wicket.util.listener.IChangeListener;import org.apache.wicket.util.resource.IResourceStream;import org.apache.wicket.util.resource.ResourceStreamNotFoundException;import org.apache.wicket.util.watch.IModifiable;import org.apache.wicket.util.watch.ModificationWatcher;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * This is Wicket's default IMarkupCache implementation. It will load the markup and cache it for * fast retrieval. * <p> * If the application is in development mode and a markup file changes, it'll automatically be * removed from the cache and reloaded when needed. * <p> * MarkupCache is registered with {@link IMarkupSettings} and thus can be replaced with a subclassed * version. * * @see IMarkupSettings * * @author Jonathan Locke * @author Juergen Donnerstag */public class MarkupCache implements IMarkupCache{ /** Log for reporting. */ private static final Logger log = LoggerFactory.getLogger(MarkupCache.class); /** Map of markup tags by class (exactly what is in the file). */ private final ICache markupCache; /** The markup cache key provider used by MarkupCache */ private IMarkupCacheKeyProvider markupCacheKeyProvider; /** The markup resource stream provider used by MarkupCache */ private IMarkupResourceStreamProvider markupResourceStreamProvider; /** The markup loader used by MarkupCache */ private IMarkupLoader markupLoader; /** The application object */ private final Application application; /** * Constructor. * * @param application */ public MarkupCache(Application application) { this.application = application; markupCache = newCacheImplementation(); if (markupCache == null) { throw new WicketRuntimeException("The map used to cache markup must not be null"); } } /** * @see org.apache.wicket.markup.IMarkupCache#clear() */ public final void clear() { markupCache.clear(); } /** * * @see org.apache.wicket.markup.IMarkupCache#shutdown() */ public void shutdown() { markupCache.shutdown(); } /** * @see org.apache.wicket.markup.IMarkupCache#removeMarkup(java.lang.String) */ public final Markup removeMarkup(final String cacheKey) { if (cacheKey == null) { throw new IllegalArgumentException("Parameter 'cacheKey' must not be null"); } if (log.isDebugEnabled()) { log.debug("Remove from cache: cacheKey=" + cacheKey); } // Remove the markup and any other markup which depends on it // (inheritance) Markup markup = (Markup)markupCache.get(cacheKey); if (markup != null) { markupCache.remove(cacheKey); // In practice markup inheritance has probably not more than 3 or 4 // levels. And since markup reloading is only enabled in development // mode, this max 4 iterations of the outer loop shouldn't be a // problem. int count; do { count = 0; // If a base markup file has been removed from the cache, than // the derived markup should be removed as well. Iterator iter = markupCache.getKeys().iterator(); while (iter.hasNext()) { Markup cacheMarkup = (Markup)markupCache.get(iter.next()); MarkupResourceData resourceData = cacheMarkup.getMarkupResourceData() .getBaseMarkupResourceData(); if (resourceData != null) { String baseCacheKey = resourceData.getResource().getCacheKey(); if (markupCache.get(baseCacheKey) == null) { if (log.isDebugEnabled()) { log.debug("Remove from cache: cacheKey=" + cacheMarkup.getMarkupResourceData().getResource().getCacheKey()); } iter.remove(); count++; } } } } while (count > 0); // And now remove all watcher entries associated with markup // resources no longer in the cache. Note that you can not use // Application.get() since removeMarkup() will be call from a // ModificationWatcher thread which has no associated Application. final ModificationWatcher watcher = application.getResourceSettings() .getResourceWatcher(true); if (watcher != null) { Iterator iter = watcher.getEntries().iterator(); while (iter.hasNext()) { IModifiable modifiable = (IModifiable)iter.next(); if (modifiable instanceof MarkupResourceStream) { MarkupResourceStream resourceStream = (MarkupResourceStream)modifiable; String resourceCacheKey = resourceStream.getCacheKey(); if (markupCache.containsKey(resourceCacheKey) == false) { iter.remove(); } } } } } return markup; } /** * @see org.apache.wicket.markup.IMarkupCache#getMarkupStream(org.apache.wicket.MarkupContainer, * boolean, boolean) */ public final MarkupStream getMarkupStream(final MarkupContainer container, final boolean enforceReload, final boolean throwException) { if (container == null) { throw new IllegalArgumentException("Parameter 'container' must not be 'null'."); } // Look for associated markup final Markup markup = getMarkup(container, container.getClass(), enforceReload); // If we found markup for this container if (markup != Markup.NO_MARKUP) { return new MarkupStream(markup); } if (throwException == true) { // throw exception since there is no associated markup throw new MarkupNotFoundException("Markup not found. Component class: " + container.getClass().getName() + " Enable debug messages for org.apache.wicket.util.resource to get a list of all filenames tried"); } return null; } /** * @see org.apache.wicket.markup.IMarkupCache#hasAssociatedMarkup(org.apache.wicket.MarkupContainer) */ public final boolean hasAssociatedMarkup(final MarkupContainer container) { return getMarkup(container, container.getClass(), false) != Markup.NO_MARKUP; } /** * @see org.apache.wicket.markup.IMarkupCache#size() */ public final int size() { return markupCache.size(); } /** * Get a unmodifiable map which contains the cached data. The map key is of type String and the * value is of type Markup. * * @return */ protected final ICache getMarkupCache() { return markupCache; } /** * THIS IS NOT PART OF WICKET'S PUBLIC API. DO NOT USE IT. * * I still don't like this method being part of the API but I didn't find a suitable other * solution. * * @see org.apache.wicket.markup.IMarkupCache#getMarkup(org.apache.wicket.MarkupContainer, * java.lang.Class, boolean) */ public final Markup getMarkup(final MarkupContainer container, final Class clazz, final boolean enforceReload) { Class containerClass = clazz; if (clazz == null) { containerClass = container.getClass(); } else if (!clazz.isAssignableFrom(container.getClass())) { throw new WicketRuntimeException("Parameter clazz must be an instance of " + container.getClass().getName() + ", but is a " + clazz.getName()); } // Get the cache key to be associated with the markup resource stream final String cacheKey = getMarkupCacheKeyProvider(container).getCacheKey(container, containerClass); // Is the markup already in the cache? Markup markup = (enforceReload == false ? getMarkupFromCache(cacheKey, container) : null); if (markup == null) { if (log.isDebugEnabled()) { log.debug("Load markup: cacheKey=" + cacheKey); } // Who is going to provide the markup resource stream? // And ask the provider to locate the markup resource stream final IResourceStream resourceStream = getMarkupResourceStreamProvider(container).getMarkupResourceStream( container, containerClass); // Found markup? if (resourceStream != null) { final MarkupResourceStream markupResourceStream; if (resourceStream instanceof MarkupResourceStream) { markupResourceStream = (MarkupResourceStream)resourceStream; } else { markupResourceStream = new MarkupResourceStream(resourceStream, new ContainerInfo(container), containerClass); } markupResourceStream.setCacheKey(cacheKey); // load the markup and watch for changes markup = loadMarkupAndWatchForChanges(container, markupResourceStream, enforceReload); } else { markup = onMarkupNotFound(cacheKey, container); } } return markup; } /** * Will be called if the markup was not in the cache yet but could not be found either. * <p> * Subclasses may change the default implementation. E.g. they might choose not update the cache * to enforce reloading of any markup not found. This might be useful in very dynamic * environments. * * @param cacheKey * @param container * @return Markup.NO_MARKUP */ protected Markup onMarkupNotFound(final String cacheKey, final MarkupContainer container) { if (log.isDebugEnabled()) { log.debug("Markup not found: " + cacheKey); } // flag markup as non-existent return putIntoCache(cacheKey, Markup.NO_MARKUP); } /** * Put the markup into the cache if cacheKey is not null and the cache does not yet contain the * cacheKey. Return the markup stored in the cache if cacheKey is present already. * * @param cacheKey * If null, than ignore the cache * @param markup * @return markup The markup provided, except if the cacheKey already existed in the cache, than * the markup from the cache is provided. */ protected Markup putIntoCache(final String cacheKey, Markup markup)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -