|
|
Could someone examine my test? Servlet-based example
Last post 01-25-2006, 05:16 PM by zambizzi. 10 replies.
-
01-24-2006, 08:08 PM |
-
zambizzi
-
-
-
-
Joined on 11-12-2005
-
-
Posts 15
-
-
-
|
Could someone examine my test? Servlet-based example
I have created a simple test in the last few minutes to see how db4o works in a Servlet environment w/ multiple concurrent users. This example works and I am successfully release the server & client "connections" after each request in a servlet. However, I'm sure someone might look at this and tell me it is not 'best practices' or that I'm doing something sub-optimal...which is what I'm here to hear! If not...then great...db4o is even easier than I thought.
I wanted to simulate a very basic 3-tier design in which db4o is not called directly in the servlet but in a class somewhere in the middle-tier.
Here are some code snippets:
doGet in the servlet (simply listing contents of ObjectSet):
Bridge br = new Bridge(); ObjectSet set = br.getAllCars(); while (set.hasNext()) { Car car = (Car)set.next(); response.getWriter().println(car.getMake() + "<br />"); }
'getAllCars()' method of 'Bridge' class being called in servlet (see above):
public ObjectSet getAllCars() { ObjectContainer client = Db4oServer.getClient(); //custom class (see next below) ObjectSet cars = client.get(new Car()); return cars; }
my custom 'Db4oServer' class:
public class Db4oServer { private static ObjectServer server; private static ObjectContainer client; public Db4oServer() { } public static ObjectContainer getClient() { Db4o.configure().activationDepth(3); Db4o.configure().blockSize(8); Db4o.configure().callConstructors(true); server = Db4o.openServer("C:\\db4oTest.yap", 0); client = server.openClient(); return client; } public static void close() { client.close(); server.close(); } }
I call the 'close()' method of the above class in a servlet Filter like so:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); response.getWriter().print("Filter was called!"); Db4oServer.close(); }
So, while this may not be the best practice, it seems to work. The client & server is closed on each request as seen in the server output:
[db4o 5.0.018 2006-01-24 11:47:46] 'C:\db4oTest.yap' close request [db4o 5.0.018 2006-01-24 11:47:47] 'C:\db4oTest.yap' closed
This was inspired by my Hibernate experience where I would open the Hibernate Session in a DAO and close it in a Filter...very similar to what I did here.
Any thoughts? Criticisms? Enhancements? Hate mail? Let me hear what you guys think.
-v
|
|
-
01-24-2006, 09:08 PM |
-
mjablonski
-
-
-
-
Joined on 10-23-2005
-
Bielefeld, Germany
-
Posts 726
-
-
-
|
Re: Could someone examine my test? Servlet-based example
There's a short article in the db4o-knowledge-base on using db4o in servlet-environments. Search for servlet or use this link: http://na1.salesforce.com/sol/public/searchdetail.jsp?ps=1&retURL=%2Fsol%2Fpublic%2Fsearch.jsp%3Fsearch%3Dservlet%26orgId%3D00D300000000PmR&orgId=00D300000000PmR&id=501300000001hyr Some suggestions: Your code works, but it is far from being optimal. You open/close the ObjectServer with every request, which is really useless and will kill any performance. Open/close the ObjectServer at the containter-startup/shutdown (use a ContextListener) and store a reference to the ObjectServer in a class-variable. If you need an ObjectClient, get the reference to the ObjectServer, open up a client, do your work and close the client again. That will give you much more performance and transaction-safety. If you don't need to rollback any transactions at database-level, you can even open up a db4o-database with OpenFile at container-startup/shutdown and share ObjectContainer for all your requests. This way you'll get maximum performance with lowest memory requirements from db4o in a servlet-environment (only one, not many ObjectContainers with already activated objects). HTH, Maik
http://db4o.blogspot.com/
|
|
-
01-24-2006, 10:05 PM |
-
zambizzi
-
-
-
-
Joined on 11-12-2005
-
-
Posts 15
-
-
-
|
Re: Could someone examine my test? Servlet-based example
Yes, this approach makes more sense performance-wise. My only complaint about this implementation is that db4o is directly tied to my presentation-layer when I would really prefer it be removed and reside on (or *as*) my data-access layer.
I've heard all of the arguements before about how db4o isn't really a database and should just be considered an extension of my application environment...and it's OK for the presentation-layer to be aware of it but I'm not in agreement w/ that arguement.
With this approach the server object would reside directly in the servlet context and I would need to pass it into my object in order for it to create a client, return records, and then the ContextListener would be responsible for closing the server...I'm not crazy about this design...it essentially voids the architecture that was keeping db4o and data-access logic hidden.
|
|
-
01-24-2006, 10:23 PM |
-
mjablonski
-
-
-
-
Joined on 10-23-2005
-
Bielefeld, Germany
-
Posts 726
-
-
-
|
Re: Could someone examine my test? Servlet-based example
zambizzi wrote: | With this approach the server object would reside directly in the servlet context and I would need to pass it into my object in order for it to create a client, return records, and then the ContextListener would be responsible for closing the server...I'm not crazy about this design...it essentially voids the architecture that was keeping db4o and data-access logic hidden.
|
|
You can still hide nearly all db4o-specifics from your presentation layer. Think of the ContextListener just as a trigger which calls an appropriate method in your persistence layer to open up or close the ObjectServer along with your application-startup/shutdown. The reference to your ObjectServer shouldn't be stored in the ServletContext, but in a static class-variable of the persistence layer. This way you can reuse your persistence layer in non-web-applications as well. Calling something like YourPersistenceManager.openDatabase() / closeDatabase() from the ContextListener should not worry you about the "clean" design of a layered application...;) BTW: Hibernate usually uses a ContextListener to initialize itself in a servlet-environment. Cheers, Maik
http://db4o.blogspot.com/
|
|
-
01-24-2006, 10:54 PM |
-
zambizzi
-
-
-
-
Joined on 11-12-2005
-
-
Posts 15
-
-
-
|
Re: Could someone examine my test? Servlet-based example
OK, that makes perfect sense, the light went on. I'm not all that familiar w/ ContextListeners so this is farily new to me.
I did exactly that and I'm getting a whole slew of errors when the application starts in Tomcat (5.5.x)
Here's the new layout:
Servlet doGet:
Bridge br = new Bridge(); ObjectSet set = br.getAllCars(); while (set.hasNext()) { Car car = (Car)set.next(); response.getWriter().println(car.getMake() + "<br />"); }
...which calls my 'Bridge' class, getAllCars method:
public ObjectSet getAllCars() { ObjectContainer client = Db4oServer.getClient(); ObjectSet cars = client.get(new Car()); return cars; }
...which calls the custom Db4oServer class
public class Db4oServer { private static ObjectServer server; private static ObjectContainer client; public static void openServer() { Db4o.configure().activationDepth(3); Db4o.configure().blockSize(8); Db4o.configure().callConstructors(true); if (server == null) server = Db4o.openServer("C:\\db4oTest.yap", 0); } public static void closeServer() { if (server != null) server.close(); } public static ObjectContainer getClient() { return server.openClient(); } public static void closeClient() { if (client != null) client.close(); } }
I close each client in the filter like so:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); Db4oServer.closeClient(); }
And...last but not least, here's the ContextListener:
public class db4oListener implements ServletContextListener { public void contextInitialized(ServletContextEvent evt) { Db4oServer.openServer(); }
public void contextDestroyed(ServletContextEvent evt) { Db4oServer.closeServer(); } }
I see a these errors in the log when Tomcat starts:
java.lang.NullPointerException at com.db4oTest.DAL.Bridge.getAllCars(Bridge.java:39) at com.db4oTest.servlet.Test.doGet(Test.java:34) at javax.servlet.http.HttpServlet.service(HttpServlet.java:689) at javax.servlet.http.HttpServlet.service(HttpServlet.java:802) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at com.db4oTest.servlet.db4oFilter.doFilter(db4oFilter.java:39) ...
com.db4o.ext.DatabaseFileLockedException at com.db4o.JDK_1_4.lock(Unknown Source) at com.db4o.Platform4.lock(Unknown Source) at com.db4o.io.RandomAccessFileAdapter.<init>(Unknown Source) at com.db4o.io.RandomAccessFileAdapter.open(Unknown Source) at com.db4o.YapRandomAccessFile.open(Unknown Source) at com.db4o.YapRandomAccessFile.<init>(Unknown Source) at com.db4o.Sessions.open(Unknown Source) at com.db4o.Db4o.openFile(Unknown Source) at com.db4o.Db4o.openServer(Unknown Source) at com.db4oTest.data.Db4oServer.getClient(Db4oServer.java:34) at com.db4oTest.DAL.Bridge.getAllCars(Bridge.java:32) at com.db4oTest.servlet.Test.doGet(Test.java:34) at javax.servlet.http.HttpServlet.service(HttpServlet.java:689) at javax.servlet.http.HttpServlet.service(HttpServlet.java:802) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at com.db4oTest.servlet.db4oFilter.doFilter(db4oFilter.java:39) ...
java.lang.RuntimeException: 'Client Connection embedded client1' is closed. close() was called or open() failed. at com.db4o.inside.Exceptions4.throwRuntimeException(Unknown Source) at com.db4o.inside.Exceptions4.throwRuntimeException(Unknown Source) at com.db4o.YapStream.checkClosed(Unknown Source) at com.db4o.QResultClient.next(Unknown Source) at com.db4o.inside.query.ObjectSetFacade.next(Unknown Source) at com.db4oTest.servlet.Test.doGet(Test.java:38) ...
java.lang.NullPointerException at com.db4oTest.data.Db4oServer.closeServer(Db4oServer.java:54) at com.db4oTest.servlet.db4oListener.contextDestroyed(db4oListener.java:48) at org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:3733) at org.apache.catalina.core.StandardContext.stop(StandardContext.java:4306) at org.apache.catalina.manager.ManagerServlet.undeploy(ManagerServlet.java:1283) at org.apache.catalina.manager.ManagerServlet.doGet(ManagerServlet.java:372) ...
java.lang.RuntimeException: 'Client Connection embedded client1' is closed. close() was called or open() failed. at com.db4o.inside.Exceptions4.throwRuntimeException(Unknown Source) at com.db4o.inside.Exceptions4.throwRuntimeException(Unknown Source) at com.db4o.YapStream.checkClosed(Unknown Source) at com.db4o.YapStream.checkTransaction(Unknown Source) at com.db4o.YapStream.setExternal(Unknown Source) at com.db4o.YapStream.set(Unknown Source) at com.db4oTest.DAL.Bridge.getAllCars(Bridge.java:39) at com.db4oTest.servlet.Test.doGet(Test.java:34) ...
...and it goes on like this. I'm sure it's my misunderstanding of the ContextListener...but what am I doing wrong?
Thanks again! Your help is *really* appreciated!!
|
|
-
01-25-2006, 01:57 PM |
|
|
Re: Could someone examine my test? Servlet-based example
zambizzi wrote: | public static void openServer() { Db4o.configure().activationDepth(3); Db4o.configure().blockSize(8); Db4o.configure().callConstructors(true); if (server == null)
server =
Db4o.openServer("C:\\db4oTest.yap", 0);
} public static void closeServer() { if (server != null) server.close(); }
|
|
The server is closed, but not set to null, so it will never be reopened.
zambizzi wrote: |
public static ObjectContainer getClient() { return server.openClient(); } public static void closeClient() { if (client != null) client.close(); } }
|
|
This 'singleton' client approach will get into trouble with concurrent requests.
zambizzi wrote: | I close each client in the filter like so:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); Db4oServer.closeClient(); }
|
|
I'm not too familiar with servlet filters, but this doesn't look
healthy to me - IMO resources should always be released in the same
scope they are acquired (the 'resource acquisition is initialization'
idiom). And again, this doesn't look stable in the presence of
concurrent requests.
zambizzi wrote: |
I see a these errors in the log when Tomcat starts:
|
|
Prior to any request? No, right?
zambizzi wrote: |
com.db4o.ext.DatabaseFileLockedException [...]
at com.db4o.Db4o.openServer(Unknown Source) at com.db4oTest.data.Db4oServer.getClient(Db4oServer.java:34)
|
|
*puzzled* There's no openServer() call in getClient(), AFAICS.
zambizzi wrote: | ...and it goes on like this. I'm sure it's my misunderstanding of the ContextListener...but what am I doing wrong?
|
|
The problem is not the context listener. I can't put my finger on it
right now (probably some code or configuration information is
missing?), but in general I'd suggest to get rid of the singleton
client approach and the filter and provide a client per request that's acquired and released 'in the same place'.
Best regards,
Patrick
|
|
-
01-25-2006, 02:47 PM |
|
|
Re: Could someone examine my test? Servlet-based example
patrick_roemer wrote: | zambizzi wrote: | com.db4o.ext.DatabaseFileLockedException [...] at com.db4o.Db4o.openServer(Unknown Source) at com.db4oTest.data.Db4oServer.getClient(Db4oServer.java:34)
|
|
*puzzled* There's no openServer() call in getClient(), AFAICS.
|
|
Db4oServer is a class that the user wrote and it does contain an #openServer() call.
|
|
-
01-25-2006, 03:15 PM |
|
|
Re: Could someone examine my test? Servlet-based example
Carl Rosenberger wrote: | patrick_roemer wrote: | zambizzi wrote: | com.db4o.ext.DatabaseFileLockedException [...] at com.db4o.Db4o.openServer(Unknown Source) at com.db4oTest.data.Db4oServer.getClient(Db4oServer.java:34)
|
|
*puzzled* There's no openServer() call in getClient(), AFAICS.
|
|
Db4oServer is a class that the user wrote and it does contain an #openServer() call.
|
|
*even more puzzled* But not in its getClient() method, right?
Best regards,
Patrick
|
|
-
01-25-2006, 03:30 PM |
-
mjablonski
-
-
-
-
Joined on 10-23-2005
-
Bielefeld, Germany
-
Posts 726
-
-
-
|
Re: Could someone examine my test? Servlet-based example
zambizzi wrote: | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); Db4oServer.closeClient(); }
|
|
Ouch... lots of trouble ahead... The singleton approach will only work for the ObjectServer, but not for the client... You have only one ObjectServer (shared by all users/requests), but many clients (a fresh one for every user/request), so you can't reuse one client via a singleton approach. You'll need a fresh client for every request, otherwise you'll run into concurrent problems. If you want to use a servlet-filter, then require a ObjectClient before executing the chain, store it in a request-attribute (not request-parameter!) and release this client after the return of the chain. Something like this should work: public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ObjectContainer db4oClient = Db4oServer.openClient(); request.setAttribute("Db4oClient", db4oClient) chain.doFilter(request, response); db4oClient.close(); }Now you can take the Db4oClient in your web-application-controller from the request-attribute and inject it into your DAO-Layer (or whatever else) for handling a single user-request. Cheers, Maik
http://db4o.blogspot.com/
|
|
-
01-25-2006, 05:04 PM |
-
zambizzi
-
-
-
-
Joined on 11-12-2005
-
-
Posts 15
-
-
-
|
Re: Could someone examine my test? Servlet-based example
You were absolutely correct, removing the singleton on the ObjectServer completely cleared all of the errors that Tomcat was reporting when starting (yes, before any requests.) So, obviously, that was causing some weirdness w/ the ContextListener...but since I'm not familiar w/ the ContextListener (until now) I'm not sure if I'm in for more problems - it wouldn't appear so, the app actually runs quite well now!
Thanks a ton!
-v
|
|
-
01-25-2006, 05:16 PM |
-
zambizzi
-
-
-
-
Joined on 11-12-2005
-
-
Posts 15
-
-
-
|
Re: Could someone examine my test? Servlet-based example
mjablonski wrote: | Ouch... lots of trouble ahead... The singleton approach will only work for the ObjectServer, but not for the client... You have only one ObjectServer (shared by all users/requests), but many clients (a fresh one for every user/request), so you can't reuse one client via a singleton approach. You'll need a fresh client for every request, otherwise you'll run into concurrent problems.
If you want to use a servlet-filter, then require a ObjectClient before executing the chain, store it in a request-attribute (not request-parameter!) and release this client after the return of the chain. Something like this should work:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ObjectContainer db4oClient = Db4oServer.openClient(); request.setAttribute("Db4oClient", db4oClient) chain.doFilter(request, response); db4oClient.close(); }
Now you can take the Db4oClient in your web-application-controller from the request-attribute and inject it into your DAO-Layer (or whatever else) for handling a single user-request.
Cheers, Maik
|
|
Actually, I was only using a singleton on the server, not the client - so once I removed it from the server, all is good! Also, you're correct...a client would be required before the filter could close the client....which in this case would always be true because the client is being opened in the DAO, utilized, and then closed in the filter. Anyhow, I'm very pleased! Db4o is truly powerful and *insanely* easy to use... You've all been very, very helpful, thanks again! -v
|
|
|
|
|