blob: def2d1192a7072d0333054329ab880200a467b83 [file] [log] [blame]
/*
* 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.settings;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.IResourceFactory;
import org.apache.wicket.Localizer;
import org.apache.wicket.core.util.resource.locator.IResourceStreamLocator;
import org.apache.wicket.core.util.resource.locator.ResourceStreamLocator;
import org.apache.wicket.core.util.resource.locator.caching.CachingResourceStreamLocator;
import org.apache.wicket.css.ICssCompressor;
import org.apache.wicket.javascript.IJavaScriptCompressor;
import org.apache.wicket.markup.head.PriorityFirstComparator;
import org.apache.wicket.markup.head.ResourceAggregator.RecordedHeaderItem;
import org.apache.wicket.markup.html.IPackageResourceGuard;
import org.apache.wicket.markup.html.SecurePackageResourceGuard;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.request.resource.caching.FilenameWithVersionResourceCachingStrategy;
import org.apache.wicket.request.resource.caching.IResourceCachingStrategy;
import org.apache.wicket.request.resource.caching.NoOpResourceCachingStrategy;
import org.apache.wicket.request.resource.caching.version.CachingResourceVersion;
import org.apache.wicket.request.resource.caching.version.IResourceVersion;
import org.apache.wicket.request.resource.caching.version.LastModifiedResourceVersion;
import org.apache.wicket.request.resource.caching.version.MessageDigestResourceVersion;
import org.apache.wicket.request.resource.caching.version.RequestCycleCachedResourceVersion;
import org.apache.wicket.resource.IPropertiesFactoryContext;
import org.apache.wicket.resource.PropertiesFactory;
import org.apache.wicket.resource.loader.ClassStringResourceLoader;
import org.apache.wicket.resource.loader.ComponentStringResourceLoader;
import org.apache.wicket.resource.loader.IStringResourceLoader;
import org.apache.wicket.resource.loader.InitializerStringResourceLoader;
import org.apache.wicket.resource.loader.PackageStringResourceLoader;
import org.apache.wicket.resource.loader.ValidatorStringResourceLoader;
import org.apache.wicket.util.file.IFileCleaner;
import org.apache.wicket.util.file.IResourceFinder;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Generics;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.watch.IModificationWatcher;
import org.apache.wicket.util.watch.ModificationWatcher;
/**
* Class for resource related settings
* <p>
* <i>resourcePollFrequency </i> (defaults to no polling frequency) - Frequency at which resources
* should be polled for changes.
* <p>
* <i>resourceFinders</i> - Add/modify this to alter the search path for resources.
* <p>
* <i>useDefaultOnMissingResource </i> (defaults to true) - Set to true to return a default value if
* available when a required string resource is not found. If set to false then the
* throwExceptionOnMissingResource flag is used to determine how to behave. If no default is
* available then this is the same as if this flag were false
* <p>
* <i>A ResourceStreamLocator </i>- An Application's ResourceStreamLocator is used to find resources
* such as images or markup files. You can supply your own ResourceStreamLocator if your prefer to
* store your application's resources in a non-standard location (such as a different filesystem
* location, a particular JAR file or even a database) by overriding the getResourceLocator()
* method.
* <p>
* <i>Resource Factories </i>- Resource factories can be used to create resources dynamically from
* specially formatted HTML tag attribute values. For more details, see {@link IResourceFactory},
* {@link org.apache.wicket.markup.html.image.resource.DefaultButtonImageResourceFactory} and
* especially {@link org.apache.wicket.markup.html.image.resource.LocalizedImageResource}.
* <p>
* <i>A Localizer </i> The getLocalizer() method returns an object encapsulating all of the
* functionality required to access localized resources. For many localization problems, even this
* will not be required, as there are convenience methods available to all components:
* {@link org.apache.wicket.Component#getString(String key)} and
* {@link org.apache.wicket.Component#getString(String key, org.apache.wicket.model.IModel model)}.
* <p>
* <i>stringResourceLoaders </i>- A chain of <code>IStringResourceLoader</code> instances that are
* searched in order to obtain string resources used during localization. By default the chain is
* set up to first search for resources against a particular component (e.g. page etc.) and then
* against the application.
* </p>
*
* @author Jonathan Locke
* @author Chris Turner
* @author Eelco Hillenius
* @author Juergen Donnerstag
* @author Johan Compagner
* @author Igor Vaynberg (ivaynberg)
* @author Martijn Dashorst
* @author James Carman
*/
public class ResourceSettings implements IPropertiesFactoryContext
{
/** I18N support */
private Localizer localizer;
/** Map to look up resource factories by name */
private final Map<String, IResourceFactory> nameToResourceFactory = Generics.newHashMap();
/** The package resource guard. */
private IPackageResourceGuard packageResourceGuard = new SecurePackageResourceGuard(
new SecurePackageResourceGuard.SimpleCache(100));
/** The factory to be used for the property files */
private org.apache.wicket.resource.IPropertiesFactory propertiesFactory;
/** Filesystem Path to search for resources */
private List<IResourceFinder> resourceFinders = new ArrayList<>();
/** Frequency at which files should be polled */
private Duration resourcePollFrequency = null;
/** resource locator for this application */
private IResourceStreamLocator resourceStreamLocator;
/** ModificationWatcher to watch for changes in markup files */
private IModificationWatcher resourceWatcher;
/**
* A cleaner that removes files asynchronously.
* <p>
* Used internally to remove the temporary files created by FileUpload functionality.
*/
private IFileCleaner fileCleaner;
/** Chain of string resource loaders to use */
private final List<IStringResourceLoader> stringResourceLoaders = Generics.newArrayList(6);
/** Flags used to determine how to behave if resources are not found */
private boolean throwExceptionOnMissingResource = true;
/** Determines behavior of string resource loading if string is missing */
private boolean useDefaultOnMissingResource = true;
/** Default cache duration */
private Duration defaultCacheDuration = WebResponse.MAX_CACHE_DURATION;
/** The JavaScript compressor */
private IJavaScriptCompressor javascriptCompressor;
/** The Css compressor */
private ICssCompressor cssCompressor;
/** escape string for '..' within resource keys */
private String parentFolderPlaceholder = "::";
// resource caching strategy
private IResourceCachingStrategy resourceCachingStrategy;
// application these settings are bound to
private final Application application;
private boolean useMinifiedResources = true;
private Comparator<? super RecordedHeaderItem> headerItemComparator = new PriorityFirstComparator(
false);
private boolean encodeJSessionId = false;
/**
* Configures Wicket's default ResourceLoaders.<br>
* For an example in {@code FooApplication} let {@code bar.Foo} extend {@link Component}, this
* results in the following ordering:
* <dl>
* <dt>component specific</dt>
* <dd>
* <ul>
* <li>bar/Foo.properties</li>
* <li>org/apache/wicket/Component.properties</li>
* </ul>
* </dd>
* <dt>package specific</dt>
* <dd>
* <ul>
* <li>bar/package.properties</li>
* <li>package.properties (on Foo's class loader)</li>
* <li>org/apache/wicket/package.properties</li>
* <li>org/apache/package.properties</li>
* <li>org/package.properties</li>
* <li>package.properties (on Component's class loader)</li>
* </ul>
* </dd>
* <dt>application specific</dt>
* <dd>
* <ul>
* <li>FooApplication.properties</li>
* <li>Application.properties</li>
* </ul>
* </dd>
* <dt>validator specific</dt>
* <dt>Initializer specific</dt>
* <dd>
* <ul>
* <li>bar.Foo.properties (Foo implementing IInitializer)</li>
* </ul>
* </dd>
* </dl>
*
* @param application
*/
public ResourceSettings(final Application application)
{
this.application = application;
stringResourceLoaders.add(new ComponentStringResourceLoader());
stringResourceLoaders.add(new PackageStringResourceLoader());
stringResourceLoaders.add(new ClassStringResourceLoader(application.getClass()));
stringResourceLoaders.add(new ValidatorStringResourceLoader());
stringResourceLoaders.add(new InitializerStringResourceLoader(application.getInitializers()));
}
/**
* Adds a resource factory to the list of factories to consult when generating resources
* automatically
*
* @param name
* The name to give to the factory
* @param resourceFactory
* The resource factory to add
* @return {@code this} object for chaining
*/
public ResourceSettings addResourceFactory(final String name, IResourceFactory resourceFactory)
{
nameToResourceFactory.put(name, resourceFactory);
return this;
}
@Override
public Localizer getLocalizer()
{
if (localizer == null)
{
localizer = new Localizer();
}
return localizer;
}
/**
* Gets the {@link org.apache.wicket.markup.html.PackageResourceGuard package resource guard}.
*
* @return The package resource guard
*/
public IPackageResourceGuard getPackageResourceGuard()
{
return packageResourceGuard;
}
/**
* Get the property factory which will be used to load property files
*
* @return PropertiesFactory
*/
public org.apache.wicket.resource.IPropertiesFactory getPropertiesFactory()
{
if (propertiesFactory == null)
{
propertiesFactory = new PropertiesFactory(this);
}
return propertiesFactory;
}
/**
* @param name
* Name of the factory to get
* @return The IResourceFactory with the given name.
*/
public IResourceFactory getResourceFactory(final String name)
{
return nameToResourceFactory.get(name);
}
/**
* Gets the resource finders to use when searching for resources. By default, a finder that
* looks in the classpath root is configured. {@link org.apache.wicket.protocol.http.WebApplication} adds the classpath
* directory META-INF/resources. To configure additional search paths or filesystem paths, add
* to this list.
*
* @return Returns the resourceFinders.
*/
public List<IResourceFinder> getResourceFinders()
{
return resourceFinders;
}
/**
* @return Returns the resourcePollFrequency.
*/
public Duration getResourcePollFrequency()
{
return resourcePollFrequency;
}
@Override
public IResourceStreamLocator getResourceStreamLocator()
{
if (resourceStreamLocator == null)
{
// Create compound resource locator using source path from
// application settings
resourceStreamLocator = new ResourceStreamLocator(getResourceFinders());
resourceStreamLocator = new CachingResourceStreamLocator(resourceStreamLocator);
}
return resourceStreamLocator;
}
@Override
public IModificationWatcher getResourceWatcher(boolean start)
{
if (resourceWatcher == null && start)
{
synchronized (this)
{
if (resourceWatcher == null)
{
final Duration pollFrequency = getResourcePollFrequency();
if (pollFrequency != null)
{
resourceWatcher = new ModificationWatcher(pollFrequency);
}
}
}
}
return resourceWatcher;
}
/**
* Sets the resource watcher
*
* @param watcher
* @return {@code this} object for chaining
*/
public ResourceSettings setResourceWatcher(IModificationWatcher watcher)
{
resourceWatcher = watcher;
return this;
}
/**
* @return the a cleaner which can be used to remove files asynchronously.
*/
public IFileCleaner getFileCleaner()
{
return fileCleaner;
}
/**
* Sets a cleaner that can be used to remove files asynchronously.
* <p>
* Used internally to delete the temporary files created by FileUpload functionality
*
* @param fileUploadCleaner
* the actual cleaner implementation. Can be <code>null</code>
* @return {@code this} object for chaining
*/
public ResourceSettings setFileCleaner(IFileCleaner fileUploadCleaner)
{
fileCleaner = fileUploadCleaner;
return this;
}
/**
* @return mutable list of all available string resource loaders
*/
public List<IStringResourceLoader> getStringResourceLoaders()
{
return stringResourceLoaders;
}
public boolean getThrowExceptionOnMissingResource()
{
return throwExceptionOnMissingResource;
}
/**
* @return Whether to use a default value (if available) when a missing resource is requested
*/
public boolean getUseDefaultOnMissingResource()
{
return useDefaultOnMissingResource;
}
/**
* Sets the localizer which will be used to find property values.
*
* @param localizer
* @since 1.3.0
* @return {@code this} object for chaining
*/
public ResourceSettings setLocalizer(final Localizer localizer)
{
this.localizer = localizer;
return this;
}
/**
* Sets the {@link org.apache.wicket.markup.html.PackageResourceGuard package resource guard}.
*
* @param packageResourceGuard
* The package resource guard
* @return {@code this} object for chaining
*/
public ResourceSettings setPackageResourceGuard(IPackageResourceGuard packageResourceGuard)
{
this.packageResourceGuard = Args.notNull(packageResourceGuard, "packageResourceGuard");
return this;
}
/**
* Set the property factory which will be used to load property files
*
* @param factory
* @return {@code this} object for chaining
*/
public ResourceSettings setPropertiesFactory(org.apache.wicket.resource.IPropertiesFactory factory)
{
propertiesFactory = factory;
return this;
}
/**
* Sets the finders to use when searching for resources. By default, the resources are located
* on the classpath. To add additional search paths, add to the list given by
* {@link #getResourceFinders()}. Use this method if you want to completely exchange the list of
* resource finders.
*
* @param resourceFinders
* The resourceFinders to set
* @return {@code this} object for chaining
*/
public ResourceSettings setResourceFinders(final List<IResourceFinder> resourceFinders)
{
Args.notNull(resourceFinders, "resourceFinders");
this.resourceFinders = resourceFinders;
// Cause resource locator to get recreated
resourceStreamLocator = null;
return this;
}
/**
* Sets the resource polling frequency. This is the duration of time between checks of resource
* modification times. If a resource, such as an HTML file, has changed, it will be reloaded.
* The default is one second in 'development' mode and 'never' in deployment mode.
*
* @param resourcePollFrequency
* Frequency at which to poll resources or <code>null</code> if polling should be
* disabled
* @return {@code this} object for chaining
*/
public ResourceSettings setResourcePollFrequency(final Duration resourcePollFrequency)
{
this.resourcePollFrequency = resourcePollFrequency;
return this;
}
/**
* /**
* Sets the resource stream locator for this application
*
* Consider wrapping <code>resourceStreamLocator</code> in {@link CachingResourceStreamLocator}.
* This way the locator will not be asked more than once for {@link IResourceStream}s which do
* not exist.
* @param resourceStreamLocator
* new resource stream locator
*
* @see #getResourceStreamLocator()
* @return {@code this} object for chaining
*/
public ResourceSettings setResourceStreamLocator(IResourceStreamLocator resourceStreamLocator)
{
this.resourceStreamLocator = resourceStreamLocator;
return this;
}
/**
* @param throwExceptionOnMissingResource
* @return {@code this} object for chaining
*/
public ResourceSettings setThrowExceptionOnMissingResource(final boolean throwExceptionOnMissingResource)
{
this.throwExceptionOnMissingResource = throwExceptionOnMissingResource;
return this;
}
/**
* @param useDefaultOnMissingResource
* Whether to use a default value (if available) when a missing resource is requested
* @return {@code this} object for chaining
*/
public ResourceSettings setUseDefaultOnMissingResource(final boolean useDefaultOnMissingResource)
{
this.useDefaultOnMissingResource = useDefaultOnMissingResource;
return this;
}
/**
* Get the the default cache duration for resources.
* <p/>
*
* @return cache duration ({@link Duration#ZERO} will be returned if caching is disabled)
*
* @see Duration#ZERO
*/
public final Duration getDefaultCacheDuration()
{
return defaultCacheDuration;
}
/**
* Set the default cache duration for resources.
* <p/>
* Based on RFC-2616 this should not exceed one year. If you set {@link Duration#ZERO} caching will be
* disabled.
*
* @param duration
* default cache duration in seconds
*
* @see Duration#ZERO
* @see org.apache.wicket.request.http.WebResponse#MAX_CACHE_DURATION
* @return {@code this} object for chaining
*/
public final ResourceSettings setDefaultCacheDuration(Duration duration)
{
Args.notNull(duration, "duration");
defaultCacheDuration = duration;
return this;
}
/**
* Get the javascript compressor to remove comments and whitespace characters from javascripts
*
* @return whether the comments and whitespace characters will be stripped from resources served
* through {@link org.apache.wicket.request.resource.JavaScriptPackageResource
* JavaScriptPackageResource}. Null is a valid value.
*/
public IJavaScriptCompressor getJavaScriptCompressor()
{
return javascriptCompressor;
}
/**
* Set the javascript compressor implemententation use e.g. by
* {@link org.apache.wicket.request.resource.JavaScriptPackageResource
* JavaScriptPackageResource}. A typical implementation will remove comments and whitespace. But
* a no-op implementation is available as well.
*
* @param compressor
* The implementation to be used
* @return The old value
* @return {@code this} object for chaining
*/
public IJavaScriptCompressor setJavaScriptCompressor(IJavaScriptCompressor compressor)
{
IJavaScriptCompressor old = javascriptCompressor;
javascriptCompressor = compressor;
return old;
}
/**
* Get the CSS compressor to remove comments and whitespace characters from css resources
*
* @return whether the comments and whitespace characters will be stripped from resources served
* through {@link org.apache.wicket.request.resource.CssPackageResource
* CssPackageResource}. Null is a valid value.
*/
public ICssCompressor getCssCompressor()
{
return cssCompressor;
}
/**
* Set the CSS compressor implemententation use e.g. by
* {@link org.apache.wicket.request.resource.CssPackageResource CssPackageResource}. A typical
* implementation will remove comments and whitespace. But a no-op implementation is available
* as well.
*
* @param compressor
* The implementation to be used
* @return The old value
* @return {@code this} object for chaining
*/
public ICssCompressor setCssCompressor(ICssCompressor compressor)
{
ICssCompressor old = cssCompressor;
cssCompressor = compressor;
return old;
}
/**
* Placeholder string for '..' within resource urls (which will be crippled by the browser and
* not work anymore). Note that by default the placeholder string is <code>::</code>. Resources
* are protected by a {@link org.apache.wicket.markup.html.IPackageResourceGuard
* IPackageResourceGuard} implementation such as
* {@link org.apache.wicket.markup.html.PackageResourceGuard} which you may use or extend based
* on your needs.
*
* @return placeholder
*/
public String getParentFolderPlaceholder()
{
return parentFolderPlaceholder;
}
/**
* Placeholder string for '..' within resource urls (which will be crippled by the browser and
* not work anymore). Note that by default the placeholder string is <code>null</code> and thus
* will not allow to access parent folders. That is by purpose and for security reasons (see
* Wicket-1992). In case you really need it, a good value for placeholder would e.g. be "$up$".
* Resources additionally are protected by a
* {@link org.apache.wicket.markup.html.IPackageResourceGuard IPackageResourceGuard}
* implementation such as {@link org.apache.wicket.markup.html.PackageResourceGuard} which you
* may use or extend based on your needs.
*
* @see #getParentFolderPlaceholder()
*
* @param sequence
* character sequence which must not be ambiguous within urls
* @return {@code this} object for chaining
*/
public ResourceSettings setParentFolderPlaceholder(final String sequence)
{
parentFolderPlaceholder = sequence;
return this;
}
/**
* gets the resource caching strategy
*
* @return strategy
*/
public IResourceCachingStrategy getCachingStrategy()
{
if (resourceCachingStrategy == null)
{
final IResourceVersion resourceVersion;
if (application.usesDevelopmentConfig())
{
// development mode:
// use last-modified timestamp of packaged resource for resource caching
// cache the version information for the lifetime of the current http request
resourceVersion = new RequestCycleCachedResourceVersion(
new LastModifiedResourceVersion());
}
else
{
// deployment mode:
// use message digest over resource content for resource caching
// cache the version information for the lifetime of the application
resourceVersion = new CachingResourceVersion(new MessageDigestResourceVersion());
}
// cache resource with a version string in the filename
resourceCachingStrategy = new FilenameWithVersionResourceCachingStrategy(
resourceVersion);
}
return resourceCachingStrategy;
}
/**
* sets the resource caching strategy
*
* @param strategy
* instance of resource caching strategy
*
* @see IResourceCachingStrategy
* @return {@code this} object for chaining
*/
public ResourceSettings setCachingStrategy(IResourceCachingStrategy strategy)
{
if (strategy == null)
{
throw new NullPointerException(
"It is not allowed to set the resource caching strategy to value NULL. " +
"Please use " + NoOpResourceCachingStrategy.class.getName() + " instead.");
}
resourceCachingStrategy = strategy;
return this;
}
/**
* Sets whether to use pre-minified resources when available. Minified resources are detected by
* name. The minified version of {@code x.js} is expected to be called {@code x.min.js}. For css
* files, the same convention is used: {@code x.min.css} is the minified version of
* {@code x.css}. When this is null, minified resources will only be used in deployment
* configuration.
*
* @param useMinifiedResources
* The new value for the setting
* @return {@code this} object for chaining
*/
public ResourceSettings setUseMinifiedResources(boolean useMinifiedResources)
{
this.useMinifiedResources = useMinifiedResources;
return this;
}
/**
* @return Whether pre-minified resources will be used.
*/
public boolean getUseMinifiedResources()
{
return useMinifiedResources;
}
/**
* @return The comparator used to sort header items.
*/
public Comparator<? super RecordedHeaderItem> getHeaderItemComparator()
{
return headerItemComparator;
}
/**
* Sets the comparator used by the {@linkplain org.apache.wicket.markup.head.ResourceAggregator resource aggregator} for
* sorting header items. It should be noted that sorting header items may break resource
* dependencies. This comparator should therefore at least respect dependencies declared by
* resource references. By default, items are sorted using the {@link PriorityFirstComparator}.
*
* @param headerItemComparator
* The comparator used to sort header items, when null, header items will not be
* sorted.
* @return {@code this} object for chaining
*/
public ResourceSettings setHeaderItemComparator(Comparator<? super RecordedHeaderItem> headerItemComparator)
{
this.headerItemComparator = headerItemComparator;
return this;
}
/**
* A flag indicating whether static resources should have <tt>jsessionid</tt> encoded in their
* url.
*
* @return {@code true} if the jsessionid should be encoded in the url for resources
* implementing
* {@link org.apache.wicket.request.resource.caching.IStaticCacheableResource} when the
* cookies are disabled and there is an active http session.
*/
public boolean isEncodeJSessionId()
{
return encodeJSessionId;
}
/**
* Sets a flag indicating whether the jsessionid should be encoded in the url for resources
* implementing {@link org.apache.wicket.request.resource.caching.IStaticCacheableResource} when
* the cookies are disabled and there is an active http session.
*
* @param encodeJSessionId
* {@code true} when the jsessionid should be encoded, {@code false} - otherwise
* @return {@code this} object for chaining
*/
public ResourceSettings setEncodeJSessionId(boolean encodeJSessionId)
{
this.encodeJSessionId = encodeJSessionId;
return this;
}
}