GWT ScrollPanel for Touch Screens
Getting this working is easier than you think. Basically the only tricky bit is that the touch object allows for multiple fingers they are represented in the array ‘touches[]‘. Since we are only scrolling we only need to worry about the first finger which is why I use touches[0].screenY. Enjoy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | package com.peterfranza.gwt.mobileui.client.widgets; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.user.client.ui.ScrollPanel; /** * * @author peter.franza * */ public class TouchScrollPanel extends ScrollPanel { private int initialTouchX = -1; private int initialTouchY = -1; private int initialHorizontalOffset; private int initialVerticalOffset; private boolean moved = false; { attachTouch(getElement()); } public TouchScrollPanel(VerticalPanel body) { super(body); } private native void attachTouch(JavaScriptObject ele) /*-{ var ref = this; ele.ontouchstart = function(evt){ evt.preventDefault(); ref.@com.orci.datagateway.OpenIMWeb.mobile.client.widgets.TouchScrollPanel::setInitialTouch(II)(evt.touches[0].screenX, evt.touches[0].screenY); } ele.ontouchmove = function(evt){ evt.preventDefault(); ref.@com.orci.datagateway.OpenIMWeb.mobile.client.widgets.TouchScrollPanel::doScroll(II)(evt.touches[0].screenX, evt.touches[0].screenY); } ele.ontouchend = function(evt){ evt.preventDefault(); ref.@com.orci.datagateway.OpenIMWeb.mobile.client.widgets.TouchScrollPanel::setEndTouch(II)(evt.pageX, evt.pageY); } }-*/; private native void fireClick(int x, int y) /*-{ var theTarget = $doc.elementFromPoint(x, y); if (theTarget.nodeType == 3) theTarget = theTarget.parentNode; var theEvent = $doc.createEvent('MouseEvents'); theEvent.initEvent('click', true, true); theTarget.dispatchEvent(theEvent); }-*/; @SuppressWarnings("unused") private void setInitialTouch(int x, int y) { initialVerticalOffset = getScrollPosition(); initialHorizontalOffset = getHorizontalScrollPosition(); initialTouchX = x; initialTouchY = y; moved = false; } @SuppressWarnings("unused") private void doScroll(int x, int y) { if (initialTouchY != -1) { moved = true; int vDelta = initialTouchY - y; int hDelta = initialTouchX - x; setScrollPosition(vDelta + initialVerticalOffset); setHorizontalScrollPosition(hDelta + initialHorizontalOffset); } } @SuppressWarnings("unused") private void setEndTouch(int x, int y) { if (!moved) { fireClick(x, y); } initialTouchX = -1; initialTouchY = -1; } } |
GWT loading remote JSONP XSS
In almost all browsers there is one thing that everybody wants to do, but you just can’t, Load cross site data. That is you can’t unless you are using JSONP. Basically you load the data into its own script tag tell the server you are loading it from to wrap the data in a callback that you have defined. This will fire an event after the data has loaded. Perfect.
I wanted to use it in GWT … now I can.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package com.peterfranza.gwt.cle.client; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.json.client.JSONObject; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.RootPanel; public class JSONPLoader { private LoaderCallback callback; public void load(String jsonUrl, LoaderCallback callback) { this.callback = callback; String callbackString = "jsonLoad_" + DOM.createUniqueId().replace("-", "_") + "_callback"; String url = jsonUrl + (jsonUrl.contains("?") ? "&" : "?") + "_callback=" + callbackString; publishCallbackMethod(callbackString); Element fr1 = DOM.createElement("script"); fr1.setAttribute("src", url); RootPanel.get().getElement().appendChild(fr1); } private native void publishCallbackMethod(String callback) /*-{ var ptr = this; $wnd[callback] = function(obj) { ptr.@com.peterfranza.gwt.cle.client.JSONPLoader::loadremotedata(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(callback, obj); }; }-*/; @SuppressWarnings("unused") private void loadremotedata(String cbp, JavaScriptObject obj) { callback.onLoad(new JSONObject(obj)); } public interface LoaderCallback { void onLoad(JSONObject object); } } |
Usage:
1 2 3 4 5 6 7 8 | JSONPLoader loader = new JSONPLoader(); loader.load("http://pipes.yahoo.com/pipes/pipe.run?_id=CPreCvz42xG4eaOWouNLYQ&_render=json&location=madison§ion=mcy", new LoaderCallback() { @Override public void onLoad(JSONObject object) { Window.alert("loaded " + object.size()); } }); |
Notice that the json url does not specify the _callback parameter, this is because it is added by the JSONPLoader class.
Connecting Apache (httpd) To Active Directory
Recently I went through a small effort to connect a subversion repository to active directory. This is a good thing because it means that you no longer will need to manage the usernames and password using the old htpasswd format. The htpasswd is fine for very controlled environments but the passwords it allows you to use are pretty weak and the encryption of the passwords is fairly weak also, so allowing the connection to happen using active directory as the authority is a good thing. Also it keeps you from having to maintain more passwords, and I like that idea.
In your /etc/httpd/conf.d/filename.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | LoadModule dav_svn_module modules/mod_dav_svn.so LoadModule authz_svn_module modules/mod_authz_svn.so LDAPConnectionTimeout 15 LDAPSharedCacheSize 200000 LDAPCacheEntries 1024 LDAPCacheTTL 600 LDAPOpCacheEntries 1024 LDAPOpCacheTTL 600 <Location /server/cache-info> SetHandler ldap-status </Location> <Location /svn> DAV svn SVNPath /subversion/repos SVNListParentPath on AuthzSVNAccessFile /subversion/svnauthorz Satisfy Any AuthType Basic AuthName "Members Only" AuthzLDAPAuthoritative off AuthBasicProvider ldap AuthLDAPBindDN "svn.user@domain.com" AuthLDAPBindPassword "svn.user.password" AuthLDAPURL "ldap://<ldapserverip>/DC=domain,DC=com?sAMAccountName?sub?(objectClass=user)" Require valid-user </Location> |
However every couple of transactions I would get an error and this message would appear in my error log
/var/log/httpd/error_log
1 | [Fri Apr 30 09:21:46 2010] [warn] [client 192.168.100.105] [22578] auth_ldap authenticate: user peter.franza authentication failed; URI /svnad/projects/ [ldap_search_ext_s() for user failed][Operations error], referer: http://svn/svnad/projects/ |
The solution was to disable following referers
/etc/openldap/ldap.conf
1 | REFERRALS off |
*Note this is in /etc/openldap/ not the ldap.conf in /etc that file is used from pam authentication and not for mod_ldap
Non-blocking UDP datagram replicator
A class that listens to a UDP port and collects all the datagrams and then rebroadcasts those datagrams to other ports. This is useful for several reasons. I use it when stress testing UDP clients because I can subscribe to 1000 client sockets while only really having a single legitimate datasource.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | public class Replicator { private final AsyncDatagramServer aserver; public Replicator(final int port, final Collection<DataSinkPoint> endPoints) throws Exception { aserver = new AsyncDatagramServer(port, new AsyncDatagramServer.AsyncDatagramServerListener() { @Override public void recieveDatagram(ByteBuffer buffer) { try { for(final DataSinkPoint d: endPoints) { d.send(buffer.duplicate()); } } catch (final Exception e) { e.printStackTrace(); } } }); } private static class AsyncDatagramServer { private boolean running = true; public AsyncDatagramServer(final int port, final AsyncDatagramServerListener listener) throws Exception { new Thread(){ @Override public void run() { try { startSocket(port, listener); } catch (final Exception e) { throw new RuntimeException(e); } } }.start(); } public interface AsyncDatagramServerListener { void recieveDatagram(ByteBuffer buffer); } public void shutdown() { running = false; } private void startSocket(final int port, AsyncDatagramServerListener listener) throws IOException, SocketException, ClosedChannelException, Exception { DatagramChannel serverChannel = DatagramChannel.open(); Selector selector = Selector.open(); DatagramSocket sock = serverChannel.socket(); sock.setReuseAddress(true); sock.bind (new InetSocketAddress (port)); serverChannel.configureBlocking (false); serverChannel.register(selector, SelectionKey.OP_READ); ByteBuffer buffer = ByteBuffer.allocate(2048); while (running) { if(selector.select(500) > 0) { processData(listener, selector, buffer); } } selector.close(); serverChannel.close(); } private void processData(AsyncDatagramServerListener listener, Selector selector, ByteBuffer buffer) throws Exception { Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while (it.hasNext()) { final SelectionKey key = it.next(); if (key.isReadable()) { DatagramChannel channel = (DatagramChannel) key.channel(); buffer.clear(); if (channel.receive(buffer) != null) { buffer.flip(); listener.recieveDatagram(buffer.duplicate()); } } it.remove(); } } } public void shutdown() { aserver.shutdown(); } public interface DataSinkPoint { void send(ByteBuffer buffer) throws IOException; } private static class DataSinkPointImpl implements DataSinkPoint { private final DatagramSocket socket; public DataSinkPointImpl(SocketAddress address) throws Exception { socket = new DatagramSocket(); socket.connect(address); } @Override public void send(ByteBuffer buffer) throws IOException { socket.send(new DatagramPacket(buffer.array(), 0, buffer.limit())); } } } |
Usage:
1 2 3 4 5 6 7 8 9 10 11 12 | public static void main(String[] args) throws Exception { Collection<DataSinkPoint> endPoints = new ArrayList<DataSinkPoint>(); for(int i = 4000; i < 5000; i++) { endPoints.add(new DataSinkPointImpl( new InetSocketAddress("127.0.0.1", i))); } final Replicator r = new Replicator(3000, endPoints); Thread.sleep(5000); r.shutdown(); System.out.println("done."); } |
Announcing – “Announce” for Android
Announce is an application for Android 2.0 and above. It intercepts incoming calls and and reads the name of the caller aloud. You can also record custom voice tags to use instead of the text to speech engine.
Project Page: http://www.peterfranza.com/projects/announce-for-android/
Price: Free (That’s a good value)
Enjoy.


