Why does gson.toJson(obj) return null when I do this?
public class LoginServlet extends HttpServlet {
#Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
Gson gson = new Gson();
if (user != null) {
resp.setContentType("application/json");
resp.getWriter().println(gson.toJson(user));
} else {
class Url {
private String url;
Url(String url) {
this.url=url;
}
}
Url obj = new Url(userService.createLoginURL(req.getRequestURI()));
resp.setContentType("application/json");
resp.getWriter().println(gson.toJson(obj));
}
}
}
When I define the Url class outside the LoginServlet class it works and returns a json string of the url object?
class Url {
private String url;
Url(String url) {
this.url=url;
}
}
public class LoginServlet extends HttpServlet {
#Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
Gson gson = new Gson();
if (user != null) {
resp.setContentType("application/json");
resp.getWriter().println(gson.toJson(user));
} else {
Url obj = new Url(userService.createLoginURL(req.getRequestURI()));
resp.setContentType("application/json");
resp.getWriter().println(gson.toJson(obj));
}
}
}
I guess I'm still not sure what the actual problem you're having is...I can't get Gson to give me a null.
import com.google.gson.Gson;
public class GsonUrlParse {
public static void main( String[] args ) {
Url url = new Url( "foo" );
System.out.println( new Gson().toJson( url ) );
Url nurl = new Url( null );
System.out.println( new Gson().toJson( nurl ) );
}
}
class Url {
String url;
public Url( String url ) {
this.url = url;
}
}
Output:
{"url":"foo"}
{}
Related
I have this in my servlet:
#Override
public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
String responseText;
try {
String base = request.getParameter("base");
String convertTo = request.getParameter("convertTo");
double amount = Double.parseDouble(request.getParameter("amount"));
responseText = currencyRatesCalculation.getConvertedAmount(base, convertTo, amount);
response.setContentType(APPLICATION_JSON);
}
catch (NullPointerException | NumberFormatException exception) {
LOG.error("{} : Exception when parsing request parameters : ", LOG_STRING, exception);
responseText = "Error when parsing request parameters";
}
response.getWriter().write(responseText);
}
This is how I'm trying to test it:
#Mock
private MockSlingHttpServletRequest req;
#Mock
private MockSlingHttpServletResponse res;
#InjectMocks
private CurrencyExchangeServlet underTest;
#Before
public void setup() {
underTest = new CurrencyExchangeServlet();
req = context.request();
res = context.response();
}
#Test
public void doGet_shouldReturnHeaderAsExpected() throws IOException {
underTest.doGet(req, res);
assertEquals(req.getContentType(), "application/json");
}
junitx.framework.ComparisonFailure:
Expected :null
Actual :application/json
Some advice? Where is mistake? Im watching on tutorials but cant find excatly some example for this what I have
I try to use KSOAP2 with Basic Authentication. I´m download ksoap2-android-assembly-3.0.0-jar-with-dependencies.jar, ksoap2-extras-3.0.0.jar, ksoap2-extra-ntlm-3.0.0.jar. I Tried to use the code below:
ArrayList<HeaderProperty> headerProperty = new ArrayList<HeaderProperty>();
headerProperty.add(new HeaderProperty("Authorization", "Basic " +
org.kobjects.base64.Base64.encode("user:password".getBytes())));
it's generate the error:
java.io.IOException: HTTP request failed, HTTP status: 401
ERROR:java.io.IOException:HTTP request failed, HTTP status: 401
I Tried to use the code below too:
HttpTransportBasicAuth androidHttpTpAut = new HttpTransportBasicAuth(URL, "user", "password");
androidHttpTpAut.getServiceConnection().connect();
again not work, generate the error:
Exception in thread "main" java.lang.NoClassDefFoundError: org/ksoap2/transport/HttpTransport
at java.lang.ClassLoader.defineClass1(Native Method)
Anybody do this work fine ?
After many test, I found that the code below need to change:
ArrayList headerProperty = new ArrayList();
headerProperty.add(new HeaderProperty("Authorization", "Basic " +
org.kobjects.base64.Base64.encode("user:password".getBytes())));
androidHttpTransport.call(SOAP_ACTION, envelope);
Change above androidHttpTransport.call() to below polymorphic method:
androidHttpTransport.call(SOAP_ACTION, envelope, headerProperty);
It´s necessary put the parameter headerProperty in method androidHttpTransport.call.
And, I use only ksoap2-android-assembly-3.0.0-jar-with-dependencies.jar in project.
Thanks
You can try the following code. It worked in my case, hope it will help you too.
Create a class called HttpTransportBasicAuth
import org.ksoap2.transport.*;
import org.ksoap2.transport.HttpTransportSE;
import java.io.*;
public class HttpTransportBasicAuth extends HttpTransportSE {
private String username;
private String password;
public HttpTransportBasicAuth(String url, String username, String password) {
super(url);
this.username = username;
this.password = password;
}
public ServiceConnection getServiceConnection() throws IOException {
ServiceConnectionSE midpConnection = new ServiceConnectionSE(url);
addBasicAuthentication(midpConnection);
return midpConnection;
}
protected void addBasicAuthentication(ServiceConnection midpConnection) throws IOException {
if (username != null && password != null) {
StringBuffer buf = new StringBuffer(username);
buf.append(':').append(password);
byte[] raw = buf.toString().getBytes();
buf.setLength(0);
buf.append("Basic ");
org.kobjects.base64.Base64.encode(raw, 0, raw.length, buf);
midpConnection.setRequestProperty("Authorization", buf.toString());
}
}
}
And then, change the following in your proxy class (refer the proxy class below)
protected org.ksoap2.transport.Transport createTransport()
{
return new HttpTransportBasicAuth(url,username,password);
}
Proxy class
import java.util.List;
import org.ksoap2.HeaderProperty;
import org.ksoap2.SoapFault;
import org.ksoap2.serialization.AttributeContainer;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
public class Z_WS_SCAN_REPLENISHMENT
{
interface IWcfMethod
{
ExtendedSoapSerializationEnvelope CreateSoapEnvelope() throws java.lang.Exception;
Object ProcessResult(ExtendedSoapSerializationEnvelope envelope,SoapObject result) throws java.lang.Exception;
}
String url="http://example.com/z_ws_scan_replenishment/200/z_ws_scan_replenishment/z_ws_scan_replenishment";
String username = "username";
String password = "password";
int timeOut=60000;
public List< HeaderProperty> httpHeaders;
IServiceEvents callback;
public Z_WS_SCAN_REPLENISHMENT(){}
public Z_WS_SCAN_REPLENISHMENT (IServiceEvents callback)
{
this.callback = callback;
}
public Z_WS_SCAN_REPLENISHMENT(IServiceEvents callback,String url)
{
this.callback = callback;
this.url = url;
}
public Z_WS_SCAN_REPLENISHMENT(IServiceEvents callback,String url,int timeOut)
{
this.callback = callback;
this.url = url;
this.timeOut=timeOut;
}
protected org.ksoap2.transport.Transport createTransport()
{
return new HttpTransportBasicAuth(url,username,password);
}
protected ExtendedSoapSerializationEnvelope createEnvelope()
{
return new ExtendedSoapSerializationEnvelope();
}
protected void sendRequest(String methodName,ExtendedSoapSerializationEnvelope envelope,org.ksoap2.transport.Transport transport) throws java.lang.Exception
{
transport.call(methodName, envelope,httpHeaders);
}
Object getResult(Class destObj,SoapObject source,String resultName,ExtendedSoapSerializationEnvelope __envelope) throws java.lang.Exception
{
if (source.hasProperty(resultName))
{
Object j=source.getProperty(resultName);
if(j==null)
{
return null;
}
Object instance=__envelope.get((AttributeContainer)j,destObj);
return instance;
}
else if( source.getName().equals(resultName)) {
Object instance=__envelope.get(source,destObj);
return instance;
}
return null;
}
public Bapireturn1 ZScanReplenishment(final String ILgnum,final String IPernr,final String IScannedId,final String IScannedLgpl ) throws java.lang.Exception
{
return (Bapireturn1)execute(new IWcfMethod()
{
#Override
public ExtendedSoapSerializationEnvelope CreateSoapEnvelope(){
ExtendedSoapSerializationEnvelope __envelope = createEnvelope();
SoapObject __soapReq = new SoapObject("urn:sap-com:document:sap:soap:functions:mc-style", "ZScanReplenishment");
__envelope.setOutputSoapObject(__soapReq);
PropertyInfo __info=null;
__info = new PropertyInfo();
__info.namespace="";
__info.name="ILgnum";
__info.type=PropertyInfo.STRING_CLASS;
__info.setValue(ILgnum);
__soapReq.addProperty(__info);
__info = new PropertyInfo();
__info.namespace="";
__info.name="IPernr";
__info.type=PropertyInfo.STRING_CLASS;
__info.setValue(IPernr);
__soapReq.addProperty(__info);
__info = new PropertyInfo();
__info.namespace="";
__info.name="IScannedId";
__info.type=PropertyInfo.STRING_CLASS;
__info.setValue(IScannedId);
__soapReq.addProperty(__info);
__info = new PropertyInfo();
__info.namespace="";
__info.name="IScannedLgpl";
__info.type=PropertyInfo.STRING_CLASS;
__info.setValue(IScannedLgpl);
__soapReq.addProperty(__info);
return __envelope;
}
#Override
public Object ProcessResult(ExtendedSoapSerializationEnvelope __envelope,SoapObject __result)throws java.lang.Exception {
return (Bapireturn1)getResult(Bapireturn1.class,__result,"EReturn",__envelope);
}
},"");
}
protected Object execute(IWcfMethod wcfMethod,String methodName) throws java.lang.Exception
{
org.ksoap2.transport.Transport __httpTransport=createTransport();
ExtendedSoapSerializationEnvelope __envelope=wcfMethod.CreateSoapEnvelope();
sendRequest(methodName, __envelope, __httpTransport);
Object __retObj = __envelope.bodyIn;
if (__retObj instanceof SoapFault){
SoapFault __fault = (SoapFault)__retObj;
throw convertToException(__fault,__envelope);
}else{
SoapObject __result=(SoapObject)__retObj;
return wcfMethod.ProcessResult(__envelope,__result);
}
}
java.lang.Exception convertToException(SoapFault fault,ExtendedSoapSerializationEnvelope envelope)
{
return new java.lang.Exception(fault.faultstring);
}
}
These changes did the magic for me. Hope it will work for you as well.
I'm modeling a controller reflection based. I would like to know if you agree my implementation and what could to be enhanced.
I'm starting with reflection and I would like to know if I'm using good practices.
Sample: www.sample.com/Main?mvc=user/edit/5
See it:
public class StartController extends HttpServlet {
#Override
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String vars = req.getParameter("mvc");
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
MainController main = new MainController();
main.RESTProcess(req, resp, context, vars);
}
}
public class MainController extends Controller {
public void RESTProcess(HttpServletRequest req, HttpServletResponse resp, ApplicationContext context, String vars)
throws IOException, ServletException
{
String[] url;
int nvars;
String controller = null;
String action = null;;
String[] params = null;
int i;
int n;
String controllerName;
String actionName;
if(vars == null || "".equals(vars.trim()))
{
this.redirect(resp,"home");
}
else
{
url = vars.split("/");
nvars = url.length;
if(nvars > 0)
{
controller = url[0].trim(); //users
if(nvars > 1)
{
action = url[1].trim(); //edit
if(nvars > 2)
{
n = 0;
params = new String[nvars - 2]; //array[0] = 5 (iduser)
for(i = 2; i < nvars; i++)
{
params[n] = url[i];
n++;
}
}
}
controllerName = this.getFirstUpper(controller) + "Controller"; //HomeController, UserController pattern
if(!controllerName.equals("Controller"))
{
actionName = "action" + this.getFirstUpper(action); //actionIndex, actionEdit pattern
if(!action.equals("action"))
{
try
{
Class classe = Class.forName("com.sample.controller." + controllerName);
try
{
Constructor c = classe.getConstructor(HttpServletRequest.class, HttpServletResponse.class, String[].class);
try
{
Object obj = c.newInstance(req, resp, context, params);
Method m = classe.getMethod(actionName, null);
m.invoke(obj, null);
System.out.println(obj.toString());
}
catch (Exception e)
{
e.printStackTrace();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
else
{
this.redirect(resp,"home");
}
}
}
public String getFirstUpper(String str)
{
Integer x = str.length();
str = str.substring(0,1).toUpperCase().concat(str.substring(1, x));
return str;
}
}
public class UserController extends Controller {
private HttpServletRequest req;
private HttpServletResponse resp;
private ApplicationContext context;
private String[] params;
public UserController(HttpServletRequest req, HttpServletResponse resp, ApplicationContext context, String[] params)
{
this.req = req;
this.resp = resp;
this.context = context;
this.params = params;
}
public void actionEdit()
{
Long id = Long.valueOf(this.params[0]);
System.out.println("/home/edit");
}
}
I have this action class, this class takes care of my response
Update now passing response from DownloadStatus class, but it looks like it is null
public final class DownloadStatus extends ActionSupport implements ServletRequestAware,ServletResponseAware
{
static Logger logger = Logger.getLogger(DownloadStatus.class);
private HttpServletRequest request;
private HttpServletResponse response;
private File cfile;
private String cfileFileName;
#Override
public String execute()
{
logger.debug("Inside DownloadStatus.execute method")
try {
ChainsInvoker invoker = new ChainsInvoker()
def executionResponse = invoker.invoke(request, MYChains.download, cfile, cfileFileName)
if(executionResponse == null || ErrorHandler.checkIfError(executionResponse))
{
return ERROR
}
response.setContentType("APPLICATION/xml")
logger.debug("filename: $cfileFileName")
response.addHeader("Content-Disposition", "attachment; filename=\""+cfileFileName+"\"")
response.getWriter().print(executionResponse)
logger.debug("executionResponse :" + executionResponse)
invoker.invoke(MYChains.clean)
}catch (Exception exp) {
logger.error("Exception while Creating Status ")
logger.error(exp.printStackTrace())
}
return NONE
}
#Override
public void setServletRequest(HttpServletRequest request) { this.request = request; }
#Override
public void setServletResponse(HttpServletResponse response) { this.response = response; }
public File getcfile() { cfile }
public void setcfile(File cfile) { this.cfile = cfile }
public String getcfileFileName() { cfileFileName }
public void setcfileFileName(String cfileFileName){ this.cfileFileName = cfileFileName }
}
and below class to write stream into response
class DownloadStatusResponse implements Command {
static Logger logger = Logger.getLogger(DownloadStatusResponse.class);
#Override
public boolean execute(Context ctx) throws Exception
{
logger.debug("Inside DownloadStatusResponse.execute() method")
OutputStream response = null;
if(ctx.get(ContextParams.absFileName) != null && ctx.get(ContextParams.absFileName).toString().trim().length() != 0 )
{
HttpServletResponse resp = ctx.get(ContextParams.response)
/*I am trying to get Response here*/
response=downloadStatusFile(ctx.get(ContextParams.absFileName).toString(),resp)
}
logger.debug("Response: " + response)
ctx.put(ContextParams.response,response); /*ContextParams is a enum of keywords, having response*/
return false;
}
private OutputStream downloadStatusFile(String filename,HttpServletResponse resp)
{
logger.info("Inside downloadStatusFile() method")
File fname = new File(filename)
if(!fname.exists())
{
logger.info("$filename does not exists")
return null
}
else
{
resp.setContentType("APPLICATION/xml")
/*Exception: cannot setContentType on null object*/
resp.addHeader("Content-Disposition", "attachment; filename=\""+fname.getName()+"\"")
FileInputStream istr = new FileInputStream(fname)
OutputStream ostr = resp.getOutputStream()
/*I need to use resp.getOutputStream() for ostr*/
int curByte=-1;
while( (curByte=istr.read()) !=-1)
ostr.write(curByte)
ostr.flush();
}
return ostr
}
}
My question is how can ostr be returned to the response in DownloadStatus class?
Update (working test servlet)
I have this below servlet which does the job of getting file content into a stream and giving it back to the HttpServletResponse, but i want to use it in above code
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String fileName = req.getParameter("zipFile");
if(fileName == null) return;
File fname = new File(fileName);
System.out.println("filename");
if(!fname.exists()) {System.out.println("Does not exists"); return;}
FileInputStream istr = null;
OutputStream ostr = null;
//resp.setContentType("application/x-download");
resp.setContentType("APPLICATION/ZIP");
resp.addHeader("Content-Disposition", "attachment; filename=\""+fname.getName()+"\"");
System.out.println(fname.getName());
try {
istr = new FileInputStream(fname);
ostr = resp.getOutputStream();
int curByte=-1;
while( (curByte=istr.read()) !=-1)
ostr.write(curByte);
ostr.flush();
} catch(Exception ex){
ex.printStackTrace(System.out);
} finally{
try {
if(istr!=null) istr.close();
if(ostr!=null) ostr.close();
} catch(Exception ex){
ex.printStackTrace();
System.out.println(ex.getMessage());
}
}
try {
resp.flushBuffer();
} catch(Exception ex){
ex.printStackTrace();
System.out.println(ex.getMessage());
}
}
}
As far as I understand all you require is how to download a file using Struts2.
You need something like this is your struts.xml file
<action name="downloadfile" class="DownloadAction">
<result name="success" type="stream">
<param name="contentType">application/pdf</param>
<param name="inputName">inputStream</param>
<param name="contentDisposition">attachment;filename="document.pdf"</param>
<param name="bufferSize">1024</param>
</result>
</action>
Code:
public class DownloadAction extends ActionSupport {
private InputStream inputStream;
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public String execute() throws FileNotFoundException {
String filePath = ServletActionContext.getServletContext().getRealPath("/uploads");
File f = new File(filePath + "/nn.pdf");
System.out.println(f.exists());
inputStream = new FileInputStream(f);
return SUCCESS;
}
}
I tend to have this statement
STKUser authenticatedUser = (STKUser) request.getSession().getAttribute("STKUserSession");
in every method of my Classes. authenticatedUser is used for authorization checks/ logic flow. Is this OK or should I be coding this class differently??? Also are there any recommended books that could help improve my coding for Java Classes like the one below, which are used in web applicatons? Most of my Classes looked like the one below.
public class TD0301AssignmentForm extends Form {
private boolean notifyApprover = false;
boolean employeeChange = false;
public TD0301AssignmentForm(TD0301AssignmentDAO dao) {
this.dao = dao;
}
private TD0301Assignment unlockAssignment(HttpServletRequest request) {
STKUser authenticatedUser = (STKUser) request.getSession().getAttribute("STKUserSession");
TD0301Assignment tdas = new TD0301Assignment();
notifyApprover = true;
boolean unlock = false;
try {
// get the original data
tdas = dao.retreive(request.getParameter("calc_num"), request.getParameter("calc_rev"), request.getParameter("calc_dept"), authenticatedUser);
if ("3".equals(tdas.getForm_approve_state()) && authenticatedUser.getBadge().equals(tdas.getOriginator())) {
tdas.setForm_approve_state("1");
notifyApprover = true;
unlock = true;
}
}
public TD0301Assignment updateAssignment(HttpServletRequest request) {
STKUser authenticatedUser = (STKUser) request.getSession().getAttribute("STKUserSession");
....
if (authenticatedUser.getBadge().equals(tdas.getOriginator())) {
//do something
}
EDIT
The TD0301AssignmentForm Class is accessed using these two Classes.
Servlet
TD0301AssignmentDAO dao = new TD0301AssignmentDAO();
TD0301AssignmentForm form = new TD0301AssignmentForm(dao);
TD0301Assignment obj = new TD0301Assignment();
String pkString = "calc_num=" + request.getParameter("calc_num") + "&calc_rev=" + request.getParameter("calc_rev") + "&calc_dept="
+ request.getParameter("calc_dept");
modelMap.put("dbTable", dbTable);
modelMap.put("action", request.getRequestURL());
modelMap.put("reportTitle", "CommitmentReport");
// I think this is the Application Controller Strategy
actionMap.put(null, new ListAction(modelMap, form, "WEB-INF/views/genericList_v.jsp", "WEB-INF/views/genericList_v.jsp"));
actionMap.put("list", new ListAction(modelMap, form, "WEB-INF/views/genericList_v.jsp", "WEB-INF/views/genericList_v.jsp"));
actionMap.put("view", new ViewAction(modelMap, form, obj, "WEB-INF/views/genericView_v.jsp", "WEB-INF/views/genericView_v.jsp"));
actionMap.put("delete", new DeleteAction(modelMap, form, obj, "WEB-INF/views/genericDeleteConfirm_v.jsp", "WEB-INF/views/genericView_v.jsp"));
actionMap.put("sqlConfirmDelete", new DeleteConfirmAction(form, request.getRequestURL() + "?message=Deletion was successful!", request.getRequestURL()
+ "?method=view&" + pkString));
actionMap.put("edit", new EditAction(modelMap, form, obj, "WEB-INF/views/genericEdit_v.jsp", "WEB-INF/views/genericView_v.jsp"));
actionMap.put("sqlUpdate", new UpdateAction(modelMap, form, obj, request.getRequestURL() + "?message=Update was successful!", "WEB-INF/views/genericEdit_v.jsp"));
actionMap.put("new", new NewAction(modelMap, form, "WEB-INF/views/genericAdd_v.jsp"));
actionMap.put("sqlInsert", new InsertAction(modelMap, form, obj, request.getRequestURL() + "?message=Insert was successful!", "WEB-INF/views/genericAdd_v.jsp"));
String op = request.getParameter("method");
ControllerAction action = (ControllerAction) actionMap.get(op);
if (action != null) {
action.service(request, response);
} else {
String url = "WEB-INF/views/errorMessage_v.jsp";
String errMessage = "Operation '" + op + "' not a valid for in '" + request.getServletPath() + "' !!";
request.setAttribute("message", errMessage);
request.getRequestDispatcher(url).forward(request, response);
}
public class EditAction implements ControllerAction {
private Form form;
private Object obj;
private String xPage;
private String yPage;
private HashMap modelMap;
public EditAction(HashMap modelMap, Form form, Object obj, String yPage, String xPage) {
this.form = form;
this.obj = obj;
this.xPage = xPage;
this.yPage = yPage;
this.modelMap = modelMap;
}
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
obj = form.edit(request);
Iterator it = modelMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry)it.next();
request.setAttribute(pairs.getKey().toString(), pairs.getValue());
}
request.setAttribute("obj", obj);
request.setAttribute("form", form);
if (form.isSucces()) {
RequestDispatcher view = request.getRequestDispatcher(yPage);
view.forward(request, response);
}
else {
RequestDispatcher view = request.getRequestDispatcher(xPage);
view.forward(request, response);
}
}
}
If you find yourself retrieving the same value all the time, you'd probably at least want to abstract it into a method in a base class:
public class BaseForm extends WhateverYouHave {
public STKUser getUser(HttpServletRequest request) {
return request.getSession().getAttribute("STKUserSession");
}
...
}
...
public class AnotherServlet extends BaseForm {
public TD0301Assignment updateAssignment(HttpServletRequest request) {
if (getUser(request).equals(tdas.getOriginator())) {
...
Another, potentially cleaner option depending on your dispatch/instantiation/etc. mechanism would be to inject the value into your forms (if they're not singletons, unclear):
public class AnotherServlet extends BaseForm {
public AnotherServlet(STKUser user) {
this.user = user;
...
}
public TD0301Assignment updateAssignment(HttpServletRequest request) {
if (user.equals(tdas.getOriginator())) {
...
Or provide it as an argument to form methods (if they are):
public TD0301Assignment updateAssignment(STKUser user, HttpServletRequest request) {
if (user.equals(tdas.getOriginator())) {
...
It's unfortunate your forms are tied directly to the servlet spec; it's more pleasant to do as much development as possible without that requirement.