Google's mission statement goes as follows:
Sitebricks is a simple development layer for web applications built on top of Google Guice. Sitebricks focuses on early error detection, low-footprint code, and fast development. Like Guice, it also balances idiomatic Java with an emphasis on concise code.
A Sitebrick based web-application is deployed as a good old WAR file in a servlet container. That's the first information not explicitly stated on the website. If you have worked with another web framework before, you probably know the concept of a controller or a REST-based Resource. You can find the same concept in Sitebricks, too:
You can see that the class is pretty simple and it reminds me a lot of Restlet, which I used extensively the past few months. With the At annotation you declare the Uri under which your resource should be available. You can also define variable parts in your Uri, e.g. /guestbook/:id, which is used in my sample to access an entry of the guestbook.
The methods annotated with Get and Post are being called upon a request with the corresponding method. If you take a closer look at the method load(), you can see, that it doesn't return any value. That's where the convention or should I call it magic, of the framework begins:
In your Html file, that you define along with your resource, there are expressions, that access members of your resource class. In the Html snippet above we iterate over a Collection named "entries". It's a member of the Guestbook class and is assigned whenever there's a Get request, which triggers the method load() to be called. The expression items=entries calls the getEntries() method on Guestbook, to return the Collection.
Frankly, this strikes me kinda odd. You call a method to populate a member and then its getter to retrieve the value? Shouldn't the annotated Get method return the value directly? I'm not quite sure what the design goal was here, but the first thing that comes into my mind is concurrency. If your resource class is a Singleton, you could run into quite some trouble.
Take a look at the form in the Html snipped above. It has two input fields, which are properties, mapped by name to the member newEntry. The member must be accessible under the resource declared in the action attribute of the form. This was not mentioned in the Sitebrick docs either. When you submit the form, the getNewEntry() method is called and the input field values are set at the returned instance. After that, the annotated save() method is called with the member newEntry already populated.
Again, the design here is rather unusual. You are working on a member, which in case of a Singleton resource, means there is shared state. Of course, if you define your resources not as Singleton, you don't have any problems. However, it's definitely a pitfall, in my opinion.
Overall, I think Google Sitebricks contains a couple of cool ideas. I like the expression language kind of approach, which makes it easy to work with Html snippets. The possibility to define little components (Bricks) and add them in other sites, is another really cool feature.
The project is still in its early stages and I hope they'll address those odd design decisions, mentioned above. Although it's not standards-based, with Google backing the framework, I could imagine Sitebricks getting some traction.
16 comments:
Thanks for the writeup and the example.
It would be great if you could also provide a war file, so we can see how web.xml and the rest of the app should be configured.
The sitebricks documentation is not good. It is not clear where you should place the html files for instance? Documentation says place it with the classes or at the root level. Neither one worked for me.
As you can tell, I tried building a simple app myself after reading the infoq interview, but could not getting it running -- probably some stupid error on my part.
Thanks
I uploaded an exploded War file to http://download.linsin.de/google-sitebricks-sampleWeb.zip, which you can simply download and deploy.
You can find all files, including the web.xml, on GitHub.
Thanks David, that helps a lot. Appreciate it.
Hello,
Thanks very much for your post!
The design goal behind @Get not returning a value directly is that it maps 1:1 with the HTTP method GET. Thus, you *can* return a value (the next resource to redirect to).
This can either be a String as in your example or a page itself, with Sitebricks figuring out how to translate that into a 302 redirect.
I completely appreciate your concerns about our docs. We are working hard to get those upto scratch. Insightful posts like yours help us identify what to fix and are very much appreciated.
Dhanji.
Can you use warp-persist as a persistence solution with Sitebricks or does the DAO contain the usual Hibernate-boilerplate ( tx.begin() ... do sth. ... tx.commit() ) ?
@Dhanij
The design goal behind @Get not returning a value directly is that it maps 1:1 with the HTTP method GET.
That' a good reason actually!
@Daniel K
I don't know warp-persist, but to my knowledge it's based on Guice, so I don't see why it shouldn't work.
Great work, David. One side note: please pay attention to the root of your blog http://dlinsin.blogspot.com
I was unable to deploy the application using Eclipse and tomcat.
Error was : HTTP Status 404 - /google-sitebricks-sample/
I created a dynamic web project in eclipse, Added all java & html, also all the required jars, and also changed the web.xml file.
when I am deploying the application to tomcat server it gives me above error, my console was like this:
Nov 12, 2009 2:14:26 PM
....
INFO: XML validation disabled
Nov 12, 2009 2:14:28 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Error configuring application listener of class de.linsin.sample.sitebricks.config.GuestbookListener
java.lang.NoClassDefFoundError: com/google/inject/servlet/GuiceServletContextListener
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1847)
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:873)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1326)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1205)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3712)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4216)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:736)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:448)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:700)
at org.apache.catalina.startup.Catalina.start(Catalina.java:552)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:295)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:433)
Caused by: java.lang.ClassNotFoundException: com.google.inject.servlet.GuiceServletContextListener
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1359)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1205)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
... 22 more
Nov 12, 2009 2:14:28 PM org.apache.catalina.core.StandardContext listenerStart
.......
SEVERE: Skipped installing application listeners due to previous error(s)
INFO: Server startup in 2061 ms
Where is the thing went wrong I dont know? Plz help me
hey komal,
I guess you are missing Google Guice in your WEB-INF/lib. You might wanna download the deployable WAR, that I built and compare it to the one you deployed into your Tomcat:
http://download.linsin.de/google-sitebricks-sampleWeb.zip
Thank you david.
Can you just send me a Simple war file, as I am a student and my college does not allowed me to download .zip file? If possible.
komal, you need to write me an email at dlinsin@gmail.com, in order for me to have your email address.
Hey David,
Still I am unable to deploy the sample application.
Unzip the folder, put it into to the tomcat->webapp directory, Started my server and type the url:
http://localhost:8080/google-sitebricks-sampleWeb/
gives me error: HTTP Status 404 - /google-sitebricks-sampleWeb/
I don't understand where the things went wrong or where is stupid error on my part.
komal,
first of all I think you should try using jetty. I haven't tried the sample in tomcat, so I cannot fully guarantee it will work.
second of all you should deploy the sample to http://localhost:8080/sample, because I had to hardcode the resource uri, because of an issue in Sitebricks. After successful deployment you should be able to access the sample under http://localhost:8080/sample/guestbook.
Hi David,
I had created "sample.war" and deploy it into jetty. When I type the url:
http://localhost:8080/sample/guestbook/
It gives me error like this:
HTTP ERROR: 500
com.google.sitebricks.dom4j.DocumentException: Error on line 1 of document : White spaces are required between publicId and systemId. Nested exception: White spaces are required between publicId and systemId.
I am not getting what is mine stupid mistake why I am not able to deploy any single application using.
Komal, check out this bug. Could it be related to your problem?
Komal,
That might be related to issue #23 as David suggested.
Try with the latest 0.8-SNAPSHOT and if you still have problems, attach your template to issue #23 and I'll check it out.
-brad
Post a Comment