 |
My Favorite (Dead) Java Boilerplate
Posted by kgh on November 13, 2005 at 04:03 PM | Comments (31)
In the Java platform we have tended to focus on adding lots of
power and flexibility. That's great, but sometimes that power
and flexibility can get in the way of doing common tasks. As
part of the Ease-of-Development initiative we have been focusing
on simplifying common tasks and getting rid of unnecessary
boilerplate code.
Here are my five of my favorite cleanups so far:
#1: Opening a Text File
In JDK 1.1 to 1.4, in order to open a simple text output file
you needed to do:
FileWriter fout = new FileWriter("fred.txt");
BufferedWriter bout = new BufferedWriter(fout);
PrintWriter pout = new PrintWriter(bout);
Say what? Why are we having to type three "new"s in order
to do what should be a simple operation?
In Tiger we have finally added direct support for the common case
and now you can do:
PrintWriter pout = new PrintWriter("fred.txt");
This is an interesting example of a common glitch in our thinking.
In the Java platform we often like to provide lots of well designed,
well separated components that can be plugged together in interesting
ways. In fact some people might argue be that the design is cleaner
if the PrintWriter class doesn't know anything about files. Well,
personally, I don't think so. I think it is good to provide clean,
well separated components, but we also need to provide simple
shortcuts to support the most common use cases.
#2: Avoiding the Content Pane Pain
In JDK 1.1 to 1.4, if you wanted to add a Swing GUI component to
a container you simply said container.add(component).
Well, yes, except that if the container happened to be a JFrame
you would get a helpful runtime exception saying that you ought
to be saying
frame.getContentPane().add(component);
Umm, say what? Rather than raising the exception, couldn't the
JFrame.add method itself call JFrame.getContentPane().add()?
Yes it could. And in Tiger it does. Now you can just call add,
as you would with any other container object.
frame.add(component);
This only saves a few keystrokes, so is this really a big deal?
Yes. The real saving is that you can avoid having to learn
a whole new unnecessary concept. The reason that the
JFrame.add method originally threw an exception was because
JFrames actually support three different panes (content,
glass and root),
and it was
considered important to educate developers about those choices.
Well, I've written various Swing applications over the
years and I've never actually found the need to exploit the various
different panes. The old behavior of JFrame.add
forced me to go away and learn about panes. And that was
distracting and unhelpful. The lesson here is that (once again)
it is normally better to provide simple sensible defaults and to
avoid forcing people to learn about complex options.
#3: Self Registering JDBC Drivers
Since JDK 1.1, in order to load a JDBC driver, you needed to do:
Class.forName("com.fred.FredDriver");
Connection con = DriverManager.getConnection("jdbc:fred:fredsite.com");
Umm, what exactly is that Class.forName doing there?
This one is my fault. Back in the early days of JDBC, we needed a way
for the JDBC DriverManager to locate drivers. We arranged that newly
loaded driver classes would register with the
DriverManager. And then we asked that developers call
"Class.forName" to force the driver class to be loaded.
Sigh. This is an ugly wart. I'm happy to report that this one is
going away as part of JDBC 4.0 in
Mustang. The JSR-221 Expert Group
is adding a new mechanism so that the JDBC DriverManager can locate
and load driver classes without the need for developers to explicitly
type Class.forName. So you will be able to just do the obvious:
Connection con = DriverManager.getConnection("jdbc:fred:fredsite.com");
#4: Locating Resources in J2EE
In J2EE 1.4, if you wanted to locate a reference to a remote EJB you needed to type:
Context context = new InitialContext();
Object obj = context.lookup("fred");
FredHome fred = (FredHome) PortableRemoteObject.narrow(obj, FredHome.class);
Yikes. What on earth is going on with that PortableRemoteObject.narrow?
I have to confess that I'm one of the prime culprits here and, given some of
the constraints, it may have been unavoidable. But I think this one definitely does
win "Ugly Boilerplate of the Year".
As part of Java EE 5,
there are now specific mechanisms for dependency
injection, so you can now replace that code with a simple annotated
field definition:
@Resource FredHome fred;
And then the Java EE runtimes will take care of locating the resource,
doing the "narrow" for you and injecting the resource object into your field.
By default the resource name is inferred from the field name and the
type is inferred from the field type.
This is an example of using JSR-175 annotations to restructure how we
handle a common task so that it can become much simpler. I'm very
excited with what is happening with annotations as part of Java EE 5
- I think it is allowing us to greatly simplify Java EE programming.
And I'd like to find more ways to use annotations in Java SE, too.
#5: Iterating over Collections
My last example is from Tiger. We've all been used
to typing some standard boilerplate for iterating over collections, such as:
Vector<Wombat> v = getWombats();
Enumeration<Wombat> e = v.elements();
while (e.hasMoreElements()) {
Wombat w = e.nextElement();
...
}
Josh Bloch successfully argued for a language change to make it easier
to iterate over both collections and arrays, so now in Tiger we can do:
Vector<Wombat> v = getWombats();
for (Wombat w : v) {
...
}
We all knew this would be useful, but I have been really surprised by how
much I have enjoyed using it, for both arrays and collections. This
has turned into one of my favorite Tiger features. The resulting code
is distinctly easier to read, partly because we have managed to eliminate
an unnecessary local variable.
This is an example of fixing a boilerplate problem we barely realized
we had. I'd like to find a few more things like this!
To be continued...
I'll continue this topic in my next blog.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
#1 is a useful enhancement, yet you still can't create a FileReader/FileWriter with a specific encoding, hence:
InputStream in1 = new FIleInputStream(file);
InputStreamReader reader = new InputStreamReader(in1, encoding);
plus, potentially a BufferedReader/Writer...
Posted by: scolebourne on November 14, 2005 at 06:54 AM
-
scolebourne ,
Did you see the PrintWriter constructor that takes a file name and charset?
public PrintWriter(String fileName, String csn)
Posted by: rufus on November 14, 2005 at 07:56 AM
-
That's some really special code you've got there in those PrintWriter constructors.
If the OutputStreamWriter throws UnsupportedEncodingException, OutOfMemoryError, NullPointerException or something else, then the FileOutpuStream is not closed. Nor is there any way for client code to close it.
Posted by: tackline on November 14, 2005 at 09:09 AM
-
We used to have lots of boilerplate code for our applications like follows:
IProcessingDialog dialog = ProcessingDialogFactory.createDialog(
"Doing something","Some long message", getSomeIcon());
Runnable task = new Runnable(){
public void run(){
try{
dialog.open();
// do some processing work
doWork();
dialog.setProgress(50);
doNextWork();
dialog.setProgress(100);
}catch(Exception1 e){
// show an error with bug reporting
ErrorManager.getInstance().showErrorWithBugReport(e,
"User explanation");
}catch(Exception2 e2){
// show some error without bug reporting
ErrorManager.getInstance().showError(e, "User explanation");
}finally{
dialog.close();
}
}
};
TaskRunner.runInAppropriateThread(task);
The above workflow is quite common when developing java applications and can easily be refactored to make it an easy one liner. If there was a standard mechanism (pluggable of course) that could be used to automate this workflow beginners would be much more inclined to make "safe" gui apps, enhancing the user experience of java apps in general.
Another area is that of actions. We built our own Action factory for easily creating actions, which enables the simple creation of actions with icons, tooltips, mnemonics etc. Not only should it do this, but enhance user experience. For example, if you watch users interact with your applications you will find the odd user who always double clicks everything. This may wreak havoc in some circumstances, so you dont want your action to run twice. The action factory should make it easy to specify the minimum time between successive clicks. It should also handle disabling the action after it is clicked and while any tasks are running so the user knows it is unclickable or processing. Also, cursor management should be included. All the things that matter to users, and are possible with java but left up to the developer.
The inclusion of boilerplate like this in the jre with smart defaults will drastically improve the user experience of java applications out of the box, and the development speed of beginning users in my opinion.
Posted by: profiler on November 14, 2005 at 03:37 PM
-
How about dropping the need for generics in the new enchanced for loop. Really the compiler should be able to do the downcasting for me.
regards,
Kirk
Posted by: kcpeppe on November 15, 2005 at 12:53 AM
-
Would like to second the comment by "profile"; but I would also like to add a comment about internationalizing Swing UI components. For each component you might have to make two or more method calls. Instead we use a class called ResourcePicker to apply the resources direct from the ResourceBundle using the Component "Name" property to look them up. You can see some more details in RFE 6313125.
Also using the "Name" property allow you to create UI tests in tools such as Abbot that are locale independant.
Posted by: gdavison on November 15, 2005 at 05:05 AM
-
Hi Graham,
Groovy has a lot of features that boost productivity by making complex tasks simpler to achieve and simple tasks a breeze. Having real closures would make iterating over collections simpler without the need for additional artificial constructs (for loops). Coupled with some ruby style convenience methods, and strong autoboxing - the for loop could be replaced with the likes of:-
int x = 10;
x.times ({ count | System.out.println(" counting " + count)});
and iteration over a collection
map.each({key, value | System.out.println("key = " + key + ", value = " + value)});
More powerful reflection utils would open up the possibility of developers producing simpler APIs for complex tasks. E.g. a mechanism for adding "virtual" methods to a class would enable groovy style builders in Java.
An application-wide flag for converting Checked exceptions into RuntimeExceptions could be useful too. A large percentage of Java code seems to be cluttered boiler plate try/catch/finally code that rarely does anything useful with the caught exceptions.
The Xml apis are powerful, but verbose. A simpler api for the 90% of simple day to day tasks would be appreciated - building an XML document and writing it as a string etc. Take a look at REXML for an example of how easy it could be. And while we're looking at files how about file.getText() - again from Groovy - as a convenvience method?
It's great to see someone at Sun looking into this - after ten years Java needs to move up to a higher level of abstraction!
Posted by: johnmcclean on November 15, 2005 at 05:17 AM
-
It's easy to create a default button on a JDialog that has it's action invoked when the enter key is pressed. Why not have an easy way of mapping a cancel button as well? This is an ease of use thing that would help out quite a bit.
Posted by: rabbe on November 15, 2005 at 05:19 AM
-
Some small things:
accessing the last element of a List:
instead of using get(list.size()-1) add a simple method as: getLast()
a simple filter method maybe (i did not think of the details of it)?
public List filter(Comparator c)
if the elements in the list complies the comparator result as 0, it returns a sublist of them.
this can be a part of Collection interface or Collections class.
i think language level changes (like Groovy) should not be included that fast. i am happy with Java's consistency and simplicity. well thought Convenience methods are better.
Posted by: ahmetaa on November 15, 2005 at 07:39 AM
-
'profiler' beat me to it. I've long thought a really useful addition to the language would be the ability to just spawn a block of code off into its own thread.
// Do something
thread
{ // Create a new Runnable with this code and start it
}
// Do more things
In the above I've used a new keyword thread, but I'm sure an intuative syntax could be agreed upon which didn't have this drawback. Now if Java also had the in-built notion of a queue of Runnables, then we could do...
thread(queue)
{ // Create a Runnable and append it to the end of worker thread 'queue'
}
This would make writing certain network code and most event handlers a lot easier, clearer, and hopefully promote good practice.
Posted by: javakiddy on November 15, 2005 at 08:38 AM
-
Quoting JohnMcClean "... after ten years Java needs to move up to a higher level of abstraction! ..."
On the contrary, I think the level Java's abstraction should be lowered. What we have today is a higly abstract api (for ex: as Graham pointed, the java.io is a thing of OO Beauty). However, common coders (not even programmers) do not understand that high a level of abstraction (and the design patterns) used.
What we need is a set of Helper, Utility or Convenience classes and/or methods in the **standard Java API**. For example, sending a simple email with attachments should be a walk in the park without having to learn 10 different classes and writing 40 lines of code.
One special request from my side: While you are at this Ease-of-development, for heaven's sake, please provide some appenders/handlers that are similar to Log4J. That way, we can all burry the ugly, Jakarta Commons Logging classes!
Posted by: chakrayadavalli on November 15, 2005 at 01:23 PM
-
There are a few things I miss in Java:
Easy use of hashmaps:
HashMap myMap = new HashMap();
myMap["background-color"] = "red";
myMap["foreground-color"] = "black";
Easy use of transactions:
transaction
{
Runtime.getRuntime().exec("format c:");
}
commit
{
System.out.println("Done");
}
rollback(RollbackException e)
{
System.out.println("Hard drive saved!");
}
Posted by: weelink on November 15, 2005 at 01:38 PM
-
How about allow a counter to be specified in the enhanced for loop?:
Collection c = new Collection();
for(String s : c : int i) {
System.out.println(i + " " + si);
}
Posted by: abruegl on November 15, 2005 at 03:22 PM
-
In #4 you had: @Resource FredHome fred;
But 'Resource' is a very abstract kind of concept. It could mean anything from memory to disk space to something I expect the system to go get for me, to configuration settings.
Wouldn't it be much cleaer to say something like:
@EJB FredHome fred;
I confess to not having looked at that part of the annotations spec recently, so I realise that @EJB is probably used somewhere else already?
... which makes me wonder whether some kind of dot notation would be cool for annotations (I'm thinking here of some kind of IDE integration, so you could start typing @EJB.r and it pops up a list of options like... umm... remoteReference, remoteInterface... etc.
Whereas obviously having to type in in full @EJB.remoteReference would be bothersome.
Is there a good explanation of annotations, which doesn't involve handwaving and 'and then the magic happens'? I confess to liking the concept a lot, but not being a fan of black boxes, especially magic black boxes.
Maybe I need to play around with writing my own annotations, is there a tutorial for that?
Posted by: rickcarson on November 15, 2005 at 05:46 PM
-
Oh, those wishes!
Graham, the beautiful language of yours, it's pure joy.
And the enhancements and simplifications of another beautiful language - Java. It's great and it's getting greater.
I really like the idea of "default behaviors" of complex components, but I would miss the fexibility I get by composition. For example I like the I/O constructs and I certainly like to target the different panes in the JFrame - it's an excellent architecture to play with.
So to keep the the flexible and complex constructs, while offering simple and stright forward ways of doing things in the standard cases is the way to go.
Thanks for an enlightening article!
/Bertil
Posted by: petit on November 15, 2005 at 05:48 PM
-
How about a language construct or annotation that makes explicitly creating getters and setters for fields in a JavaBean unnecessary?
Posted by: barin on November 15, 2005 at 07:39 PM
-
I'm responsible for the JFrame.add() boilerplate item (#2). Since it
seems to have come in for the harshest criticsm ("distracting and
unhelpful" - ouch), I thought I'd explain why it was there.
See
JFrame.add() contentPane Pain: The Complete Story.
Posted by: hansmuller on November 16, 2005 at 11:42 AM
-
Easy delegator/wrapper implementations:
class WrapperImpl implements Type {
@Delegator Type member
WrapperImpl(Type target) {
this.member = target;
}
}
All calls to the wrapper impls should automatically delegated to member, except if a method have been explicitly implemented.
Posted by: joehni on November 16, 2005 at 11:54 AM
-
A shorthand notation for anonymous inner classes that only have one method. This would be ubiquitous in scope.
Up to 1.5:
Thread t = new Thread(new Runnable() {
public void run() {
// Do something
}
});
Proposal for Mustang:
Thread t = new Thread(new Runnable() {
// Do something
});
The anonymous inner class syntax is so ugly, that it really discourages good practice is many cases. e.g. using something like Jakarta Commons Collections Predicate to filter a collection is not really a viable alternative to a for loop due to ugly syntax.
Up to 1.5:
Collection items = ...
CollectionUtils.filter(items, new Predicate<String>() {
public boolean evaluate(String item) {
return item.equals("hello");
}
});
Proposal for Mustang:
Collection items = ...
CollectionUtils.filter(items, new Predicate<String>(item) {
return item.equals("hello");
});
The Predicate example also shows the proposed syntax for when the method has parameters.
The scope for removing boiler plate code by the above syntax is enormous.
Implement this in Mustang and I'll buy you a beer! :-)
Posted by: andrewom on November 16, 2005 at 08:55 PM
-
hi :
how about this??
Proposal for Mustang:
Collection items = ...
CollectionUtils.filter(items, new Predicate().evaluate(String item) {
return item.equals("hello");
});
Posted by: fcmmok on November 16, 2005 at 10:57 PM
-
Also, how to improve these codes?
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation((int)screenSize.getWidth()/2, (int)screenSize.getHeight()/2);
&&
while (fin.hasNext()) {
line = fin.nextLine();
line = line.trim();
if (!"".equals(line)) {
String[] tokens = line.split("\t");
for (String s : tokens) {
s = s.trim();
}
myCollection.add(tokens);
}
}
Posted by: fcmmok on November 17, 2005 at 11:25 AM
-
I can second the delegate suggestion but expand it for dynamic proxies for the purpose of unit testing.
From my perspective, I like to use dependency injection, and inject mock-object implementation of a dependency that returns some canned data to the object that I am testing. Often the test case will end up calling only one method on the mock, so I want to implement that, but I don't want to bother implementing all the other methods defined in the interface.
So to do this, I end up writing the invocation handler, then the instatiation of the mock. This amounts to a lot of lines of code just to make a mock that returns some canned value:
class MockDynamicProxyInvocationHandler implements InvocationHandler
{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
final String methodName = method.getName();
if (methodName.equals("methodToTest"))
{
mMessageObj = (Serializable) args[0];
return "Canned Value"; // some canned value for test
}
throw new RuntimeException(
"TestCase error: unanticipated method call in InvocationHandler:" + methodName);
}
}
MyInterface mockObject =
(MyInterface) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{MyInterface.class},
new MockDynamicProxyInvocationHandler ());
What I really want is to create my mock on the fly as an anonymous inner class by not have to implenent all the methods defined in the interface. Let other methods throw an exception if called. It would make unit testing a lot easier.
Posted by: msimpson4 on November 18, 2005 at 05:47 PM
-
I've always thought that the JDBC-driver was an brilliant example of plugin-design, with the small wart of having to load the class, and I have used similar mechanisms whenever possible. How are you able to omit the Class.forName? Do you scan all the classes in the classloader for implementations of JDBC drivers? Isn't that very costly?
I'd also like to second andrewom's proposal. Easier closures address almost half the complaints on this thread.
rickcarson @EJB is horrible compared to @Resources. Good annotations decribe intent, not mechanism. (Example: @Confidential is much much better than @Encrypted, @ReadOnly rather than @Getter)
Posted by: jhannes on November 19, 2005 at 05:30 AM
-
I've read over the suggestions here and I liked the idea of the thread keyword, though perhaps it is a little before it's time. Some of the other ideas seemed very domain-specific, some, perhaps, trite.
I was really excited many moons ago about the notion of JavaBeans, however, the cumbersome nature of them as well as the disparate nature of their intended use makes them an unfeasible paradigm for many developers, our loss.
BeanBuilder is still in beta (or something), indicating to me that the deployment strategy for this component has deviated somewhat. The bean "concept" is confused by EJB, by Swing type beans, and by a TON of "mandatory" boilerplate. Perhaps annotations will save the day here, who knows?
To further the "bean cause" we'd likely need a slew of event handlers pre-defined in a baseclass: This would likely slow the whole system down in some n-bean manner. A static event bitset could help, perhaps, by reducing the number of tests for handlers and by reducing unneeded object loads. It might not be necessary for every bean to have a vetoablePropertyChangeListener for every data member, however, if it cost little more than extending, say, BaseBean, or implementing a "smart" interface, then it would matter little to the average developer.
The other suggestion I liked was getting rid of the getters and setters, a language idiom would suffice, and we're long past the day when the (rather, if not, highly) specific rationale for insisting on their use was commonly useful. My suggestion is to use the following convention for replacing them:
class Test {
Integer one;
}
class Main {
public void main() {
Test t = new Test();
int x = t.one(); // the implied getter
x += 22;
t.one( x ); // the implied setter.
}
}
Another language feature I miss is the inherited constructor. It is probably called something else, but rears up nowadays with some of the exception handling and re-handling: If I write a constructor MyError(String), do I really wan't to write a constructor in each subclass? Eek! Perhaps they are called fall-through constructors. I realize there are very good reasons for having this particular language feature, and, no doubt there are many workarounds that promote better design and instill more advanced language-use.
One last feature is some type of copy-like constructor,. To avoid a complicated use scenario and description; I'll off the cuff with an example. Classes Y and Z, each derived from base-class B. Having an array of Z, I'd also like an array of Y, each with the same parent as a corresponding Z. My only real option here is to use a copy constructor to get the B part of Y from Z. I'm not sure if there is a real advantage to having the parent object of Y and Z be the same, likely just the opportunity to confuse things in an additional manner (and the opportunity to create circular-constructor-synchronization-deadlock). It presents an implied facade/decorator/visitor pattern which likely isn't more useful than less complicated. For large objects, there could be some performance viability in lieu of creating and copying some hundreds of data member references (and managing those references in the gc). Seems a highly specialised suggestion, perhaps not useful (inner classes serve me fine).
I think that the java platform is great, improving, and I also think that Sun has taken some really courageous, wise, and useful approaches to the Java language without giving up the esoteric power we all love about it!
Thanks,
Andrew Waddington
P.S. One other feature would be to replace some of the more mundane exceptions (ClassCast, ArrayIndex, NullPointer) with built-in assertions, perhaps with some "deploy mode" to bypass them for tested code.
Posted by: amfwaddi on November 29, 2005 at 05:27 PM
-
Have the compiler consume a new code-generating annotations.
Define a number of useful annotations that abbreviate code by having the compiler generate bytecode.
interface SomeInterface {
void a();
void b();
}
@IsAdapter(UnsupportedOperationException.class)
class Test implements SomeInterface {
private @BeanProperty(BeanProperty.READ_ONLY) String name;
public void a() { ... }
}
produces bytecode equivalent to...
class Test implements SomeInterface {
String name;
public String getName() { return name; }
public void a() { ... }
public void b() { throw new UnsupportedOperationException(); }
}
Likewise the suggested Delegator annotation is just a compiler instruction to produce the appropriate wrapper methods to perform delegation.
Posted by: talden on June 19, 2006 at 06:05 PM
-
[Ugh don't I wish I'd clicked preview instead...]
Have the compiler consume a new code-generating annotations.
Define a number of useful annotations that abbreviate code by having the compiler generate bytecode.
EG.
interface SomeInterface {
void a();
void b();
}
@IsAdapter(UnsupportedOperationException.class)
class Test implements SomeInterface {
private @BeanProperty(BeanProperty.READ_ONLY) String name;
public void a() { ... }
}
produces bytecode equivalent to...
class Test implements SomeInterface {
String name;
public String getName() { return name; }
public void a() { ... }
public void b() { throw new UnsupportedOperationException(); }
}
Likewise the suggested Delegator annotation is just a compiler instruction to produce the appropriate wrapper methods to perform delegation.
Posted by: talden on June 19, 2006 at 06:10 PM
-
Getting rid of the endless typing of getters and setters would be my #1, (don't start with "the EDI can do that") but the annotations must not be just as much to type as the regular getter-on-one-line, so maybe:
@Prop String name; // simple set and get
@ReadProp String name3; // only get
@BoundProp String name2; // get and set, where set fires PCE
@VetoBoundProp String name3; // get and set, where set fires VPC and PCE
Or maybe the annotation can analyze the class it is in and can search for a "firePropertyChange(...)" method. If present it will generate the call, if not, it will not.
Posted by: tbee on June 22, 2006 at 06:37 AM
-
I don't like the proposed solution for the JDBC driver instantiation boilerplate.
It sounds as if the solution would introduce a lot of overhead, since the candidates for JDBC driver classes on the classpath will have to be enumerated one by one. Additionally, there might be different versions of a driver, or different drivers that support the same URL scheme, so it is unclear which driver will eventually be loaded...
I'd think of a "clean" factory pattern. It should not extend to the degree that we have in the XML and Transformer APIs, but a simple method like DriverManager.registerDriver(String driverClassName) instead of the registerDriver that takes the driver instannce as an argument would do fine, wouldn't it?
Another reason for JDBC boilerplate is that e.g. ResultSet is not java.util.collections compatible. Why not introduce an Iterable<Map<String,?>> implementation of this thing?
Talking of XML: The XML APIs contain loads of boilerplate code. Adherence to W3C DOM is fine, but usability is another issue.
Again, make e.g. NodeList java.util.collections compatible and use generics where appropriate...
Just my $0.2...
Posted by: mklemm on November 09, 2006 at 03:57 AM
-
amateur sex porn
Posted by: teddy_k on December 15, 2006 at 05:24 PM
-
mature sex granny
Posted by: teddy_k on December 17, 2006 at 03:48 PM
-
XXX Disney Porn
INCEST
Gary Roberts Art & Comics
Posted by: qspider on February 23, 2007 at 06:07 AM
|