In the previous articles on http://surgeworksmobile.com/cross-platform/jqtouch-mobile-app-for-user-registration-tutorial, I focused on the JQTouch side of things. I’d like to mention the ingredients to make the app work with SmartFoxServer since that can be tricky.
I built both grails and jython web services to serve as the back end interface for this application. Unfortunately, both these implementations would not work with SFS, so I made a very simple JSP servlet to handle registration requests.
First, the structure of the app. Do not provide any jar files. They need to be provided by SFS:
in WEB-INF/web.xml:
<web-app>
<servlet>
<servlet-name>AppServlet</servlet-name>
<servlet-class>AppServlet</servlet-class>
</servlet>
</web-app>
sneakapp.xml, in the top directory, we document where the servlet will appear in the web site’s hierarchy.
< ?xml version=”1.0″ encoding=”ISO-8859-1″?>
< !DOCTYPE Configure PUBLIC “-//Mort Bay Consulting//DTD Configure//EN” “http://jetty.mortbay.org/configure.dtd”>
<configure>
<set name=”contextPath”>/sneakapp</set>
<set name=”war”><systemproperty name=”jetty.home” default=”.”/>/webapps/sneakapp.war</set>
</configure>
in images/ are the .png files refered by index.html.
css/ contains the jqtouch css support files.
js/jqtouch contains the jqtouch javascript files.
To deploy, the app, build a war, place it, and restart SFS:
javac WEB-INF/classes/AppServlet.java
jar cvf /opt/SFS_PRO_1.6.6/Server/webserver/webapps/sneakapp.war .
/etc/init.d/sfs stop ; /etc/init.d/sfs start
Finally, the servlet code in WEB-INF/classes/AppServlet.java. A few points to note.
- Output is formatted for use by JQTouch, with a place in the warning string to place error messages
- The SFS hook is handled in handleInternalRequest. This call accepts a list of strings and returns a string
- The servlet performs basic validation; all validation happens at the server
import java.security.*; import java.math.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.json.*; import it.gotoandplay.smartfoxserver.*; public class AppServlet extends HttpServlet { private static SmartFoxServer sfs; private static String success; private static String failure; private static String warning; private static BufferedWriter writer = null; public void init(ServletConfig config) throws ServletException { sfs = SmartFoxServer.getInstance(); success = "<div id="confirm"> <div class="toolbar"> <h1>Thank you</h1> <a href="#home" class="back">Home</a> </div> <center> Thank you for signing up! <p>You will receive an email soon with instructions on how to activate your account. <p>Please also check your Spam folder! </center> </div>"; failure = "<div id="fail"> <div class="toolbar"> <a href="#register" class="back">Try again</a> </div> <center> <div class="message">This username or email are in use already. Please try again and complete all required fields.</div> </center> </div>"; warning = "<div id="fail"> <div class="toolbar"> <a href="#register" class="back">Try again</a> </div> <center> <div class="message">%s Please try again.</div> </center> </div>"; try { writer = new BufferedWriter(new FileWriter("/var/www/out.txt",true)); } catch(Exception e) {} } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String hash = ""; String emailhash = ""; String key = ""; String result = ""; try { hash = mymd5(request.getParameter("password")); emailhash = mymd5(request.getParameter("email")); key = mymd5("token" + Math.random()); } catch(Exception e) {} String params[] = new String[7]; params[0] = request.getParameter("username"); params[1] = hash; params[2] = request.getParameter("first_name"); params[3] = request.getParameter("last_name"); params[4] = request.getParameter("email"); params[5] = emailhash; params[6] = key; PrintWriter out = response.getWriter(); try { String msg = ""; msg += verify(request, "first_name", "First name"); msg += verify(request, "last_name", "Last name"); msg += verify(request, "email", "email"); msg += verify(request, "username", "Username"); msg += verify(request, "password", "password"); if(!msg.equals("")) { out.format(warning, msg); return; } if(writer != null) { for(int i=0; i < 7; i++) writer.write(params[i] + " "); writer.write(request.getParameter("password")); writer.write("n"); writer.flush(); } result = (String) sfs.getZone("SOTL").getExtension("sotl_db").handleInternalRequest(params); if(result.equals("")) out.println(success); else out.format(warning, result); } catch(Exception e) { out.format(warning, e.toString()); } } public String verify(HttpServletRequest request, String parameter, String name) { if(request.getParameter(parameter) == null || request.getParameter(parameter).equals("")) { return name + " is required."; } return ""; } public static String mymd5(String pass) throws NoSuchAlgorithmException { MessageDigest m = MessageDigest.getInstance("MD5"); m.reset(); m.update(pass.getBytes()); byte[] digest = m.digest(); BigInteger bigInt = new BigInteger(1,digest); String hashtext = bigInt.toString(16); // Now we need to zero pad it if you actually want the full 32 chars. while(hashtext.length() < 32 ){ hashtext = "0"+hashtext; } return hashtext; } }
Again, the index.html from the root of the java project ties everything together, pulls in JQTouch, and connects the form to the servlet:
<!doctype html> <html> <head> <meta charset="UTF-8" /> <title>Sneak on the Lot</title> <style type="text/css" media="screen">@import "js/jqtouch/jqtouch.min.css";</style> <style type="text/css" media="screen">@import "css/themes/jqt/theme.min.css";</style> <script src="js/jqtouch/jquery.1.3.2.min.js" type="text/javascript" charset="utf-8"></script> <script src="js/jqtouch/jqtouch.min.js" type="application/x-javascript" charset="utf-8"></script> <script type="text/javascript" charset="utf-8"> var jQT = new $.jQTouch({ icon: 'images/icon.png', addGlossToIcon: false, startupScreen: 'images/splash.jpg', statusBar: 'black', preloadImages: [ 'css/themes/jqt/img/back_button.png', 'css/themes/jqt/img/back_button_clicked.png', 'css/themes/jqt/img/button_clicked.png', 'css/themes/jqt/img/grayButton.png', 'css/themes/jqt/img/whiteButton.png', 'css/themes/jqt/img/loading.gif' ] }); </script> <style type="text/css" media="screen"> #home { background-image: url(images/main.jpg); background-repeat: no-repeat; } a.splashlink { position: absolute; display: block; padding: none; margin: none; text-shadow: none; background-color: transparent; border-bottom: none; border-top: none; } .register { width: 140px; height: 40px; top: 156px; left: 89px; } .video { width: 72px; height: 63px; top: 284px; left: 17px; } ul.nostyle { -webkit-background-clip: none; background-image: none; background-color: none; color: none; border-bottom-width: 0; border-left-width: 0; border-right-width: 0; border-top-width: 0; } li.nostyle { -webkit-background-clip: none; background-image: none; background-color: none; color: none; border-bottom-width: 0; border-left-width: 0; border-right-width: 0; border-top-width: 0; } </style> </head> <body> <div id="home"> <ul> <li><a href="#register"></a></li> <li><a href="http://www.youtube.com/watch?v=VXB4LqW6AYI" rel="external"></a> </ul> </div> <form id="register" action="AppServlet" method="POST"> <div> <h1>Register</h1> <a href="#">Back</a> <a href="#">Sign up</a> </div> <ul> <li><input type="text" name="first_name" placeholder="First Name" id="some_name" /></li> <li><input type="text" name="last_name" placeholder="Last Name" id="some_name" /></li> <li><input type="text" name="email" placeholder="Email" id="some_name" autocapitalize=off /></li > <li><input type="text" name="username" placeholder="Username by which you'll be known" id="some_ name" autocapitalize=off /></li> <li><input type="password" name="password" placeholder="password" id="some_name" /></li> </ul> </form> </body> </html>