Matt RaibleMatt Raible is a Java Champion and Developer Advocate at Okta. developer.okta.com

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

Building high-content web applications

I've recently been tasked with rebuilding a JSP-based site using a Struts architecture. One of the issues (that I see) in the current architecture is that there are a number of JSPs with the text for the pages hard-coded in them. After re-writing this app, we plan on deploying it to 25+ customers - and we certainly don't want to have 25 different JSPs (with text) for each customer. I've proposed a database, but that might be a little resource intensive - so I'm wondering how folks have done this in the past (I'm sure it's been done before)?

Options I see are:

  • A Database table with the following columns (page_id, title, content, section_id).
  • Text files that are imported using <c:import url=""/>

What options have you used (feel free to add more) - if you've used the database approach - how do you define the page table? Maybe we should use the Roller way and use Velocity and OSCache.

Posted in Java at Aug 19 2003, 06:30:28 PM MDT 18 Comments
Comments:

Many CMS store content in the database and scale pretty well. Actually having the content in the db is the right architecture.

Posted by Unknown on August 19, 2003 at 07:02 PM MDT #

We use a custom JSP taglib that executes Velocity templates that live on the filesystem. Something like this:
    <tags:showSnippet path="some/path/to/template"/>
where some/path/to/template.vm lives on the filesystem rooted somewhere that is configurable in application settings. Velocity is a dream for this - drop some stuff in the context like request, session, application collections, as well as context URL, etc and you're good to go!

Posted by Erik Hatcher on August 19, 2003 at 08:13 PM MDT #

So you don't use a Velocity Servlet? If since this is a custom JSP Tag - I could presumably use this with Tiles, eh? Sounds pretty damn cool - if you can hook me up with a few hints for creating the taglib, I'll be on my way! Thanks chief. ;-)

Posted by Matt Raible on August 19, 2003 at 09:16 PM MDT #

I would love to hear more about this Velocity thing too. To add my 2 cents, storing the content in a database is a good idea and it is the common thing to do. However you might have to look at a few other issues. Mainly how and who will be updating the content. If it's a "client" that needs to update the content, i've always found it better to provide the easiest way possible (depending on user knowhow, etc) for that client to update the content.

Posted by John Cavacas on August 19, 2003 at 09:52 PM MDT #

Here's how it currently works. If a customer (a.k.a. client) wants to update the content of their site, they submit a help ticket. At that point, an HTML or JSP Developer will open up the JSP and change the text - repackage the war file and redeploy. So you can see here - I'm just trying to make my job easier. I don't want to open a JSP, edit the content and check it back into the source tree. I just want to hit a web interface or database, update and save. IMO, it's very important we get this designed right now. Currently there is only one site designed this way - so it's easy to update and maintain - but the goal is to produce 42 in the next 6 months. This could <em>quickly</em> become a maintenance nightmare if each site requires code changes for content update.

Posted by Matt Raible on August 19, 2003 at 10:08 PM MDT #

Why not preprocess JSP files w/ XSLT? This would have no performance effects at deployment and would make development easier. Good separation of presentation and content.

Posted by Don Brown on August 20, 2003 at 01:43 AM MDT #

Why not get the text from the application context? Unless you are running your application in a distributed environment, you can even build a small admin interface around it in order to modify the text on the fly. The initial text would be in the web.xml.

Posted by Fokko Degenaar on August 20, 2003 at 07:32 AM MDT #

For heavy traffic sites hitting the database to retrieve content every time would probably not be a good idea. You may be able to put in some caching but even that can be problematic. One common solution is to have content stored in a database for editing but then published to static files when moved into production. This provides the best of both worlds since the content can be modified on the fly but the loading of static files will be less of a bottleneck. That's the theory at least.

Posted by Anthony Eden on August 20, 2003 at 08:16 AM MDT #

very interesting, does anybody have some source code perhaps? maybe even to use a database approach with hibernate?

Posted by anonymous on August 20, 2003 at 09:08 AM MDT #

