JSF Central - Seam in Depth with Gavin King
JSF Central

 
 Home 
 
 Products 
 
 Articles & Books 
 
 Resources 
Community Chat
 
Seam in Depth with Gavin King
by Kito Mann
13 Jun 2008 01:30 EDT

This podcast is an interview between JSFCentral editor-in-chief Kito D. Mann and Gavin King, the creator of JBoss Seam and Hibernate. It was recorded in May of 2007 at the JavaOne conference in San Francisco, CA.


Podcast (MP3)
Welcome to the JSFCentral podcast #2 for the week of June 9, 2008. This series of podcasts features interviews, news, and commentary for those working with JavaServer Faces technology. We’re pleased to announce the first annual JSFOne conference, hosted by JSFCentral and the No Fluff, Just Stuff Symposiums, September 4th through the 6th, right outside of Washington, D.C. Keep you eye on JSFCentral.com for details.
We' re proud to announce JSFOne, the one event exclusively focused on the JSF ecosystem, taking place September 4th through the 6th in the Washington, DC area. JSFOne is a conference for application developers, solution architects, and project managers who develop applications with JavaServer Faces, Seam, and related technologies. Our speakers are deeply involved in every aspect of the JavaServer Faces ecosystem, from developing core technologies to building real-world applications. JSFOne is hosted by JSFCentral and the No Fluff Just Stuff Symposiums.
KitoI am here with Gavin King, at JavaOne 2007. Today we are going to talk about Seam and whatever else you think is relevant, Gavin. For those who don’t know, could you tell us who you are and what you do?
Gavin I work for Red Hat and I am currently leading our efforts to build a development environment based upon Java EE 5, Seam, and Eclipse Tooling. We plan to be the most productive environment of building applications you have ever seen. Our vision is something that is comparable to .NET in some ways.
Kito I guess the other thing to point out is that you started Hibernate, right? When did you start Hibernate? How long has it been now?
Gavin About five or six years ago. I was developing Hibernate for maybe five years.
Kito What were you doing before that?
Gavin I worked for an IBM business partner in Australia doing consulting type work: building applications using web stuff.
Kito Tell us what Seam is.
Gavin Seam is a couple of things. First of all it is an attempt to build a whole solution for developing applications based upon Java and Java standards. We have seen recently a lot of excitement around some environments built on scripting languages which attempt to – rather than solve narrow technical concerns, rather than attempting to be a persistence framework or a presentation framework – attempt to be a whole application framework that solves everything from “How should I set up the project?” [and] “How should I build my project?” to “How should I persist my data?” [and] “How do I design a UI?” I think people are really looking for that and I think the days of hacking together architecture by tying 20 different frameworks together with string is kind of over. I think people are sick of that and are looking for really unified, really consistent frameworks with a much deeper level of integration than we have had in Java in the past. I think it is more by luck that this seems to have happened in the scripting world first, and I don’t think that there are any of the lessons that you can learn from things like Ruby on Rails [that] are not applicable to Java. I think that when you layer some of the kind of tooling and standardized project setup, and deep integration that you find in an environment like Ruby on Rails -- once you take those same ideas and build an environment in Java EE -- then you get just as much productivity. In fact you get more, because Java is able to be tooled to a much greater extent. We simply can’t build the kind of refactoring and code completion tools that we have in Java -- that we take for granted in Java -- in a dynamic language. The second thing is the underlying infrastructure of EE is so much more powerful. I have heard from a few people how they kind of start going Rails and it solves some of their problems really quickly, then they get to a particular point and they realize they’ve run out of support for what they are trying to do. You will never experience that in Java because of the pure depths of the infrastructural software that is available to you. The second thing Seam is, is an attempt to totally change the way we build web applications and change away from a stateless, pure request oriented architecture to an architecture based upon state management, based upon conversations, based upon stateful components. This is the thing that sounds least well-defined and less relevant when I say it like that, but actually once people sit down and start using Seam, they kind of re-evaluate the way they build applications. It is the thing that excites people the most.
Kito If we were to peel back the layers a bit… We have talked about how Seam simplifies programming on the Java EE platform, how you can approach or surpass Rails-type productivity. When you get down to it, and the stateful stuff, what exactly does it consist of?
Gavin The earliest iterations of our work came from three places. The first place was that we were working hard on the EJB 3 spec, and we were working hard on trying to make Java EE 5 much simpler and much easier to develop applications from a pure code point of view. What code do you need to write? In some stage of the process of writing the EJB 3 spec I realized that a major problem we had in the EE environment was this split between the transactional tier -- the EJB tier, and the web tier, which in the spec was JSF essentially. I realized that we had these two component models -- the JSF managed bean model and the EJB 3 model -- and they didn’t really know about each other. That made the job of a normal EE developer writing an application that gets data from the database and puts it on the screen much more difficult, imposing a kind of unnatural layer in there. Years ago I started talking about the idea of having a new kind of enterprise bean called a Web Bean. That kind of fell by the wayside; it wasn’t really time, we weren’t really ready. It turns out that to really do that right it takes way more work than we had in the EJB spec. First of all it was the idea of Seam started with collapsing down the layers; integrating the JSF managed bean model with the EJB 3 model. Essentially from one point of view you can see it as replacing managed beans, which in the JSF spec is more of a placeholder. It gives you the ability to be able to bind some Java classes to your screens in a standard way, but there is no functionality beyond that in the managed bean model. There is no way to demarcate transactions or to access databases or anything like that. To take out that placeholder and replace it with EJB [and] to tie EJB into JSF as the view presentation was the first step. The second problem we were trying to solve was that we would run up against a real wall in Hibernate in that everybody was complaining about lazy initialization exceptions. Everyone was having trouble processing optimistic transactions and the problem was about to get way worse. So let’s look at this. First of all, almost every web application has a need to pull data off the database this way, let the user change the data, go back to the server and persist those changes. The trouble with that is that we are using stateless architecture like stateless session beans, like Spring, and in these stateless architectures, with no kind of conversation model, the only way of managing the Hibernate session that really works is to have a session per request. But they had an optimistic transaction which spanned multiple of these atomic transactions and so they had a session per atomic transaction. That meant that they are forever worrying about using the merge operation to get changes to the object propagated back to the database. They were worrying about trying to fetch extra data by navigating associations, causing lazy initialization exceptions, all these kinds of problems. What was about to happen, what we could see happen, was that everybody was going down the Ajax route, which fundamentally changes the interaction model of the web from one of these few course-grained requests to lots of little fine-grained requests. Imagine implementing a TreeView: we are underlying that Tree View as a graph of objects persisted by Hibernate. Each time a user expands a node, we need to go off and fetch another lazy association on the server side. If I have to go back and re-fetch the whole object graph from the database every time I want to expand a node, my database is not going to be able to handle the load. We knew we needed to create a new model of interaction with the server where we could have state sitting on the server side, and an extended persistence context that spanned that conversation…and paradoxically people have been trained for years to think that the stateless architecture is the scaleable one. In fact, this is not true at all. The stateless architecture is the one that is unscalable. The reason for that is that the database is the least scaleable tier. A stateless architecture results in lots and lots of hits in the database. People usually mitigate this by introducing a second level cache, which is stateful. It is reintroducing state back in. The trouble with a second level cache is that it has to be kept transactionally consistent across the whole cluster with the database. That is a very, very, expensive thing to do. Anytime you need to keep something consistent across all nodes in the cluster, you are introducing a big limit to scalability. Conversational state on the other hand, with modern clustering architectures like recent releases of JBoss, only needs to be kept consistent across a node and his buddy. The perceived wisdom about whether statefullness or statelessness was scalable was in fact the opposite of what was really the case, once you started looking at applications with optimistic transactions and Ajax.
Kito Okay, so we are talking about state and how it is not such a bad thing after all. In more specific terms, what is a stateful component in Seam and how does the conversation play into that?
Gavin A stateful component is just a stateful session bean, or in fact can be any Java class. You don’t have to use EJB 3 with Seam if you don’t want to. It is just a Java class which would specify a context. You can say “this object is scoped to the conversation” or “this object is scoped to the session.” In fact this is a model that is just taken straight from the JSF managed bean model, but what we have done is added new contexts -- we added the conversation context and the business process context -- to that model. It can be bound to the view layer just by EL expressions, just like in JSF. So then these stateful components are able to interact with each other so we kind of have a more sophisticated version of dependency injection, which is designed to cope with the problems that you come across when you have stateful components from different contexts interacting with each other. What we are trying to achieve in the component model is extremely loosely coupled applications. There are a couple of different ways you can achieve this coupling, and you need to do all of them at the same time. The first way is to separate API and implementation -- that is something you can do by using an interface and making it easy to override the implementation of a component at run time. The second thing you might want to do is separate out orthogonol concerns and infrastructural concerns. A great way to do that is using EJB interceptors and annotations. The third thing, which people think a lot less about, is that when objects collaborate together, very often you find that these objects have different life cycles. Very often you find yourself writing code where one object has to spend a lot of time worrying about the lifecycle of the other object (about creating it and destroying it). Especially in this model in Seam with these more long-lived components in various contexts with various different lifecycles, that’s something you don’t want to worry about. So you can achieve a much looser coupling between the objects if you allow the container to maintain the lifecycle of the objects so that the client of an object doesn’t need to know when [the object] is created, when it is destroyed, it doesn’t need to be aware of that.
Kito Tell us a little bit more about this extended version of dependency injection, which you call dependency bijection, what is that in specific terms?
Gavin Bijection is two things. The traditional dependency injection framework, which is designed with stateless services in mind, when it instantiates a component, it injects the dependencies of that object. Whether it is done via the constructer, via calling some setter methods is irrelevant. This breaks as soon as objects have scopes. JSF’s model is kind of broken because it combines scoped objects with traditional dependency injection, [which is] instantiation-time dependency injection. What if I have an object in a wide scope like a business process scope or a conversation scope that needs to use an object in a narrow scope like the request scope? When it is instantiated, it gets this request scope object, [and] it outlives the request scope object.
Kito Right, it is really not even allowed in standard JSF.
Gavin In standard JSF, you are going to have to write all your objects so the flow of control always goes from request object all the way down to session scope object. You can’t go the other way and that is incredibly inconvenient. The first thing was to switch that model and instead do injection of things dynamically, to do injection of things when you call the method on the component. When an invocation comes into a component, when a method gets called, the first thing we do is find all the objects that it is using. When the invocation goes back out, we go back and add all those references, so that is different. The second thing is, we realize that because these objects don’t really represent dependant services -- they represent objects with state -- what we really need to do is change our point of view to thinking about contextual variables, thinking about states. It is actually variables, not dependency we are interested in. If we are talking about variables, what we really want to be able to do is read and write them, get and set them. We don’t want to do that by getting a hold of some context object -- HTTP session or something like that -- and calling “get” and “set” attribute methods. The different point of view Seam has is that it is not about dependency injection. What it is about is aliasing a contextual variable, a conversation scope variable, onto an instance variable of a component so that we can read the contextual variable by reading the instance variable, and write the contextual variable by writing to the instance variable. We have a horribly named thing called outjection, which means when the invocation comes back out of the method, when we return from the method, Seam will actually look at the values of the instance variables and write their current state back out to the context.
Kito We mentioned conversation and the whole business process scope. Can you talk more about exactly how these contexts are different than normal application, request, and session scope?
Gavin The contexts that are built into Seam are ones you recognize from servlet spec, you know -- request, session, application -- exactly the same thing they mean in the servlet spec or in JSF. The two big ones that we add are conversation and business process. Conversation is a context which represents an interaction with a particular user that lasts for a relatively short period of time, and corresponds to an optimistic transaction on the database (probably). One thing about the conversation context is that it is demarcated explicitly by the application. There is no way that Seam can figure out of its own accord what the boundaries of the conversation are. The developer specifies on my get document method “this is the beginning of the conversation,” [and] specifies on the update document method “this is the end of the conversation,” for example. What Seam is able to do automatically is track that context. So for example, it knows that if I have multiple browser windows and multiple tabs open in the same browser window, it automatically tracks that this tab relates to this conversation on the server. It can have multiple concurrent conversations that appear isolated from each other. A second context we introduce is the business process context. A business process is some kind of long-running, long-lived collaboration between multiple users. Unlike a conversation it can last for weeks, months potentially. When we first looked at this we wanted to know what was the relationship between conversations -- that came out of Hibernate and out of thinking about Ajax -- and business process -- this thing that existed in workflow engines like JBPM. What we figured out was that JBPM has this idea of a task, so JBPM is all about managing task lists and dependencies between tasks that users have to do. We figured out that when the user comes in, sees a task on their list and clicks the task, goes off and does the work, finishes the task, the task vanishes. What is that? That’s a conversation. We figured out that in the language of workflow, a conversation is a task. Some conversations in Seam are also tasks, and they have access not only to the conversation context, not only to the state associated with this conversation, with this task, but also to the business process context. That is their way of sharing, or propagating state between this current task that this user is working on, and other tasks which other users in the future or past have worked on for this same business process.
Kito Give us an example of something that fits well into standard conversation, and something that fits well into business process.
Gavin For example, a conversation is approving a document. The business process is the whole work of writing, editing, approving, and publishing the document to the web.
Kito One of the other things that is popular in the web landscape now are dialogue framework type things. There is Spring Webflow, Shale Dialogues. Of course they have a context associated with that particular workflow. How do you see Seam’s features as far as conversations and business process stuff compared to those?
Gavin The instance of a dialogue is a conversation, so these frameworks are addressing the point of view of conversation, are implementing conversation from the point of view of I am creating some sort of complex page flow. Their emphasis is on the page flow side of things. I am going to say that that’s actually the least interesting thing to think about when you are thinking about conversations. Seam has a page flow language, based upon JBPM, so Seam actually has unified language for representing business processes with collaboration and page flows in an application. Page flow is a flow for a single conversation. In Seam you can do the same thing as you can in a dialogue framework or Spring Web Flow. Most Seam users find very few cases where they want to use this stuff, because generally, most page flows are quite trivial and simple. When we think about the edit document conversation, its page flow is maybe one page. I don’t want to have to create some silly XML document to represent the flow of that conversation -- it is trivial. Particularly when we start thinking about Ajax, most of the requests in this conversation are not relating to any page flow, [so] we are stuck on one page. I think that is fundamentally getting things the wrong way around. Conversation should be able to have page flows, but you shouldn’t have to have a page flow to be able to have a conversation.
Kito One of the complaints about Seam is that in all the examples, you basically collapse the business tier. Your Seam component that actually replaces a backing bean -- either a stateful session bean or a stateless session bean or whatever – there may be direct database access, or… lot of stuff that people who are into layers don’t like to see. What is your response to that?
Gavin Layering is something that you should define for your application. I don’t generally find that layering is a useful construct. To me, if I want to abstract and layer out some piece of functionality, then I am just going to refactor it out as an object. I don’t need to be able to separate out some functionality and separate some concern out from another concern. I am just going to refactor out an object. I don’t need to know what layer that object lives in. To me, layering like this is a far too straitjacketed, inflexible, and unexpressive way to do these kinds of abstractions and separation of concerns. In my personal taste, I don’t think it is meaningful to think about separating your presentation tier logic from your business tier logic. I think that is a totally bogus construct in terms of maintainability of the system but clearly other people disagree. That’s fine; everyone gets to choose their own layering. If you want to go for a traditional, over-engineered J2EE style architecture of DAOs talking to the entity manager, and over them a layer of stateless transaction scripts -- the so-called business logic -- then over that a layer of stateless or stateful actions that call those, you can absolutely do that in Seam. You would be nuts to do it because you will write three or four times as much code as you need to. Let’s face it, maintainability of code usually comes down to how much code you have. The idea that I make my system more maintainable by writing more code is almost always false. Normally the less code I have the easier it is to maintain.
Kito Do you think though, that some of the reason people move to layers is that if you have to do transaction management and other concerns, if there are no other facilities outside of the code to do that, you end up putting all that in a JSF backing bean or something? Do you think the fact that with Seam you don’t have to write as much of that boilerplate code, that it makes it more appealing not to have the different layers?
Gavin What’s happened is that traditionally people didn’t have access to an architecture. We are forced down the path of separating so-called presentation logic and so-called business logic. They made up all kinds of reasons to justify that to themselves. If you have an architecture in which that is not required, pretty soon we discover that we didn’t really need it after all. The trouble is that when you are writing a Struts application you are writing objects and the presentation to your objects are things which extend actions, which are very much Struts objects that live in the Struts world. JSF and Seam totally dissolve that. When I am looking at a Seam component that is connected to the view, it’s not extending action, it’s not doing anything specific to presentation, and it’s just doing business logic. That is the magic of JSF. JSF allows me to bind with an EL, use EL to bind the view directly onto the business components without needing to format the business logic components in any strange way. I don’t see technical artifacts of HTTP or of the view in my JSF backing beans or Seam components.
Kito But some things you still have to do, right? Like create application messages, and if you are using Seam annotations you are still saying this is a Seam component, right?
Gavin One artifact you do often see is Faces Messages. Seam has a built-in component called Faces Messages where you add a message onto the page. In the Seam examples you will see adding Faces Messages mixed in with access to the entity manager. If I wanted to be able to access these Seam components from a web service or something other than JSF, then yes I would need to factor out that code. Okay, let’s do it then when I need to. The idea that you build your application all in one layer like the Seam examples are done -- that it is going to be way too hard to later separate out the seams when you need them -- is totally bogus. [With] the refactoring tool we have in Java today, adding layers in later is the easiest thing on earth. It is so easy it’s ridiculous. When you are at the early stages of a project, your job is to get some useful functionality for the users to use now. Later on, if you discover there are all these other parts in the system, other views needed, refactor out the common logic that’s really needed and do it then. Then you will know what actually makes sense for your layering, where the layers should really start and end.
Kito While we are talking about this whole separation of concerns, some people think that like in the EJB 3 layer, the idea of annotating your entity classes with the actual table name or very specific details about the integration with the database might be an invalid use of annotations, in the sense that those types of concerns might have been better off in the Hibernate mapping document, or the JPA equivalent. What is your personal take on that? Obviously you can do either or, right?
Gavin That’s kind of a discredited view. It is clear that the case where someone chose to map the same object model to completely different schemas is vanishingly rare. I have never seen such a thing anywhere in my life and I have a great deal of experience with customers who use Hibernate. I am not talking about the same object model being mapped to named different schemas -- of course that’s common -- so you shouldn’t put the name of your schema or the name of your catalogue in the annotations. You should put them in your JDBC connection properties actually. The cases where I have two schemas which are identical in every respect, apart from the fact that the table and column names are changed --if that happens you really need to consider how you are using the database. What I want is that I have a set of Java classes here which represent my data model, which I can look at and see how this corresponds. It is so much easier to see what’s really going in my system when the two kinds of information are right there together. I can’t imagine going back to XML.
Kito Originally, Seam only worked with JBoss, but now it works with other servers, and of course it always worked with Tomcat by itself. What was the motivation behind that?
Gavin This is one of the core values of Seam: we are targeting standards. Seam was always designed to run on any EE 5 application server. Of course, at the time when Seam came out there were no EE 5 application servers, so we kind of got it running on JBoss and Tomcat first. For us to then take it and make sure the examples all ran on WebLogic, WebSphere, and Glassfish was some work, but it wasn’t code changes in Seam, it was just figuring out how to get all the different configuration settings, and testing and installing our competitor’s application servers, which of course is always harder than installing JBoss. It was a fair amount of work, but not so much changing Seam, just getting the examples ready and tested. The special case is Tomcat; it is not an EE environment. Seam has no hard dependency to anything in the EE environment apart from JSF and JPA. We expect a JPA/JCA environment for data source management. I understand that a lot of people aren’t using that today -- that’s nuts. You see in some frameworks, including Hibernate, this jewel thing where there are all these switches to decide whether or not we are using JPA transactions in a JCA data source or maybe using plain JDBC in Tomcat. It is really absurd the amount of duplication of code this is creating in the Java community. People are creating abstractions of abstractions. Hibernate has its own transaction abstraction which extracts JDBC or JPA. Spring has its own built over the top of that. It is just a disaster. There should be one transaction extraction user transaction. So far we have been resisting supporting anything other than JPA and Seam, which leaves people saying “well if I am using Tomcat I don’t have JPA right?” Well with JBoss micro-container you can have a full JPA/JCA environment in Tomcat. There are clear and obvious advantages to having that, compared to using Tomcat’s broken data source.
Kito What about other plain web containers like Jetty or Orion?
Gavin It is much the same situation. If there is no JPA environment then simply use the JBoss micro-container – which is being rebranded Embeddable JBoss. We have been told by some that if we want to deliver pieces of Java EE then we kind of have to deliver the whole lot. There will be an embeddable JBoss which you just install into Tomcat. People are always shocked when I demo in JBoss micro-container my TestNG test for Seam application. Seam has this testing harness, which instead of being a unit test -- instead of being a test of this object on its own -- is actually a test of the whole flow through the application. It creates a JCA/JTA, a mock JSF, and EJB 3 environment there -- basically a whole Java EE environment in the test case to run the tests. People are shocked when I run this test, it takes half a second and I am like “yeah that is an entire Java re-environment inside my IDE, inside TestNG,” and they are shocked because they have been taught this nonsense about Java EE being this heavyweight thing and it’s bogus.
Kito I think the J2EE legacy will take a long time to counteract.
Gavin Yeah J2EE had very many problems. Java EE still has some problems, but I mean we need to be a bit fairer here. J2EE was created by people with essentially a C programming background, by people who hadn’t really programmed Java, by people in the early days of the web, people who probably didn’t really know how web applications worked or had written a web application, whereas Java EE 5 was created by Java developers who had been building web applications most of their professional career. This is a different environment that builds upon the work that was previously there, and Java EE 6 is going to be even better. We are revitalizing the platform and it’s actually very exciting and it’s great to see.
Kito One of the other things Seam does now is integrate with Spring. Of course there have always been ways to integrate JSF in Spring, but it seems to have some special facilities for integrating with Spring. [In] what situations would someone want to use Seam and Spring? Because they overlap in a lot of ways.
Gavin The people who are migrating from a legacy, sort of Struts, Spring, Hibernate application, and they want to get back to JSF, EJB 3, JPA, and in that case they want to be able to re-use all their existing business logic components. By far the easiest way to make that migration step is just to use the Spring integration in Seam, which lets you inject Seam components into Spring components, Spring components into Seam components, take advantage of the wonderful stuff in Seam like the conversation scope persistence context in a Spring component. That gives the upgrade path to EE in Seam. The second possibility is that maybe you just like Spring, or you want to take advantage of some kind of integration thing that Spring has that Seam doesn’t have. For example, perhaps you really like Acegi for security or something like that, in which case you can use Spring as a mechanism of integrating those into the Seam environment.
Kito I guess the last question is the WebBeans JSR, which you started. I’m on the EG, and I need to either reschedule my Wednesday or you need to change the phone calls because I just never can make the phone call. Tell us a little bit about WebBeans and how it relates to Seam and JSF.
Gavin WebBeans is an attempt to take some of the ideas we have in Seam -- for example, contextual components, conversations, deeper integration of JSF and EJB -- and bring them into EE 6. That was the original goal. Since we have started on this work, we have talked about what is the scope of our work and what is the platform we are targeting, and decided that the first piece of work we will do is defining the component model -- we will try and make that independent of JSF and EJB 3. We integrate beautifully with them but the work will be to define a component model which can be potentially reused in Java SE. One of the people who has pushed very much for that is Bob Lee from Google. The reason for that is Bob wants Guice to be a WebBeans implementation. For people who have looked at Guice, it is another Java EE 5 style dependency injection framework and if you look at most of the dependency injection frameworks that are around -- even if you look at Seam -- generally resolution of components is done via string names or IDs. Bob rightly observed that that is difficult to refactor and it’s not very type safe. Bob has gone for a different approach, where to resolve dependencies we use an annotation. You might say ”@In @LoggedInUser user” to get the currently logged in user. That is – I think – a really elegant but really cool way to do it. What we have been trying to do is to try and figure out how to take what’s great about that model in Guice, what’s great about Seam’s contextual component model and bijection, and merge that into a single model. That’s the work we have been doing in the Expert group.
Kito Of course I think I speak for all the other JSF EG members -- we are hoping to take anything that doesn’t directly fit into WebBeans that fixes holes in JSF and roll it into JSF 2.
Gavin One of the things I spent a lot of time doing in Seam was to plug a couple of the missing things in JSF and now we need to merge that back into JSF 2. I think JSF 2 is an amazing opportunity to build something wonderful. There is a lot in JSF which is great; some of the things are done so right by comparison to everything else out there. Some other things are not done great and now we need to do them great. The good thing is now we have the experience and now we know how to fix them. There are enough frameworks like Shale, ADF, Seam, which plug the holes, that we really know where the holes are and what the solutions to them look like.
Kito Alright, that’s about it. Do you have anything else to add? Did you have a good time at Java One?
Gavin Yes, as always, I am totally wrecked. My talks went well and were well attended so it was good.
Kito Alright, Gavin. Well, thank you.


RSS feed(all feeds)

The Editor's Desk
Podcasts
Inside Facelets
In the Trenches

Site version 1.83  Report web site problems

Copyright (C) 2003-2015 Virtua, Inc. All Rights Reserved. Java, JavaServer Faces, and all Java-based marks are trademarks or registered trademarks of Oracle Corporation. in the United States and other countries. Virtua, Inc. is independent of Oracle Corporation. All other trademarks are the sole property of their respective owners.