Friday, June 8, 2007

xmlbean: create .xsdconfig automatically from xsd file with customized package name

One task I had before is to create a xmlbean jar file from a set of very big xsd files, the resulting jar file is about 40MB, and also the package name of generated xmlbean classes need to be different from the default one, basically it needs to have version name:

original package name:
com.mycompany.account.address

modified package name (added v2):
com.mycompany.v2.account.address

There is only one entry xsd file, but it includes about hundreds of other xsd files, so it is almost impossible to create a .xsdconfig that will map the xsd name space to package name.

My goal is to create a repeatable and easy way to auto. generate the .xsdconfig with the mapping.

Step 1:
Use textpad or write a simple java program to search the keyword "targetNamespace=" for all xsd files, and save the output to a file named raw_namespace.txt

Step 2:
Create a java program that will read the raw_namespace.txt, then use xmlbean internal util to create package name and map that package name to new package name, then save the output to .xsdconfig

Step 2.1:
defined the mapping:
private final static HashMap nsToPackage = new HashMap();
static {
nsToPackage.put("http://service.mycompany.com", "com.mycompany.v2");
}

Step 2.2:

read the raw_namespace.txt and loop the line, then pass the value from "targetNamespace" to this function to get a new complete package name, the key here is to create a fake name space, then use the xmlbean util program NameUtil.getPackageFromNamespace to generate the package name from the fake name space:

private String getPackageName(String ns) {
String pkn = null;
Iterator it = nsToPackage.keySet().iterator();
while (it.hasNext()) {
String nsprefix = (String)it.next();
if (ns.startsWith(nsprefix)) {
StringBuffer sb = new StringBuffer();
sb.append(nsToPackage.get(nsprefix));
String nssuffix = ns.substring(nsprefix.length()+1);
String pname = NameUtil.getPackageFromNamespace(nssuffix);
sb.append(pname);
pkn = sb.toString();
break;
}
}
return pkn;
}


Step 2.3:
combine all the output from step 2.2 to .xsdconfig, which looks like this:
<xb:config xmlns:xb="http://xml.apache.org/xmlbeans/2004/02/xbean/config">
<xb:namespace uri="http://service.mycompany.com/account/address/">
<xb:package>com.mycompany.v2.account.address</xb:package>
</xb:namespace>
<xb:namespace uri="http://service.mycompany.com/common/getZipcode/">
<xb:package>com.mycompany.v2.common.getZipcode</xb:package>
</xb:namespace>
</xb:config>


The about steps can be simplify by hooking into ant API and then just do an ant build each time, but you get the point.

Thursday, May 31, 2007

A simple way to make DWR able to see portlet session

Note: If you are running tomcat 5.5, there is way to configure tomcat 5.5 that will allow sharing session between portlet and servlet, google for "emptySessionPath".

If you are still interesting, then read on.

DWR is a servlet that normally not able to see portlet session when running in portal. The reason is the way use dwr in jsp page:
<script src="/myapp/dwr/interface/dwrTest.js"></script>

Because it goes to "/myapp" directly and by pass the usual "/portal" context, then all java code invoked by dwr won't able to see the portlet session.

One solution I found is to override the dwr internal javascript variable "_path" to a portlet path, then in the portlet, reassemble the request path and invoke the dwr servlet within the portlet.

Original dwr path:
/myapp/dwr/exec/dwrTest.getZipcode

Modified dwr path which will invoke dwr within portal:
/portal/xxxx-portal-specified-param&servletName=dwr& execName=/exec/dwrTest.getZipcode


Here is a sample that I created for Vignette 7.x portal runs in tomcat 5.

First, create a jsp page to be included by other jsp that use DWR.

sample DwrPortlet.jsp (partial listing)
....
//1. create a portlet url that points to the portlet that invoke the dwr
<portlet:renderurl var="dwrPortletUrl" windowstate="RAW">
<portlet:param name="servletName" value="/dwr">
</portlet:param>

//2. over ride the internal _path to point to the portlet
<script><br /> var dwrPortletPath = "${dwrPortletUrl}";<br /> if (window.dwrTest) {dwrTest._path = dwrPortletPath ;}<br /></script>

//3. must use post and iframe method
if (window.DWREngine) { DWREngine._verb = "POST";DWREngine._method = DWREngine.IFrame; }

===== end sample DwrPortlet.jsp ====</portlet:renderurl>


Now in your portlet java code, do this to invoke the dwr:
String servletName = (String)request.getParameter("servletName");
if (StringUtils.isNotBlank(servletName)) {
if ("/dwr".equals(servletName)) {
servletName = servletName + request.getParameter("execName");
}
request.getPortletSession().getPortletContext(). getRequestDispatcher(servletName).include(request, response);
}


In the dwr code i.e. the DwrTest.java, use this code to get the portlet request or response:

PortletRequest portletRequest = (PortletRequest)WebContextFactory.get().getHttpServletRequest(). getAttribute("javax.portlet.request");