CleverGWT
From PlcWiki
m |
|||
(76 intermediate revisions not shown) | |||
Line 1: | Line 1: | ||
+ | == Installation Steps == | ||
+ | |||
+ | * Repository: ssh://user@oko.clever.cz/home/git/CleverGWT/ | ||
+ | * Path: /java/CleverGWT | ||
+ | * Entry-point descriptor ...gwt.xml: | ||
+ | |||
+ | <xml> | ||
+ | <inherits name="cz.clever.gwt.ext.Module" /> | ||
+ | </xml> | ||
+ | |||
+ | * Don't forget to check your current version of the GWT | ||
+ | |||
+ | == Resources == | ||
+ | |||
+ | Resources are located in folder '''src/cz/clever/gwt/ext/public'''. | ||
+ | |||
+ | Example of icon usage: | ||
+ | |||
+ | <java> | ||
+ | logoutBtn.setIcon(Resource.Util.getIconPath("door_out")); //$NON-NLS-1$ | ||
+ | </java> | ||
+ | |||
+ | == Unified look of the Clever System applications == | ||
+ | |||
+ | === Establishing a project === | ||
+ | |||
+ | Choose: '''Google/Web Application Project'''. Package: '''cz.clever.nazevaplikace.gwt'''. GWT 2.4.0 | ||
+ | |||
+ | === Java Build Path === | ||
+ | |||
+ | Projects - CleverGWT | ||
+ | |||
+ | Libraries - CleverGWT/lib/smartgwt-skins.jar, CleverGWT/lib/smartgwt.jar, CleverGWT/lib/log4j.jar | ||
+ | |||
+ | === AppEntryPoint === | ||
+ | |||
+ | AppEntryPoint is an implementation of the '''EntryPoint''' interface. | ||
+ | |||
+ | Invent an application name ''getAppName()'' a implementaci: | ||
+ | |||
+ | <java> | ||
+ | public void drawContent(String initData, DataCallback<com.smartgwt.client.widgets.Canvas> callback) { callback.execute(new Canvas()); } | ||
+ | </java> | ||
+ | |||
+ | <java> | ||
+ | public void logout() { | ||
+ | CallbackStatus status = null; | ||
+ | ServiceCallback<Void> callback = new ServiceCallback<Void>(status, "Logout process") { | ||
+ | @Override | ||
+ | public void onSuccesss(Void result) { | ||
+ | Window.Location.reload(); | ||
+ | } | ||
+ | }; | ||
+ | Service.Util.get().logout(callback); | ||
+ | } | ||
+ | </java> | ||
+ | |||
+ | === Interface Service a ServiceAsync === | ||
+ | |||
+ | <java> | ||
+ | @RemoteServiceRelativePath("service") | ||
+ | public interface Service extends RemoteService, ServiceCore { } | ||
+ | </java> | ||
+ | |||
+ | === Server side - ServiceImpl === | ||
+ | |||
+ | <java> | ||
+ | public class ServiceImpl extends SecuredServiceServlet implements Service { | ||
+ | |||
+ | @Override | ||
+ | protected String getAppName() { | ||
+ | return "myAppName";// security role-name | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | protected KeyValueString getUserName() { | ||
+ | return new KeyValueString() { | ||
+ | |||
+ | @Override | ||
+ | public String getKey() {return "user-data-key";} | ||
+ | |||
+ | @Override | ||
+ | public String getValue() {return (String)getUserData(); /* Default:String */} | ||
+ | |||
+ | @Override | ||
+ | public void setValue(String value) {} | ||
+ | |||
+ | }; | ||
+ | } | ||
+ | } | ||
+ | </java> | ||
+ | |||
+ | === web.xml === | ||
+ | |||
+ | <xml> | ||
+ | <servlet> | ||
+ | <servlet-name>serviceServlet</servlet-name> | ||
+ | <servlet-class>cz.clever.myapp.gwt.server.ServiceImpl</servlet-class> | ||
+ | </servlet> | ||
+ | |||
+ | <servlet-mapping> | ||
+ | <servlet-name>serviceServlet</servlet-name> | ||
+ | <url-pattern>/myapp/service</url-pattern> | ||
+ | </servlet-mapping> | ||
+ | </xml> | ||
+ | |||
+ | === myapp.gwt.xml === | ||
+ | |||
+ | Fill in correctly a Other module inherits: | ||
+ | |||
+ | <xml> | ||
+ | <inherits name="com.google.gwt.i18n.I18N" /> | ||
+ | <inherits name="com.google.gwt.xml.XML" /> | ||
+ | <inherits name="com.google.gwt.http.HTTP" /> | ||
+ | <inherits name="com.smartgwt.SmartGwt" /> | ||
+ | <inherits name="cz.clever.gwt.ext.Module" /> | ||
+ | </xml> | ||
+ | |||
+ | === log4j.properties === | ||
+ | |||
+ | Copy to src. | ||
+ | |||
+ | === Splash - landing page to the application === | ||
+ | |||
+ | Known bug: if this page is not dynamic (jsp), then the server sends 304 (not modified) during authentication and this causes Firefox to not load the page. | ||
+ | |||
+ | It will be added to the input html page: | ||
+ | |||
+ | <xml> | ||
+ | <meta http-equiv="content-type" content="text/html; charset=UTF-8"> | ||
+ | <META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE"> | ||
+ | <META NAME="AUTHOR" CONTENT="CLEVER Soft s.r.o."> | ||
+ | <META name="gwt:property" content="locale=en"> | ||
+ | </xml> | ||
+ | |||
+ | <xml> | ||
+ | <body style="background-color: #445566"> | ||
+ | </xml> | ||
+ | |||
+ | <xml> | ||
+ | <div style="border: 1px solid #7FBA00; position: absolute; left: 45%; top: 40%; padding: 2px; z-index: 20001; height: auto;"> | ||
+ | <div style="background: white; font: bold 16px tahoma, arial, helvetica; padding: 10px; margin: 0; height: 34px; color: #444;"> | ||
+ | <img src="myapp/images/loading32.gif" width="32" height="32" style="margin-right:8px; float:left; vertical-align:top;"/> | ||
+ | clever system<br/><span style="font: normal 10px tahoma, arial, helvetica;">My App Title</span> | ||
+ | </div></div> | ||
+ | </xml> | ||
+ | |||
+ | === app.json === | ||
+ | |||
+ | web.xml: | ||
+ | |||
+ | <xml> | ||
+ | <context-param> | ||
+ | <param-name>appname_</param-name> | ||
+ | <param-value><![CDATA[App Name]]></param-value> | ||
+ | </context-param> | ||
+ | </xml> | ||
+ | |||
+ | <xml> | ||
+ | <servlet> | ||
+ | <servlet-name>appInfo</servlet-name> | ||
+ | <servlet-class>cz.clever.gwt.ext.server.AppInfoServlet</servlet-class> | ||
+ | </servlet> | ||
+ | |||
+ | <servlet-mapping> | ||
+ | <servlet-name>appInfo</servlet-name> | ||
+ | <url-pattern>/app.json</url-pattern> | ||
+ | </servlet-mapping> | ||
+ | </xml> | ||
+ | |||
+ | === Security - web.xml === | ||
+ | |||
+ | <xml> | ||
+ | <security-constraint> | ||
+ | <web-resource-collection> | ||
+ | <web-resource-name>WebResName</web-resource-name> | ||
+ | <description></description> | ||
+ | <url-pattern>/*</url-pattern> | ||
+ | <http-method>GET</http-method> | ||
+ | <http-method>POST</http-method> | ||
+ | </web-resource-collection> | ||
+ | <auth-constraint> | ||
+ | <role-name>MyApp</role-name> | ||
+ | </auth-constraint> | ||
+ | </security-constraint> | ||
+ | |||
+ | <security-constraint> | ||
+ | <web-resource-collection> | ||
+ | <web-resource-name>Public</web-resource-name> | ||
+ | <url-pattern>/app.json</url-pattern> | ||
+ | </web-resource-collection> | ||
+ | </security-constraint> | ||
+ | |||
+ | <login-config> | ||
+ | <auth-method>FORM</auth-method> | ||
+ | <form-login-config> | ||
+ | <form-login-page>/Login.html</form-login-page> | ||
+ | <form-error-page>/Login.html?failed</form-error-page> | ||
+ | </form-login-config> | ||
+ | </login-config> | ||
+ | |||
+ | <security-role> | ||
+ | <role-name>MyApp</role-name> | ||
+ | </security-role> | ||
+ | </xml> | ||
+ | |||
+ | === Sounds and pictures === | ||
+ | |||
+ | Sounds and icons. Images and icons are in CleverGWT/src/cz/clever/gwt/ext/public/images. | ||
+ | |||
+ | The availability of resources is only after execution | ||
+ | '''Google/GWT Compile'''. | ||
+ | |||
+ | Example of how to play audio (required audio tag in html): | ||
+ | |||
+ | <java> | ||
+ | public static native void playAudio(String audioTagId, double volume) /*-{ | ||
+ | $doc.getElementById(audioTagId).volume = volume; | ||
+ | $doc.getElementById(audioTagId).play(); | ||
+ | }-*/; | ||
+ | </java> | ||
+ | |||
+ | Example of an html tag in the landing page (sounds are not physically part of the application where they are listed, but CleverGWT application): | ||
+ | |||
+ | <xml> | ||
+ | <audio id="select" autobuffer="autobuffer" preload="auto"> | ||
+ | <source src="myapp/sound/select.ogg" type="audio/ogg"> | ||
+ | <source src="myapp/sound/select.mp3" type="audio/mpeg"> | ||
+ | </audio> | ||
+ | </xml> | ||
+ | |||
== ClvListGrid - Extension of ListGrid == | == ClvListGrid - Extension of ListGrid == | ||
Line 4: | Line 235: | ||
<java> | <java> | ||
- | + | ListGrid grid = new ClvListGrid(new ClvListGrid.Delegate() { | |
@Override | @Override | ||
Line 29: | Line 260: | ||
Note: If you are implementing executeRemove/Add/Fetch you must call response.response(...). Not in executeUpdate. | Note: If you are implementing executeRemove/Add/Fetch you must call response.response(...). Not in executeUpdate. | ||
+ | |||
+ | Real-time data synchronization (only works with URL parameter "im"): | ||
+ | |||
+ | <java> | ||
+ | grid = new ClvListGrid(delegate, "MySubscribeId"); | ||
+ | </java> | ||
== SecuredServiceServlet == | == SecuredServiceServlet == | ||
+ | |||
+ | Benefits: | ||
+ | |||
+ | * Request without GWT permutation header will be blocked (potential CSRF attack) | ||
+ | * Authorization check before the servlet methods are called from client (methods must be annotated as Secured) | ||
<java> | <java> | ||
- | public class | + | public class MyGWTServiceImpl extends SecuredServiceServlet implements ... |
+ | |||
+ | @Override | ||
+ | protected boolean isInRole(String user, String method, Object value, Integer position) { | ||
+ | // check for permission | ||
+ | } | ||
</java> | </java> | ||
+ | |||
+ | == JettyLauncher == | ||
+ | |||
+ | Allow you to set custom logging and configuration. | ||
+ | |||
+ | Example of startup arguments: | ||
+ | |||
+ | -startupUrl Entrypoint.html ... -server cz.clever.gwt.ext.server.JettyLauncher ... | ||
+ | |||
+ | Inside a class: | ||
+ | |||
+ | <java> | ||
+ | wac.setConfigurationClasses(new String[] { | ||
+ | "org.mortbay.jetty.webapp.WebInfConfiguration", | ||
+ | "org.mortbay.jetty.plus.webapp.EnvConfiguration",//jetty-env | ||
+ | "org.mortbay.jetty.plus.webapp.Configuration", //web.xml | ||
+ | "org.mortbay.jetty.webapp.JettyWebXmlConfiguration",//jettyWeb | ||
+ | }); | ||
+ | </java> | ||
+ | |||
+ | == DocumentServlet and UploadServlet == | ||
+ | |||
+ | HttpServlet solution for uploading and downloading documents. | ||
+ | |||
+ | Example of upload servlet configuration: | ||
+ | |||
+ | <xml> | ||
+ | <servlet> | ||
+ | <servlet-name>MaterialUpload</servlet-name> | ||
+ | <servlet-class>cz.clever.cct.gwt.app.server.MyUploadServlet</servlet-class> | ||
+ | <init-param> | ||
+ | <param-name>repository</param-name> | ||
+ | <param-value>/tmp</param-value> | ||
+ | </init-param> | ||
+ | <init-param> | ||
+ | <param-name>maxMemSize</param-name> | ||
+ | <param-value>2147483646</param-value> | ||
+ | </init-param> | ||
+ | <init-param> | ||
+ | <param-name>maxFileSize</param-name> | ||
+ | <param-value>2147483646</param-value> | ||
+ | </init-param> | ||
+ | </servlet> | ||
+ | </xml> | ||
== StringUtil == | == StringUtil == | ||
Line 50: | Line 341: | ||
</java> | </java> | ||
- | Way how to build delimiter separated string from collection: | + | Way how to build a delimiter separated string from collection: |
<java> | <java> | ||
Line 93: | Line 384: | ||
String hashed = BCrypt.hashpw("password", BCrypt.gensalt()); | String hashed = BCrypt.hashpw("password", BCrypt.gensalt()); | ||
boolean verified = BCrypt.checkpw("password", hashed); | boolean verified = BCrypt.checkpw("password", hashed); | ||
+ | </java> | ||
+ | |||
+ | == String Externalizer (i18n) == | ||
+ | |||
+ | Ant external task declaration example: | ||
+ | |||
+ | <xml> | ||
+ | <target name="externalizer"> | ||
+ | <taskdef name="externalizer" classname="cz.clever.gwt.ext.ant.Externalizer"/> | ||
+ | <externalizer | ||
+ | substitution="ClientConst.Util.get().%label()" | ||
+ | labelMaxLength="20" | ||
+ | labelPrefix="lbl" | ||
+ | fileConst="src/cz/clever/cct/gwt/app/client/ClientConst.java"> | ||
+ | |||
+ | <fileset dir="src/cz/clever/cct/gwt/app/client"> | ||
+ | <include name="*.java" /> | ||
+ | <exclude name="ClientConst.java"/> | ||
+ | </fileset> | ||
+ | </externalizer> | ||
+ | </target> | ||
+ | </xml> | ||
+ | |||
+ | ClientConst.java (com.google.gwt.i18n.client.Constants) output example: | ||
+ | |||
+ | <java> | ||
+ | @DefaultStringValue("Devices") | ||
+ | String lblDEVICES(); | ||
</java> | </java> |
Current revision as of 14:38, 29 October 2020
Contents |
Installation Steps
- Repository: ssh://user@oko.clever.cz/home/git/CleverGWT/
- Path: /java/CleverGWT
- Entry-point descriptor ...gwt.xml:
<inherits name="cz.clever.gwt.ext.Module" />
- Don't forget to check your current version of the GWT
Resources
Resources are located in folder src/cz/clever/gwt/ext/public.
Example of icon usage:
logoutBtn.setIcon(Resource.Util.getIconPath("door_out")); //$NON-NLS-1$
Unified look of the Clever System applications
Establishing a project
Choose: Google/Web Application Project. Package: cz.clever.nazevaplikace.gwt. GWT 2.4.0
Java Build Path
Projects - CleverGWT
Libraries - CleverGWT/lib/smartgwt-skins.jar, CleverGWT/lib/smartgwt.jar, CleverGWT/lib/log4j.jar
AppEntryPoint
AppEntryPoint is an implementation of the EntryPoint interface.
Invent an application name getAppName() a implementaci:
public void drawContent(String initData, DataCallback<com.smartgwt.client.widgets.Canvas> callback) { callback.execute(new Canvas()); }
public void logout() { CallbackStatus status = null; ServiceCallback<Void> callback = new ServiceCallback<Void>(status, "Logout process") { @Override public void onSuccesss(Void result) { Window.Location.reload(); } }; Service.Util.get().logout(callback); }
Interface Service a ServiceAsync
@RemoteServiceRelativePath("service") public interface Service extends RemoteService, ServiceCore { }
Server side - ServiceImpl
public class ServiceImpl extends SecuredServiceServlet implements Service { @Override protected String getAppName() { return "myAppName";// security role-name } @Override protected KeyValueString getUserName() { return new KeyValueString() { @Override public String getKey() {return "user-data-key";} @Override public String getValue() {return (String)getUserData(); /* Default:String */} @Override public void setValue(String value) {} }; } }
web.xml
<servlet> <servlet-name>serviceServlet</servlet-name> <servlet-class>cz.clever.myapp.gwt.server.ServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>serviceServlet</servlet-name> <url-pattern>/myapp/service</url-pattern> </servlet-mapping>
myapp.gwt.xml
Fill in correctly a Other module inherits:
<inherits name="com.google.gwt.i18n.I18N" /> <inherits name="com.google.gwt.xml.XML" /> <inherits name="com.google.gwt.http.HTTP" /> <inherits name="com.smartgwt.SmartGwt" /> <inherits name="cz.clever.gwt.ext.Module" />
log4j.properties
Copy to src.
Splash - landing page to the application
Known bug: if this page is not dynamic (jsp), then the server sends 304 (not modified) during authentication and this causes Firefox to not load the page.
It will be added to the input html page:
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> <META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE"> <META NAME="AUTHOR" CONTENT="CLEVER Soft s.r.o."> <META name="gwt:property" content="locale=en">
<body style="background-color: #445566">
<div style="border: 1px solid #7FBA00; position: absolute; left: 45%; top: 40%; padding: 2px; z-index: 20001; height: auto;"> <div style="background: white; font: bold 16px tahoma, arial, helvetica; padding: 10px; margin: 0; height: 34px; color: #444;"> <img src="myapp/images/loading32.gif" width="32" height="32" style="margin-right:8px; float:left; vertical-align:top;"/> clever system<br/><span style="font: normal 10px tahoma, arial, helvetica;">My App Title</span> </div></div>
app.json
web.xml:
<context-param> <param-name>appname_</param-name> <param-value><![CDATA[App Name]]></param-value> </context-param>
<servlet> <servlet-name>appInfo</servlet-name> <servlet-class>cz.clever.gwt.ext.server.AppInfoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>appInfo</servlet-name> <url-pattern>/app.json</url-pattern> </servlet-mapping>
Security - web.xml
<security-constraint> <web-resource-collection> <web-resource-name>WebResName</web-resource-name> <description></description> <url-pattern>/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>MyApp</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>Public</web-resource-name> <url-pattern>/app.json</url-pattern> </web-resource-collection> </security-constraint> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/Login.html</form-login-page> <form-error-page>/Login.html?failed</form-error-page> </form-login-config> </login-config> <security-role> <role-name>MyApp</role-name> </security-role>
Sounds and pictures
Sounds and icons. Images and icons are in CleverGWT/src/cz/clever/gwt/ext/public/images.
The availability of resources is only after execution
Google/GWT Compile.
Example of how to play audio (required audio tag in html):
public static native void playAudio(String audioTagId, double volume) /*-{ $doc.getElementById(audioTagId).volume = volume; $doc.getElementById(audioTagId).play(); }-*/;
Example of an html tag in the landing page (sounds are not physically part of the application where they are listed, but CleverGWT application):
<audio id="select" autobuffer="autobuffer" preload="auto"> <source src="myapp/sound/select.ogg" type="audio/ogg"> <source src="myapp/sound/select.mp3" type="audio/mpeg"> </audio>
ClvListGrid - Extension of ListGrid
Simplification of CRUD usage:
ListGrid grid = new ClvListGrid(new ClvListGrid.Delegate() { @Override public void executeUpdate(ClvListGridResponse response) { // Map<String, Object> reqMap = response.getRequestMap(); // response.response(... } @Override public void executeRemove(ClvListGridResponse response) {} @Override public void executeFetch(ClvListGridResponse response) {} @Override public void executeAdd(ClvListGridResponse response) {} @Override public DataSourceField[] createFields() { return new DataSourceField[] { new DataSourceTextField("Field Name") }; } });
Note: If you are implementing executeRemove/Add/Fetch you must call response.response(...). Not in executeUpdate.
Real-time data synchronization (only works with URL parameter "im"):
grid = new ClvListGrid(delegate, "MySubscribeId");
SecuredServiceServlet
Benefits:
- Request without GWT permutation header will be blocked (potential CSRF attack)
- Authorization check before the servlet methods are called from client (methods must be annotated as Secured)
public class MyGWTServiceImpl extends SecuredServiceServlet implements ... @Override protected boolean isInRole(String user, String method, Object value, Integer position) { // check for permission }
JettyLauncher
Allow you to set custom logging and configuration.
Example of startup arguments:
-startupUrl Entrypoint.html ... -server cz.clever.gwt.ext.server.JettyLauncher ...
Inside a class:
wac.setConfigurationClasses(new String[] { "org.mortbay.jetty.webapp.WebInfConfiguration", "org.mortbay.jetty.plus.webapp.EnvConfiguration",//jetty-env "org.mortbay.jetty.plus.webapp.Configuration", //web.xml "org.mortbay.jetty.webapp.JettyWebXmlConfiguration",//jettyWeb });
DocumentServlet and UploadServlet
HttpServlet solution for uploading and downloading documents.
Example of upload servlet configuration:
<servlet> <servlet-name>MaterialUpload</servlet-name> <servlet-class>cz.clever.cct.gwt.app.server.MyUploadServlet</servlet-class> <init-param> <param-name>repository</param-name> <param-value>/tmp</param-value> </init-param> <init-param> <param-name>maxMemSize</param-name> <param-value>2147483646</param-value> </init-param> <init-param> <param-name>maxFileSize</param-name> <param-value>2147483646</param-value> </init-param> </servlet>
StringUtil
java.util.StringTokenizer is prohibited in the GWT client side. Here is my substitution:
StringUtil.tokenizer("Jirka, Petr, Honza", ",", new ValueCallback() { @Override public void execute(String token) { // Your code } });
Way how to build a delimiter separated string from collection:
ToString<MaterialOption> toStr = new ToString<MaterialOption>() { @Override public String get(MaterialOption obj) { //make a token from object return obj.toString(); // or if you want: return obj == null? null:obj.getOptionString(); } }; // collection with filling of course Collection<MaterialOption> myCollection = new ArrayList<MaterialOption>(); String result = StringUtil.toString(myCollection, ", ", toStr);
ParserCSV
new ParserCSV(new ParserCSV.Delegate() { @Override public void token(String token, int position) { // working with parsed tokens } @Override public String getPath() { return "MyTable.csv"; } @Override public String getDelimeter() { return ";";} @Override public String getCharsetName() { return "UTF-16"; } });
BCrypt
String hashed = BCrypt.hashpw("password", BCrypt.gensalt()); boolean verified = BCrypt.checkpw("password", hashed);
String Externalizer (i18n)
Ant external task declaration example:
<target name="externalizer"> <taskdef name="externalizer" classname="cz.clever.gwt.ext.ant.Externalizer"/> <externalizer substitution="ClientConst.Util.get().%label()" labelMaxLength="20" labelPrefix="lbl" fileConst="src/cz/clever/cct/gwt/app/client/ClientConst.java"> <fileset dir="src/cz/clever/cct/gwt/app/client"> <include name="*.java" /> <exclude name="ClientConst.java"/> </fileset> </externalizer> </target>
ClientConst.java (com.google.gwt.i18n.client.Constants) output example:
@DefaultStringValue("Devices") String lblDEVICES();