We have the exact same issue, however our solution is a bit nonstandard (but not far removed from erik's). I wrote a parser for dreamweaver templates (dwt/lbi files, and the .htm files marked up that way) and an engine that allows you to pass in a map of replacement 'content' object before rendering (we can also rewrite/replace URLs). The 'static site' is stored outside our webapp, and our webapp just knows the location of those files on disk + where a browser would locate them (get this info via JNDI). Finally, I wrote a struts tiles-a-like taglib that allows you to reference an htm file as your template. By doing all this we get all static content (including images etc) out of our webapp and onto faster non-java webservers. As its DW syntax, we can edit the content using DW or Macromedia Contribute (their low-rent CMS solution), and customers can edit this themselves (as long as they maintain the same edi/lbi regions we replace). Edits affect the 'dynamic' site immediately. Every customer gets the *same* webapp, reducing our support overhead. We cache parse trees, much like velocity, so this isn't horribly slow (its actually faster during development than the tiles equivalent). We find it useful to get the customer to sign off on the html prototype developed this way, and can demo features to marketing (in static html) before spending time implementing them, without cut & paste from the prototype to the real development - the prototype is our template. If I was doing this and *didnt* have nice DW templates to begin with, Erik's solution would be attractive. The downside of all these template solutions is however that you are maintaining lots of markup. Tapestry-like solutions (building pages from components, framework manages URLs for you) are *much* more maintainable from a code perspective, but AFAIK Tapestry's component templates need to be on the resource path (i.e. in the app). Looking ahead, the portlet spec (JSR168) is all about producing fragments of content to be embedded in other people's pages, if you're feeling bleeding edge.

Posted by Brian Ewins on August 20, 2003 at 11:29 AM MDT #

How does the ASP.NET guys handle it with their http://www.ibuyspycobol.com/ sample?

Posted by anony on August 20, 2003 at 02:14 PM MDT #

Erik and Brian any chance you guys could post some more details on you solutions on your blogs. I really like Brian's use of Dreamweaver. Erik's version reminds me of an article on OnJava about using Velocity template to create JSP tags. I guess what gets me is why this can't be just simple JSP? I'm sure I am missing something, but I don't see why you couldn't have bean or beans with that have vendor specific site info in them. As long you implement them via an interface, you implement them any way you want. First, you could try using database implementation. If this doesn't scale like Anthony fears, you could drop down to a different implementation. I still suggest the database, because it make easier to make other clients to slice and dice the data. Thus data could be reused; for example, text used on the website could easily used in a mailmerge email, etc. I really worry about generating static site. It seems, at least this is my luck, any "generated" code starts getting edited. Thus, you'd have you generation part, then you have support the "special" case for customer site 10, 15, 24... you get the idea.

Posted by Jeff Duska on August 20, 2003 at 03:10 PM MDT #

No blog, and the code is the property of my employers. I doubt if this will help you greatly, the main point is to get your templates out of your webapp as Erik did.

The gist of it...there are 2 entry points to rendering with a template, one from a servlet (mapped to *.htm and also used to trap 404 errors), the other in the template:insert tag. Both create a PageContext object (very like the JSP2 one) which carries request context. They both use a template repository to get the template object matching the requested path (checking file timestamps and parsing on demand).

The template is a List of 'Content' objects (these have one method, render(PageContext, TemplateRepository, ContentMap)) . Pieces marked as being library items/editable regions/urls are stored in here as NamedContent (the name is eg 'lbi:/Library/pagebody.lbi') . NamedContent renders itself by looking up a replacement in the content map, and if there isnt one, renders the content parsed from its base file. The contentmap contains replacements specified in a global replacement file (xml) as the actual text, uris to include, or bean.properties in scope; in the jsp case it also contains replacements pushed in by the template tags. This file is inside the webapp and isn't editable by the customer, since changing it will typically break all the site navigation.

In dreamweavermx you should model pages just like their exported xml, if you mean to support repeat regions and the like (much more complex). In DW4 the situation is simpler, with the most complex arrangement of templates being: a jsp based on an htm which includes an lbi inside an editable region, page based on a dwt file which may also include lbi files. If you try to implement this you'll find that parsed pages act both as a list of content (rendering them without replacements) and as a map of replacement editable regions which override their underlying template (in MX there are nested templates, so its much more obvious that you need to do this). You need to build the content map for a request by nesting the content maps of these templates.

URLContent objects are written as-is (for absolute urls and j_security_check), prefixed with the 'static site' location (for non-html/dir urls), prefixed with the 'dynamic site' location and rewritten with session id (for html/dir urls). Note all URLs are being made absolute in this process, so they can be used as templates for any page in your site.

I map the servlet to 404 because it can recognize when you tried to access a dir, and it redirects you to the welcome file (prior to servlet 2.4 there is no way to use a servlet as a welcome file). Once redirected to eg /index.html, if that file does not exist, the servlet forwards to the 'real' 404 error page (specified as an init param). You need a dummy '/index.htm' in your webapp to trigger the servlet for '/', at least on some servlet containers.

DW parsing: the grammar for DW templates is in its online help (if you DW was going to be any use to you you have this), in the section on "Working with Multiple Pages". DW4 syntax is fairly simple, DWMX requires you to write a partial javascript parser though and you should use javacc/antlr. URLs are parsed by recognizing all the standard HTML4 URL attributes (there's about 9 of them in the spec), plus 'background'. The HTML parsing that does this is extremely forgiving, so that the rendered output with no replacements is pretty much identical to the input (much more forgiving than xmlc for example).

The devil is in the detail of course. And as I said, this approach works best on web 'sites' (customer drives the l+f) as opposed to web 'apps' (form follows function) where something more like Tapestry would suit you better.

Posted by Brian Ewins on August 21, 2003 at 03:59 AM MDT #

I second the Velocity approach. Those can be reloaded dynamically, and you can get a little slicker with the macros. The database approach is great for a web interface, but most of the time, it's easier to edit a text file (IMO) if possible. Plus, it's easier to source-code control, and you get syntax highlighting :)

Posted by Josh Martin on June 26, 2008 at 09:24 PM MDT #

I second the Velocity approach. Those can be reloaded dynamically, and you can get a little slicker with the macros. The database approach is great for a web interface, but most of the time, it's easier to edit a text file (IMO) if possible. Plus, it's easier to source-code control, and you get syntax highlighting :)

Posted by Josh Martin on June 26, 2008 at 09:25 PM MDT #

[Trackback] PIBlog: Building a Python Web Application, Part 1 A Student's Guide to Software Engineering Projects - Free eBook for Students...

Posted by paradox1x on June 27, 2008 at 05:07 AM MDT #

Storing content in the DB is the right thing to do. Its basically how any CMS works as mentioned above. However, if you are really worried about scaling (and you shouldn't be unless you already have a reason to be), then you should consider doing some sort of page caching to the file system or with something like MemCached with an expiration policy whenever the content is remove/updated.

Posted by Russell Scheerer on June 27, 2008 at 08:10 AM MDT #

Storing content in the DB is the right thing to do. Its basically how any CMS works as mentioned above. However, if you are really worried about scaling (and you shouldn't be unless you already have a reason to be), then you should consider doing some sort of page caching to the file system or with something like MemCached with an expiration policy whenever the content is remove/updated.

Posted by Russell Scheerer on June 27, 2008 at 08:11 AM MDT #

Post a Comment:
  • HTML Syntax: Allowed