Browsing the 2009 May archive
I would be sitting on a gold mine.
SLOC Directory SLOC-by-Language (Sorted) 37890 src java=37890 5026 contrib-utilities java=5026 4457 contrib-crud java=4457 2259 contrib-mootools java=2259 1235 contrib-generaldao java=1235 1185 contrib-emailmanager java=1185 986 jetty-memcache java=986 961 contrib-cache java=961 787 jmemcached java=786,sh=1 640 contrib-thumbnail java=640 503 contrib-blueprint java=503 181 contrib-snapshot java=181 32 contrib-nicedit java=32 Totals grouped by language (dominant language first): java: 56141 (100.00%) sh: 1 (0.00%) Total Physical Source Lines of Code (SLOC) = 56,142 Development Effort Estimate, Person-Years (Person-Months) = 13.73 (164.80) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) Schedule Estimate, Years (Months) = 1.45 (17.39) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) Estimated Average Number of Developers (Effort/Schedule) = 9.47 Total Estimated Cost to Develop = $ 1,855,214 (average salary = $56,286/year, overhead = 2.40). SLOCCount, Copyright (C) 2001-2004 David A. Wheeler SLOCCount is Open Source Software/Free Software, licensed under the GNU GPL. SLOCCount comes with ABSOLUTELY NO WARRANTY, and you are welcome to redistribute it under certain conditions as specified by the GNU GPL license; see the documentation for details. Please credit this data as "generated using David A. Wheeler's 'SLOCCount'."
One of the first few things I have tackled while creating fabulously40 was stateless pagination. I wanted to keep all the public facing pages stateless to avoid session overhead. Wicket makes pagination brain dead simple at the cost of session use. I wanted to keep the simple programming model yet be completely stateless. Here is my solution to the problem.
Wicket a stateful framework, is actually really good at being stateless.
Example code on how to use it.
update: I would suggest using MixedParamHybridCodingStrategy with the stateless paging navigation to keep links clean.
add(dv = new StatelessDataView<Photo>("pics", getPhotoProvider(), amount, pageParams) {
private static final long serialVersionUID = 1L;
@Override
protected void populateItem(final Item<Photo> arg0) {
}
});
// call back page class, page paramaters passed, the dataview and 12 pagination links.
add(new StatelessSimplePagingNavigator("nav", PhotosPage.class, pageParams, dataView, 12));
The dataview
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.IDataProvider;
public abstract class StatelessDataView<T> extends DataView<T> {
private static final long serialVersionUID = 1L;
private PageParameters pp;
@Override
protected boolean getStatelessHint() {
return true;
}
protected int getPageNumber(final String param) {
String numResult = param;
if(numResult.contains(".wicket-")) {
numResult = numResult.substring(0,numResult.indexOf(".wicket-"));
}
return Integer.valueOf(numResult);
}
public StatelessDataView(final String id, final IDataProvider dataProvider, final PageParameters pp) {
super(id, dataProvider);
this.pp = pp;
if (pp.getString(id) != null) {
int pageNum = getPageNumber(pp.getString(id));
if(pageNum != -1 && pageNum >= 0 && pageNum <= getPageCount()) {
setCurrentPage(getPageNumber(pp.getString(id)));
}
}
}
public StatelessDataView(final String id, final IDataProvider dataProvider, final int itemsPerPage,
final PageParameters pp) {
super(id, dataProvider, itemsPerPage);
this.pp = pp;
if (pp.getString(id) != null) {
int pageNum = getPageNumber(pp.getString(id));
if(pageNum != -1 && pageNum >= 0 && pageNum <= getPageCount()) {
setCurrentPage(getPageNumber(pp.getString(id)));
}
}
}
}
The paging navigator
import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.navigation.paging.IPageable;
import org.apache.wicket.markup.html.navigation.paging.IPagingLabelProvider;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigation;
public class StatelessSimplePagingNavigator extends SimplePagingNavigator {
private static final long serialVersionUID = 1L;
private Class clazz;
private PageParameters pp;
private int current_page;
private int page_count;
private String pageable_id;
private String anchor = null;
@Override
protected boolean getStatelessHint() {
return true;
}
public StatelessSimplePagingNavigator(final String id, final Class clazz, final PageParameters pp,
final IPageable pageable, final int viewsize) {
this(id, clazz, pp, pageable, viewsize, false);
}
public StatelessSimplePagingNavigator(final String id, final Class clazz, final PageParameters pp,
final IPageable pageable, final int viewsize, final boolean anchorSelf) {
super(id, pageable, viewsize, anchorSelf);
this.clazz = clazz;
this.pp = pp;
this.current_page = pageable.getCurrentPage();
this.page_count = pageable.getPageCount();
this.pageable_id = ((Component) pageable).getId();
}
public StatelessSimplePagingNavigator(final String id, final Class clazz, final PageParameters pp,
final IPageable pageable, final int viewsize, final String anchor) {
super(id, pageable, viewsize, false);
this.clazz = clazz;
this.pp = pp;
this.current_page = pageable.getCurrentPage();
this.page_count = pageable.getPageCount();
this.pageable_id = ((Component) pageable).getId();
this.anchor = anchor;
}
@Override
protected void onBeforeRender() {
super.onBeforeRender();
addOrReplace(newStatelessPagingNavigationLink("next", pageable_id, current_page, 1));
addOrReplace(newStatelessPagingNavigationLink("prev", pageable_id, current_page, -1));
}
@Override
protected PagingNavigation newNavigation(final IPageable pageable, final IPagingLabelProvider labelProvider) {
PagingNavigation pg = new PagingNavigation("navigation", pageable, labelProvider) {
private static final long serialVersionUID = 1L;
@Override
protected Link newPagingNavigationLink(final String id, final IPageable pageable, final int pageIndex) {
Component c = (Component) pageable;
// PageParameters p1 = new PageParameters();
// p1.putAll(pp);
pp.put(c.getId(), String.valueOf(pageIndex));
BookmarkablePageLink<Object> lnk = new BookmarkablePageLink<Object>(id, clazz, pp) {
private static final long serialVersionUID = 1L;
@Override
protected void onComponentTag(final ComponentTag arg0) {
super.onComponentTag(arg0);
if (anchor != null) {
if(arg0.getString("href") != null) {
String href = arg0.getString("href").toString();
String atag = anchor.contains("#") ? anchor : "#" + anchor;
arg0.put("href", href + atag);
}
}
}
@Override
public boolean isEnabled() {
return (current_page != pageIndex);
}
};
if (isAnchorSelf()) {
lnk.setAnchor(StatelessSimplePagingNavigator.this);
}
return lnk;
}
};
pg.setViewSize(getViewsize());
return pg;
}
@Override
protected Link<Object> newPagingNavigationIncrementLink(final String id, final IPageable pageable, final int increment) {
Component c = (Component) pageable;
final int page = Integer.valueOf(pp.getString(c.getId()));
pp.put(c.getId(), String.valueOf(page + increment));
return new BookmarkablePageLink<Object>(id, clazz, pp) {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return (page + increment) >= 0;
}
};
}
protected Link<Object> newStatelessPagingNavigationLink(final String id, final String pageableId, final int currentPage,
final int increment) {
// PageParameters p1 = new PageParameters();
// p1.putAll(pp);
pp.put(pageableId, String.valueOf(currentPage + increment));
return new BookmarkablePageLink<Object>(id, clazz, pp) {
private static final long serialVersionUID = 1L;
@Override
public boolean isVisible() {
return (currentPage + increment) < page_count && (currentPage + increment) >= 0;
}
};
}
@Override
protected Link<Object> newPagingNavigationLink(final String id, final IPageable pageable, final int pageNumber) {
Component c = (Component) pageable;
// PageParameters p1 = new PageParameters();
// p1.putAll(pp);
pp.put(c.getId(), String.valueOf(pageNumber));
return new BookmarkablePageLink(id, clazz, pp);
}
}
Which inherits from
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.navigation.paging.IPageable;
import org.apache.wicket.markup.html.navigation.paging.IPagingLabelProvider;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigation;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigator;
public class SimplePagingNavigator extends PagingNavigator {
private static final long serialVersionUID = 1L;
private int viewsize = 0;
private boolean anchorSelf = false;
public SimplePagingNavigator(final String id, final IPageable pageable, final int viewsize) {
this(id, pageable, viewsize, false);
}
public SimplePagingNavigator(final String id, final IPageable pageable, final int viewsize, final boolean anchorSelf) {
super(id, pageable);
setOutputMarkupId(true);
this.setViewsize(viewsize);
this.setAnchorSelf(anchorSelf);
}
@Override
protected void onBeforeRender() {
if (get("navigation") != null) {
remove("navigation");
}
if (get("prev") != null) {
remove("prev");
}
if (get("next") != null) {
remove("next");
}
super.onBeforeRender();
if (get("first") != null) {
remove("first");
}
if (get("last") != null) {
remove("last");
}
if (getViewsize() != 0) {
getPagingNavigation().setViewSize(getViewsize());
}
}
@Override
protected PagingNavigation newNavigation(final IPageable pageable, final IPagingLabelProvider labelProvider) {
PagingNavigation pg = new PagingNavigation("navigation", pageable, labelProvider) {
private static final long serialVersionUID = 1L;
@Override
protected Link newPagingNavigationLink(final String id, final IPageable pageable, final int pageIndex) {
Link lnk = (Link) super.newPagingNavigationLink(id, pageable, pageIndex);
if (isAnchorSelf()) {
lnk.setAnchor(SimplePagingNavigator.this);
}
return lnk;
}
};
pg.setViewSize(getViewsize());
return pg;
}
@Override
public boolean isVisible() {
if (getPageable() != null) {
return (getPageable().getPageCount() > 1);
}
return true;
}
public void setAnchorSelf(final boolean anchorSelf) {
this.anchorSelf = anchorSelf;
}
public boolean isAnchorSelf() {
return anchorSelf;
}
public void setViewsize(final int viewsize) {
this.viewsize = viewsize;
}
public int getViewsize() {
return viewsize;
}
}
I have dumped varnish as our primary cache due to multiple failures of service. I have tried to make it work but varnish kept insisting on producing 503 XID backend failures on perfectly healthy backends. I have tried doing all types of crazy configuration hacks such as forcing varnish to retry backends via a round-robin director. It did not work out all too well since the round trip added latency when varnish had to re-fetch the document multiple times. The final straw that broke the camel’s back was when varnish configured for a 256mb malloc store grew to an astonishing size of 780mb+ RSS.
I have switched to squid-3 and so far it has been stable and fast. I will later post a matching squid configuration to the one below that does the same thing.
Squid-3 will require this patch for it to compile on Solaris.
Varnish on Solaris is a dud.
List of failures
1. Producing 503 responses for perfectly healthy backends. Backend never even gets contacted.
2. Growing to a crazy size when using the malloc implementation.
3. Segfaulting every hour on the hour with the newest trunk r4080+
Here is the configuration I have used. Feel free to use it if varnish works for you.
#
# This is a basic VCL configuration file for varnish. See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# $Id: default.vcl 1818 2007-08-09 12:12:12Z des $
#
# Default backend definition. Set this to point to your content
# server.
# my wonderful re-try hack, that kinda works.
director www_dir round-robin {
{ .backend = { .connect_timeout = 2s; .host="127.0.0.1"; .port="8001"; } }
{ .backend = { .connect_timeout = 2s; .host="127.0.0.1"; .port="8001"; } }
{ .backend = { .connect_timeout = 2s; .host="127.0.0.1"; .port="8001"; } }
{ .backend = { .connect_timeout = 2s; .host="127.0.0.1"; .port="8001"; } }
}
#backend default { .host = "127.0.0.1"; .port = "8089"; .connect_timeout = 2s; }
sub vcl_recv {
remove req.http.X-Forwarded-For;
set req.http.X-Forwarded-For = client.ip;
set req.grace = 2m;
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpeg|jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|ico|swf|flv|dmg)") {
# No point in compressing these
remove req.http.Accept-Encoding;
} elsif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
# unkown algorithm
remove req.http.Accept-Encoding;
}
}
# don't trust MSIE6
# if (req.http.user-agent ~ "MSIE [1-6]\.") {
# remove req.http.Accept-Encoding;
# }
if (req.http.host == "jira.fabulously40.com") {
pipe;
}
if (req.request == "GET" || req.request == "HEAD") {
if ( req.url ~ "\.(xml|gif|jpg|swf|css|png|jpeg|tiff|tif|svg|ico|pdf|ico|swf)") {
remove req.http.cookie;
lookup;
}
# avoid caching jsps
if ( req.url ~ "\.js([^p]|$)" ) {
remove req.http.cookie;
lookup;
}
}
# don't bother caching large files
if(req.url ~ "\.(mp3|flv|mov|mp4|mpg|mpeg|avi|dmg)") {
pipe;
}
if (req.request != "GET" && req.request != "HEAD") {
pipe;
}
if (req.request == "POST") {
pipe;
}
if (req.http.Expect || req.http.Authorization || req.http.Authenticate || req.http.WWW-Authenticate) {
pipe;
}
# pipe pages with these cookies set
if (req.http.cookie && req.http.cookie ~ "_.*_session=") {
pipe;
}
if (req.http.cookie && req.http.cookie ~ "JSESSIONID=") {
pipe;
}
if (req.http.cookie && req.http.cookie ~ "PHPSESSID=") {
pipe;
}
if (req.http.cookie && req.http.cookie ~ "wordpress_logged_in") {
pipe;
}
lookup;
}
sub vcl_error {
# retry on errors
if (obj.status == 503) {
if ( req.restarts < 12 ) {
restart;
}
}
}
sub vcl_fetch {
# don't cache when these cookies are in place
if(beresp.http.Location || beresp.http.WWW-Authenticate) {
pass;
}
if(beresp.http.cookie && beresp.http.cookie ~ "JSESSIONID=") {
pass;
}
if(beresp.http.Set-Cookie && beresp.http.Set-Cookie ~ "JSESSIONID=") {
pass;
}
if(beresp.http.cookie && beresp.http.cookie ~ "_.*_session=") {
pass;
}
if(beresp.http.Set-Cookie && beresp.http.Set-Cookie ~ "_.*_session=") {
pass;
}
if(beresp.http.cookie && beresp.http.cookie ~ "PHPSESSID=") {
pass;
}
if(beresp.http.Set-Cookie && beresp.http.Set-Cookie ~ "PHPSESSID=") {
pass;
}
if(beresp.http.cookie && beresp.http.cookie ~ "wordpress_logged_in") {
pass;
}
if(beresp.http.Set-Cookie && beresp.http.Set-Cookie ~ "wordpress_logged_in") {
pass;
}
if(beresp.http.Cache-Control && beresp.http.Cache-Control ~ "no-cache") {
pass;
}
if(beresp.http.Pragma && beresp.http.Pragma ~ "no-cache") {
pass;
}
# avoid defaults since we *want* pages cached with cookies
# if (!beresp.cacheable) {
# pass;
# }
# if (beresp.http.Set-Cookie) {
# pass;
# }
#cache for 30 minutes..
if((beresp.http.Cache-Control !~ "max-age" || beresp.http.Cache-Control !~ "s-maxage") && beresp.ttl < 1800s) {
set beresp.ttl = 1800s;
}
set beresp.grace = 2m;
# anonymous users get 10 min delay
if(beresp.http.Content-Type && beresp.http.Content-Type ~ "html" && (beresp.http.Cache-Control !~ "max-age" || beresp.http.Cache-Control !~ "s-maxage")) {
set beresp.ttl = 600s;
}
# remove server affinity cookie from cached pages.
if(beresp.http.Set-Cookie && beresp.http.Set-Cookie ~ "X-SERVERID=") {
remove beresp.http.Set-Cookie;
}
if(beresp.http.Set-Cookie && beresp.http.Set-Cookie ~ "SERVERID=") {
remove beresp.http.Set-Cookie;
}
if(beresp.http.X-Backend) {
remove beresp.http.X-Backend;
}
deliver;
}
I am about to run out the house for the Labor Day weekend, quickie post on my new coding url strategy.
MixedParamHybridUrlCodingStrategy lets you keep stateful multi-pagemap URLs clean while using mixed parameters.
Example…
This will mount “/questions/stupid-category” and convert it to…
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.wicket.IRequestTarget;
import org.apache.wicket.PageParameters;
import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy;
import org.apache.wicket.request.target.coding.HybridUrlCodingStrategy;
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.util.value.ValueMap;
public class MixedParamHybridUrlCodingStrategy extends HybridUrlCodingStrategy {
private final String[] parameterNames;
public MixedParamHybridUrlCodingStrategy(final String mountPath, final Class pageClass) {
super(mountPath, pageClass);
this.parameterNames = new String[] {};
}
public MixedParamHybridUrlCodingStrategy(final String mountPath, final Class pageClass,final boolean refresh) {
super(mountPath,pageClass,refresh);
this.parameterNames = new String[] {};
}
public MixedParamHybridUrlCodingStrategy(final String mountPath, final Class pageClass,final String[] params) {
super(mountPath,pageClass,false);
this.parameterNames = params;
}
public MixedParamHybridUrlCodingStrategy(final String mountPath, final Class pageClass,final boolean refresh,final String[] params) {
super(mountPath,pageClass,refresh);
this.parameterNames = params;
}
@Override
protected void appendParameters(final AppendingStringBuffer url, final Map parameters)
{
Set parameterNamesToAdd = new HashSet(parameters.keySet());
// Find index of last specified parameter
boolean foundParameter = false;
int lastSpecifiedParameter = parameterNames.length;
while (lastSpecifiedParameter != 0 && !foundParameter)
{
foundParameter = parameters.containsKey(parameterNames[–lastSpecifiedParameter]);
}
if (foundParameter)
{
for (int i = 0; i <= lastSpecifiedParameter; i++)
{
String parameterName = parameterNames[i];
final Object param = parameters.get(parameterName);
String value = param instanceof String[] ? ((String[])param)[0] : (String)param;
if (value == null)
{
value = "";
}
if (!url.endsWith("/"))
{
url.append("/");
}
url.append(urlEncodePathComponent(value));
parameterNamesToAdd.remove(parameterName);
}
}
if (!parameterNamesToAdd.isEmpty())
{
boolean first = true;
final Iterator iterator = parameterNamesToAdd.iterator();
while (iterator.hasNext())
{
url.append(first ? ‘?’ : ‘&’);
String parameterName = (String)iterator.next();
final Object param = parameters.get(parameterName);
String value = param instanceof String[] ? ((String[])param)[0] : (String)param;
url.append(urlEncodeQueryComponent(parameterName)).append("=").append(
urlEncodeQueryComponent(value));
first = false;
}
}
String pageMap = (String)parameters.get(WebRequestCodingStrategy.PAGEMAP);
if (pageMap != null)
{
pageMap = WebRequestCodingStrategy.encodePageMapName(pageMap);
if (!url.endsWith("/"))
{
url.append("/");
}
url.append(WebRequestCodingStrategy.PAGEMAP).append("/").append(
urlEncodePathComponent(pageMap));
}
}
@Override
public CharSequence encode(final IRequestTarget requestTarget) {
String url = String.valueOf(super.encode(requestTarget));
if(url.contains(".wicket")) {
// Rewrite URL from.. /foo?bar=5.wicket-xxx to /foo.wicket-xxx?bar=5
return url.replaceAll("(.*)\\?(.*)\\.(wicket-[0-9]+)$", "$1.$3?$2");
}
return url;
}
@Override
protected ValueMap decodeParameters(final String urlFragment, final Map urlParameters)
{
PageParameters params = new PageParameters();
if(urlFragment == null) {
return params;
}
// Add all url parameters and normalize
for(Iterator<Map.Entry> itr = urlParameters.entrySet().iterator();itr.hasNext();) {
Map.Entry<String,String[]> e = itr.next();
if(e.getValue() != null) {
String val = (e.getValue())[0];
if(val != null) {
if(val.contains(".wicket")) {
val = val.substring(0,val.indexOf(".wicket"));
urlParameters.put(e.getKey(), val);
}
}
}
}
params.putAll(urlParameters);
String urlPath = urlFragment;
if (urlPath.startsWith("/"))
{
urlPath = urlPath.substring(1);
}
if (urlPath.length() > 0)
{
String[] pathParts = urlPath.split("/");
if (pathParts.length > parameterNames.length)
{
throw new IllegalArgumentException(
"Too many path parts, please provide sufficient number of path parameter names");
}
for (int i = 0; i < pathParts.length; i++)
{
if(pathParts[i].contains(".wicket")) {
pathParts[i].substring(0,pathParts[i].indexOf(".wicket"));
}
if (WebRequestCodingStrategy.PAGEMAP.equals(pathParts[i]))
{
params.put(WebRequestCodingStrategy.PAGEMAP,
WebRequestCodingStrategy.decodePageMapName(urlDecodePathComponent(pathParts[i])));
}
params.put(parameterNames[i], urlDecodePathComponent(pathParts[i]));
}
}
return params;
}
}
GrizzlyConnector patch for Jetty to work with QueuedThreadPool
Leave a comment | Filed under administration programmingThis is a late night post, so I am just going to make it short. This patch lets you use QueuedThreadPool with the Grizzly Connector. This is a monkey patch, getMaxThreads() should be moved up into the Thread Interface.
--- GrizzlyConnection-old.java Sat May 2 01:08:02 2009 +++ GrizzlyConnector.java Sat May 2 00:56:37 2009 @@ -51,6 +51,8 @@ import org.mortbay.jetty.webapp.WebAppContext; import org.mortbay.log.Log; import org.mortbay.thread.BoundedThreadPool; +import org.mortbay.thread.QueuedThreadPool; +import org.mortbay.thread.ThreadPool; /* ------------------------------------------------------------------------------- */ /** @@ -178,8 +180,13 @@ controller.setProtocolChainInstanceHandler(instanceHandler); Pipeline pipeline = new DefaultPipeline(); - pipeline.setMaxThreads( - ((BoundedThreadPool)getServer().getThreadPool()).getMaxThreads()); + if(getServer().getThreadPool() instanceof BoundedThreadPool) { + pipeline.setMaxThreads( + ((BoundedThreadPool)getServer().getThreadPool()).getMaxThreads()); + } else { + pipeline.setMaxThreads( + ((QueuedThreadPool)getServer().getThreadPool()).getMaxThreads()); + } controller.setPipeline(pipeline); }


(2 votes, average: 4.50 out of 5)