Recently, in the independent Fatwire Developer Yahoo Group someone was asking how to get symbolic URL instead of numeric ones. Since I have a good experience writing URL Assemblers, I decided to write a blog post about improving the more commonly used URL Assembler: FistSiteII url assembler.
When writing a custom URL Assembler for FatWire, the normal starting point is the FirsSiteII URL assembler that is provided by Fatwire as an example.
A common complaint about the FSIIAssembler is generated URLs looks like this:
http://localhost:8080/cs/Satellite/FirstSiteII/FSII/Product_C/123445678/12345679
They are certainly more friendly than the "c=Product_C&cid=12345678&p=12345679" generated by default (using the QueryAssembler, actually), but they are not yet very useful for SEO, since there meaningful informations required by search engines are still missing from the URL.
So I will show in this post how to improve the FirstSiteII URL assembler to replace numeric ids with symbolic names, in order to get URLs like this:
In an ideal world I can released the full changed code for the fixed URL Assember, but as far as I know this code is proprietary, so I cannot release it with my modifications. If someone from Fatwire is willing to give me the authorization, I will make all the code available.
In the meanwhile however, I wrote an helper class where the bulk of the code goes, and I can release it os github since it is all my code. The code is now part of the FatStart project, and it is available here: AssemblerHelper source code.
So basically, getting that class and following my instructions you should be able to easily duplicate my efforts, as long as you can get the FSIIAssember source code from Fatwire (and as a regular Fatwire customer, you can just ask the Support).
The underlying idea of the AssemberHelper is replacing cids with asset names. So for example, instead of using the cid 1234568 for the page named FSIIProducts, in the url there will be exactly FSIIProducts. This replacement can be applied (or cannot applied, depending on your preferences) to the parameter p as well. I have done it but it is not strictly required.
AssemblerHelper provides 2 static methods:
The "c" (the current asset type) is always required since you need to query for the "name" field by the "cid" in the "c" table. When you do the reverse, it is still a lookup in the "c" table for the id of a rows with the given "name".
Actually, uniqueness of names is no more enforced in all the versions of Fatwire, so there is the danger that a cid cannot be replaced with an UNIQUE name. So in the code there is a provision that when a name is not unique, no replacement is done (name2cid will return the numeric cid instead of the corresponding name).
The resulting name is also URL encoded, so if can be used in URLs. Furthermore, since there is the danger of SQLInjection, I added some code to detect quotes and to filter out dangerous URL to reduce the risk.
The decoding method (name2cid) will translate a symbolic name back in as a numeric c, searching for the id of an asset of the given type with that name. The method will take care of the url decoding as well.
Once you have the AssemblerHelper code, you got the FSIIAssembler code, there are only a couple of modification required in order to use my AssemblerHelper.
In the getPath method the url is built concatenating some elements. The current cid is stored in data.cid, the current p is stored in data.p, and current c is stored in data.c
Locate the code that builds the returned path (it is easy, as long as you can read Java) and replace references to data.cid with AssemblerHelper.cid2name(data.c, data.cid) (you need an"import com.sciabarra.fatwire.*" in your source code as well).
If you want to replace also the ∫with a simbolic name, you have to add also AssemblerHelper.cid2name("Page", data.p).
That is all for the encoding.
Fatwire actually do the decoding in the SatelliteServer. Since the decoding requires access to the database, it cannot be performed on remote Satellites. For this reason, the actual decoding of the name back into a cid is performed in the wrapper page.
However, before adding the decoding in the wrapper page, an additional modifications must be done in the FSIIAssembler. There is a check that cid and the p are actually numeric in valueOf method of the inner class FSIIAssemblyData. Since now the cid and p can be string, that check must be removed. So, just locate the exception surronding the Long.valueOf(cid) and comment it out. Do this also for the Long.valueOf(p) if you have replaced also the p with a symbolic name. That is all for the URL assembler.
Now it is time to add the conversion from the name to an id in the WrapperPage. Open the FSIIWrapper CSElement and look for the render:satellitepage tag. Replace arguments
<render:argument name="cid" value="<%=ics.GetVar("cid")%>"/> <render:argument name="p" value="<%=ics.GetVar("p")%>"/>
with
<render:argument name="cid" value="<%= com.sciabarra.fatwire.AssemblerHelper.name2cid(ics.GetVar("c"),ics.GetVar("cid"))%>"/> <render:argument name="p" value="<%= com.sciabarra.fatwire.AssemblerHelper.name2cid("Page",ics.GetVar("p"))%>"/>
And now you are done.
This solution is a potential performance killer since it performs a query for each URL requests. Fatwire has a ResultSet cache, so this problem could be not an issue, but if it is (maybe because there are too many different requests so the query cache cannot cache enough result sets) my suggestion is to add an additional caching level, storing name2cid mapping in an HashMap. I would use a WeakHashMap (so memory won't be an issue) and store the hash map in the application context, but I have not tried this solution so far. It is left as an exercise for the reader...