org.apache.wicket.resource.aggregation.AbstractResourceAggregatingHeaderResponse Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.ops4j.pax.wicket.service Show documentation
Show all versions of org.ops4j.pax.wicket.service Show documentation
Pax Wicket Service is an OSGi extension of the Wicket framework, allowing for dynamic loading and
unloading of Wicket components and pageSources.
/*
* 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.resource.aggregation;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.wicket.Resource;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.markup.html.DecoratingHeaderResponse;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.resource.ResourceUtil;
/**
* A header response that can be used to aggregate resources (primarily resource references) into
* groups that can be rendered after the entire hierarchy of IHeaderContributors have been
* traversed. A subclass of this could use that group to render a single URL to some aggregating
* servlet (for example) that could cut down on the number of HTTP requests the client must make.
*
* Resource references are aggregated according to the key that your subclass creates in the
* {@link #newGroupingKey(ResourceReferenceAndStringData)}. This key is used in a Map, so it needs
* to properly implement hashCode and equals.
*
* If your key does not implement Comparable<KeyClass>, you need to also return a Comparator
* for it from the {@link #getGroupingKeyComparator()} method.
*
* @author Jeremy Thomerson
* @param
* the type of ResourceReferenceCollection returned by
* {@link #newResourceReferenceCollection()} and passed to all the methods that take a
* ResourceReferenceCollection. You will typically just use ResourceReferenceCollection
* for this param, unless you are returning a specific type of
* ResourceReferenceCollection from your subclass.
* @param
* the class of the key that you will create from
* {@link #newGroupingKey(ResourceReferenceAndStringData)}
*/
public abstract class AbstractResourceAggregatingHeaderResponse
extends DecoratingHeaderResponse
{
private final List topLevelReferences = new ArrayList();
/**
* Construct.
*
* @param real
* the wrapped header response
*/
public AbstractResourceAggregatingHeaderResponse(IHeaderResponse real)
{
super(real);
}
@Override
public void renderJavascriptReference(ResourceReference reference)
{
topLevelReferences.add(new ResourceReferenceAndStringData(reference, null, false));
}
@Override
public void renderJavascriptReference(ResourceReference reference, String id)
{
topLevelReferences.add(new ResourceReferenceAndStringData(reference, id, false));
}
@Override
public void renderCSSReference(ResourceReference reference)
{
topLevelReferences.add(new ResourceReferenceAndStringData(reference, null, true));
}
@Override
public void renderCSSReference(ResourceReference reference, String media)
{
topLevelReferences.add(new ResourceReferenceAndStringData(reference, media, true));
}
@Override
public void close()
{
// up until now, no ResourceReference objects have been passed to the real response. we need
// to group them into top-level groups and then render those groups
SortedMap map = new TreeMap(getGroupingKeyComparator());
for (ResourceReferenceAndStringData ref : topLevelReferences)
{
K key = newGroupingKey(ref);
R coll = map.get(key);
if (coll == null)
{
map.put(key, coll = newResourceReferenceCollection());
}
coll.add(ref);
}
// now, render our groups to the real response
Set alreadyRendered = new LinkedHashSet();
for (Entry entry : map.entrySet())
{
renderCollection(alreadyRendered, entry.getKey(), entry.getValue());
}
onAllCollectionsRendered(topLevelReferences);
// finally, we close the real response
super.close();
}
/* methods designed to be overridden if needed */
/**
* creates a ResourceReferenceCollection. If you want a specific type of
* ResourceReferenceCollection for your subclass of
* {@link AbstractResourceAggregatingHeaderResponse}, override this method.
*
* Note that because of the generics definition, you will probably have to cast to R. R is the
* parameter used when creating your subclass defining the type of ResourceReferenceCollection
* this returns and is passed into all methods that take a ResourceReferenceCollection
*
* @return a newly created collection to contain resource references
*/
@SuppressWarnings("unchecked")
protected R newResourceReferenceCollection()
{
return (R)new ResourceReferenceCollection();
}
/**
* This key is what is used to determine how to group (or aggregate) your resources. It must
* implement equals and hashCode correctly so that it can be used as a key in a HashMap. These
* methods must be implemented so that if two references are given over the course of the
* hierarchy traversal, and those two references should be grouped (or aggregated), the keys
* returned for each should equal each other and their hash codes should be equal as well.
*
* Typical implementations should use whether or not the resource reference is CSS as their
* first grouping parameter, since you don't want to render JS and CSS in the same tag (one
* needs to be in a link tag and one in a script tag).
*
* Note that if your grouping key class (K) does not implement Comparable<K>, you must
* also override {@link #getGroupingKeyComparator()} and return a valid comparator that sorts
* keys in the order you want references rendered.
*
* @param ref
* the resource reference with associated data that came from the render*Reference
* methods
* @return a new key used to group the references.
*/
protected abstract K newGroupingKey(ResourceReferenceAndStringData ref);
/**
* This comparator is used to sort the grouping keys that you return from
* {@link #newGroupingKey(ResourceReferenceAndStringData)}.
*
* Note that if your grouping key class (K) implements Comparable<K>, you do not need to
* override this method.
*
* @return a Comparator for K
*/
protected Comparator getGroupingKeyComparator()
{
return null;
}
/**
* When the entire hierarchy has been traversed and {@link #close()} is called, we loop through
* the grouped collections and render them in this method. This method is typically overridden
* to render your collection how you want to render them.
*
* For instance, if you want to aggregate your groups into a single HTTP request, you can
* override this method, create the URL to your aggregation servlet (or {@link Resource}), and
* then call getRealResponse().renderJavascriptReference(yourUrl), or the appropriate
* method to render the URL for a group of CSS references.
*
* @param alreadyRendered
* a set of resource references that have already been rendered in other groups
* @param key
* they grouping key for this group
* @param coll
* the collection of resource references to render
*/
protected void renderCollection(Set alreadyRendered, K key,
R coll)
{
for (ResourceReferenceAndStringData data : coll)
{
renderIfNotAlreadyRendered(alreadyRendered, data);
}
}
/**
* Renders a single resource reference, only if it has not already been rendered. Note that you
* will typically not need to override this method. You should typically override
* {@link #render(ResourceReferenceAndStringData)} directly, which is called from this method if
* the resource reference has not been rendered elsewhere.
*
* @param alreadyRendered
* the set of references that have already been rendered in other groups
* @param data
* the reference (and associated data) to conditionally render.
*/
protected void renderIfNotAlreadyRendered(Set alreadyRendered,
ResourceReferenceAndStringData data)
{
if (!alreadyRendered.contains(data))
{
render(data);
alreadyRendered.add(data);
}
}
/**
* Renders a single resource reference. This is called from
* {@link #renderIfNotAlreadyRendered(Set, ResourceReferenceAndStringData)} for references that
* had not been rendered elsewhere.
*
* @param data
* the reference (and associated data) to conditionally render.
*/
protected void render(ResourceReferenceAndStringData data)
{
ResourceUtil.renderTo(getRealResponse(), data.getReference(), data.isCss(),
data.getString());
}
/**
* After all the collections have been rendered, we call this callback so your subclass can add
* any other logic as needed. For instance, if you are aggregating YUI resources, your
* {@link #renderCollection(Set, Object, ResourceReferenceCollection)} method might have
* rendered only a YUI constructor that loaded all the JS files for each group. Then, you need
* to loop through the references again, and render any JS inside a sandboxed YUI.use()
* statement. You would render those here by creating the YUI.use statement, and call
* getHeaderResponse().renderJavascript(yourJS, null)
*
* @param allTopLevelReferences
* all the references that were rendered by the developers
*/
protected void onAllCollectionsRendered(
List allTopLevelReferences)
{
}
/* other interface methods: */
@Override
public void renderJavascriptReference(String url)
{
// TODO: can we aggregate this? probably shouldn't...
getRealResponse().renderJavascriptReference(url);
}
@Override
public void renderJavascriptReference(String url, String id)
{
// TODO: can we aggregate this? probably shouldn't...
getRealResponse().renderJavascriptReference(url, id);
}
@Override
public void renderCSSReference(String url)
{
// TODO: can we aggregate this? probably shouldn't...
getRealResponse().renderCSSReference(url);
}
@Override
public void renderCSSReference(String url, String media)
{
// TODO: can we aggregate this? probably shouldn't...
getRealResponse().renderCSSReference(url, media);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy