├── .gitattributes ├── .gitmodules ├── src ├── main │ ├── resources │ │ ├── channel.conf │ │ ├── mod.json │ │ └── com │ │ │ ├── goodow │ │ │ └── realtime │ │ │ │ ├── Platform.gwt.xml │ │ │ │ └── channel │ │ │ │ ├── ChannelProd.gwt.xml │ │ │ │ └── Channel.gwt.xml │ │ │ └── google │ │ │ └── gwt │ │ │ └── core │ │ │ └── linker │ │ │ └── SingleScriptTemplate.js │ ├── java │ │ └── com │ │ │ ├── goodow │ │ │ └── realtime │ │ │ │ ├── core │ │ │ │ ├── Storage.java │ │ │ │ ├── Net.java │ │ │ │ ├── AsyncResultHandler.java │ │ │ │ ├── Handler.java │ │ │ │ ├── PlatformFactory.java │ │ │ │ ├── VoidHandler.java │ │ │ │ ├── AsyncResult.java │ │ │ │ ├── Future.java │ │ │ │ ├── Diff.java │ │ │ │ ├── Registration.java │ │ │ │ ├── WebSocket.java │ │ │ │ ├── Scheduler.java │ │ │ │ ├── Platform.java │ │ │ │ ├── Registrations.java │ │ │ │ └── impl │ │ │ │ │ └── FutureResultImpl.java │ │ │ │ ├── channel │ │ │ │ ├── package-info.java │ │ │ │ ├── MessageHandler.java │ │ │ │ ├── State.java │ │ │ │ ├── ReplyFailure.java │ │ │ │ ├── mqtt │ │ │ │ │ └── packet │ │ │ │ │ │ └── MqttPacket.java │ │ │ │ ├── ReplyException.java │ │ │ │ ├── util │ │ │ │ │ ├── IdGenerator.java │ │ │ │ │ └── FuzzingBackOffGenerator.java │ │ │ │ ├── server │ │ │ │ │ ├── ChannelVerticle.java │ │ │ │ │ ├── impl │ │ │ │ │ │ ├── JavaDiff.java │ │ │ │ │ │ ├── VertxPlatform.java │ │ │ │ │ │ ├── VertxScheduler.java │ │ │ │ │ │ ├── VertxMessage.java │ │ │ │ │ │ ├── BridgeHook.java │ │ │ │ │ │ ├── VertxWebSocket.java │ │ │ │ │ │ └── VertxBus.java │ │ │ │ │ └── ChannelBridge.java │ │ │ │ ├── impl │ │ │ │ │ ├── BusHookProxy.java │ │ │ │ │ ├── MessageImpl.java │ │ │ │ │ ├── BusProxy.java │ │ │ │ │ ├── ReconnectBus.java │ │ │ │ │ ├── WebSocketBus.java │ │ │ │ │ ├── SimpleBus.java │ │ │ │ │ └── ReliableSubscribeBus.java │ │ │ │ ├── Message.java │ │ │ │ ├── BusHook.java │ │ │ │ └── Bus.java │ │ │ │ ├── html │ │ │ │ ├── ChannelEntryPoint.java │ │ │ │ ├── HtmlNet.java │ │ │ │ ├── HtmlPlatform.java │ │ │ │ ├── HtmlDiff.java │ │ │ │ ├── SockJS.java │ │ │ │ └── HtmlScheduler.java │ │ │ │ └── objc │ │ │ │ ├── ObjCNet.java │ │ │ │ ├── ObjCDiff.java │ │ │ │ ├── ObjCWebSocket.java │ │ │ │ ├── ObjCPlatform.java │ │ │ │ └── ObjCScheduler.java │ │ │ └── google │ │ │ └── gwt │ │ │ └── core │ │ │ └── client │ │ │ └── js │ │ │ ├── JsNoExport.java │ │ │ ├── JsExport.java │ │ │ ├── JsNamespace.java │ │ │ ├── JsProperty.java │ │ │ └── JsType.java │ └── assembly │ │ └── mod.xml └── test │ └── java │ └── com │ └── goodow │ └── realtime │ └── channel │ ├── server │ └── VertxBusTest.java │ └── WebSocketBusTest.java ├── vertx_classpath.txt ├── run-server.sh ├── .gitignore ├── README.md ├── .travis.yml ├── gwt-compile.sh ├── Makefile ├── pom.xml └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "GDChannel"] 2 | path = GDChannel 3 | url = https://github.com/goodow/GDChannel.git 4 | [submodule "bower-realtime-channel"] 5 | path = bower-realtime-channel 6 | url = https://github.com/goodow/bower-realtime-channel.git -------------------------------------------------------------------------------- /src/main/resources/channel.conf: -------------------------------------------------------------------------------- 1 | { 2 | "port": 1986, 3 | "host": "0.0.0.0", 4 | 5 | "inbound_permitted": [{}], 6 | "outbound_permitted": [{}], 7 | 8 | "sjs_config": { 9 | "prefix": "/channel" 10 | }, 11 | 12 | "bridge_config": { 13 | "ping_interval": 10000 14 | } 15 | } -------------------------------------------------------------------------------- /vertx_classpath.txt: -------------------------------------------------------------------------------- 1 | # This file contains information on where to find the resources of your module during development 2 | # This file is used when running your module as you develop - it tells Vert.x where to find 3 | # the resources of your module 4 | 5 | # Feel free to edit it if you have a non standard project structure and put the resources of your 6 | # module elsewhere 7 | 8 | src/main/resources 9 | target/classes 10 | target/dependencies 11 | bin -------------------------------------------------------------------------------- /run-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## Default configuration: https://github.com/goodow/realtime-channel/blob/master/src/main/resources/channel.conf 4 | 5 | 6 | ## Option 1: Build from source and run with Maven 7 | git clone https://github.com/goodow/realtime-channel.git 8 | cd realtime-channel 9 | mvn clean package vertx:runMod 10 | 11 | 12 | ## Option 2: Run with pre-installed Vert.x 13 | # wget https://raw.githubusercontent.com/goodow/realtime-channel/master/src/main/resources/channel.conf 14 | # vertx runmod com.goodow.realtime~realtime-channel~0.5.5-SNAPSHOT -conf channel.conf -------------------------------------------------------------------------------- /src/main/resources/mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "com.goodow.realtime.channel.server.ChannelVerticle", 3 | "auto-redeploy": true, 4 | 5 | "includes": "com.goodow.realtime~realtime-json~0.5.5-SNAPSHOT", 6 | 7 | "description": "Event bus over WebSocket for Java/Android/iOS/Javascript/GWT/J2ObjC", 8 | "licenses": ["The Apache Software License Version 2.0"], 9 | "author": "Larry Tin", 10 | 11 | "developers": ["Larry Tin"], 12 | "keywords": ["EventBus", "WebSocket", "android", "ios", "javascript", "gwt", "j2objc"], 13 | "homepage": "https://github.com/goodow/realtime-channel/" 14 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/Storage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | public interface Storage { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | @com.google.gwt.core.client.js.JsNamespace("$wnd.realtime.channel") 15 | package com.goodow.realtime.channel; 16 | -------------------------------------------------------------------------------- /src/main/assembly/mod.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | mod 6 | 7 | zip 8 | 9 | 10 | false 11 | 12 | 13 | 14 | 15 | ${vertx.mods.directory}/${vertx.module.name} 16 | 17 | ** 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/resources/com/goodow/realtime/Platform.gwt.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Maven build output. 2 | target/ 3 | 4 | # Generated for Eclipse. 5 | .settings/ 6 | .project 7 | .classpath 8 | 9 | # Xcode 10 | .DS_Store 11 | */build/* 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata 21 | profile 22 | *.moved-aside 23 | DerivedData 24 | .idea/ 25 | *.hmap 26 | *.xccheckout 27 | 28 | # CocoaPods 29 | Pods 30 | 31 | # Generated for IntelliJ IDEA. 32 | .idea/ 33 | *.ipr 34 | *.iws 35 | *.iml 36 | ## Additional for IntelliJ 37 | out/ 38 | # generated by mpeltonen/sbt-idea plugin 39 | .idea_modules/ 40 | # generated by JIRA plugin 41 | atlassian-ide-plugin.xml 42 | # generated by Crashlytics plugin (for Android Studio and Intellij) 43 | com_crashlytics_export_strings.xml 44 | 45 | bower_components/ -------------------------------------------------------------------------------- /src/main/resources/com/goodow/realtime/channel/ChannelProd.gwt.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/Net.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | import com.goodow.realtime.json.JsonObject; 17 | 18 | public interface Net { 19 | WebSocket createWebSocket(String url, JsonObject options); 20 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/AsyncResultHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | /** 17 | * Handler for {@link AsyncResult} 18 | */ 19 | public interface AsyncResultHandler extends Handler> { 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/Handler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | /** 17 | * A generic event handler 18 | */ 19 | public interface Handler { 20 | 21 | /** 22 | * Something has happened, so handle it. 23 | */ 24 | void handle(E event); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/html/ChannelEntryPoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.html; 15 | 16 | import com.google.gwt.core.client.EntryPoint; 17 | 18 | class ChannelEntryPoint implements EntryPoint { 19 | 20 | @Override 21 | public void onModuleLoad() { 22 | HtmlPlatform.register(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/MessageHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel; 15 | 16 | import com.goodow.realtime.core.Handler; 17 | 18 | /** 19 | * Handler for {@link Message} 20 | */ 21 | public interface MessageHandler extends Handler> { 22 | @Override 23 | public void handle(Message message); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/State.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel; 15 | 16 | import com.google.gwt.core.client.js.JsExport; 17 | import com.google.gwt.core.client.js.JsType; 18 | 19 | @JsExport 20 | @JsType 21 | public enum State { 22 | CONNECTING, OPEN, CLOSING, CLOSED; 23 | public static final State values[] = values(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/PlatformFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | /** 17 | * Generic platform interface. New platforms are defined as implementations of this interface. 18 | */ 19 | public interface PlatformFactory { 20 | Diff diff(); 21 | 22 | Net net(); 23 | 24 | Scheduler scheduler(); 25 | 26 | Platform.Type type(); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/objc/ObjCNet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.objc; 15 | 16 | import com.goodow.realtime.core.Net; 17 | import com.goodow.realtime.core.WebSocket; 18 | import com.goodow.realtime.json.JsonObject; 19 | 20 | class ObjCNet implements Net { 21 | @Override 22 | public WebSocket createWebSocket(String url, JsonObject options) { 23 | return new ObjCWebSocket(url, options); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/html/HtmlNet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.html; 15 | 16 | import com.goodow.realtime.core.Net; 17 | import com.goodow.realtime.core.WebSocket; 18 | import com.goodow.realtime.json.JsonObject; 19 | 20 | class HtmlNet implements Net { 21 | 22 | @Override 23 | public WebSocket createWebSocket(String url, JsonObject options) { 24 | return SockJS.create(url, null, options); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/VoidHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | /** 17 | * This class can be used for simple handlers which don't receive any value. 18 | */ 19 | public abstract class VoidHandler implements Handler { 20 | 21 | @Override 22 | public final void handle(Void event) { 23 | handle(); 24 | } 25 | 26 | /** 27 | * Handle the event. It should be overridden by the user. 28 | */ 29 | protected abstract void handle(); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/AsyncResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | /** 17 | * Represents a result that may not have occurred yet. 18 | */ 19 | public interface AsyncResult { 20 | /** 21 | * An exception describing failure. This will be null if the operation succeeded. 22 | */ 23 | Throwable cause(); 24 | 25 | /** 26 | * Did it fail? 27 | */ 28 | boolean failed(); 29 | 30 | /** 31 | * The result of the operation. This will be null if the operation failed. 32 | */ 33 | T result(); 34 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | realtime-channel [![Build Status](https://travis-ci.org/goodow/realtime-channel.svg?branch=master)](https://travis-ci.org/goodow/realtime-channel) 2 | ================ 3 | 4 | Event bus over WebSocket for Java/Android/iOS/Javascript/GWT/J2ObjC 5 | 6 | Visit [Google groups](https://groups.google.com/forum/#!forum/goodow-realtime) for discussions and announcements. 7 | 8 | ### Server side: realtime-channel 9 | * Vert.x Module Identifier: [com.goodow.realtime~realtime-channel~0.5.5-SNAPSHOT](https://oss.sonatype.org/index.html#nexus-search;gav~com.goodow.realtime~realtime-channel~~~) 10 | * Configuration: https://github.com/goodow/realtime-channel/blob/master/src/main/resources/channel.conf 11 | * Development And Running: https://github.com/goodow/realtime-channel/blob/master/run-server.sh 12 | 13 | ### For java and android: realtime-android 14 | See: https://github.com/goodow/realtime-android 15 | 16 | ### For iOS and Mac OS X: GDChannel 17 | See: https://github.com/goodow/GDChannel 18 | 19 | ### For javascript: realtime.channel 20 | See: https://github.com/goodow/bower-realtime-channel 21 | 22 | You can try out the Goodow Realtime Channel API Playground on its [live instance](http://realtimeplayground.goodow.com/bus.html). 23 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/Future.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | public interface Future extends AsyncResult { 17 | /** 18 | * Has it completed? 19 | */ 20 | boolean complete(); 21 | 22 | /** 23 | * Set the failure. Any handler will be called, if there is one 24 | */ 25 | Future setFailure(Throwable throwable); 26 | 27 | /** 28 | * Set a handler for the result. It will get called when it's complete 29 | */ 30 | Future setHandler(Handler> handler); 31 | 32 | /** 33 | * Set the result. Any handler will be called, if there is one 34 | */ 35 | Future setResult(T result); 36 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/objc/ObjCDiff.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.objc; 15 | 16 | import com.goodow.realtime.core.Diff; 17 | import com.goodow.realtime.json.JsonArray; 18 | 19 | import java.util.Comparator; 20 | 21 | /*-[ 22 | #import "JavaObjCDiff+Adapter.h" 23 | ]-*/ 24 | class ObjCDiff implements Diff { 25 | @Override 26 | public native void diff(String before, String after, ListTarget target) /*-[ 27 | [self diff:before after:after target:target]; 28 | ]-*/; 29 | 30 | @Override 31 | public native void diff(JsonArray before, JsonArray after, ListTarget target, 32 | Comparator comparator) /*-[ 33 | ]-*/; 34 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/Diff.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | import com.goodow.realtime.json.JsonArray; 17 | 18 | import java.util.Comparator; 19 | 20 | public interface Diff { 21 | public static interface ListTarget { 22 | void insert(int startIndex, T values); 23 | 24 | void remove(int startIndex, int length); 25 | 26 | void replace(int startIndex, T values); 27 | 28 | void move(int fromIndex, int toIndex, int length); 29 | } 30 | 31 | void diff(String before, String after, ListTarget target); 32 | 33 | void diff(JsonArray before, JsonArray after, ListTarget target, 34 | Comparator comparator); 35 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/Registration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | import com.google.gwt.core.client.js.JsType; 17 | 18 | /** 19 | * Registration objects returned when an event handler is bound (e.g. via 20 | * {@link com.goodow.realtime.channel.Bus#subscribe}), used to deregister. 21 | */ 22 | @JsType 23 | public interface Registration { 24 | 25 | Registration EMPTY = new Registration() { 26 | @Override 27 | public void unregister() { 28 | } 29 | }; 30 | 31 | /** 32 | * Deregisters the handler associated with this registration object if the handler is still 33 | * attached to the event bus. If the handler is no longer attached to the event bus, this is a 34 | * no-op. 35 | */ 36 | void unregister(); 37 | } 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | before_install: 4 | - git config --global user.name "Goodow Bot" 5 | - git config --global user.email "dev@goodow.com" 6 | - cd bower-realtime-channel && git pull origin v0.5.x && git remote set-url origin "https://${GH_TOKEN}@github.com/goodow/bower-realtime-channel.git" 7 | - cd ${TRAVIS_BUILD_DIR} 8 | - wget -P target/travis https://raw.githubusercontent.com/goodow/maven/master/settings.xml 9 | 10 | script: 11 | - mvn test -B 12 | - ./gwt-compile.sh 13 | 14 | after_success: 15 | - cd bower-realtime-channel && git commit -a -m "Generated by goodow/realtime-channel@${TRAVIS_COMMIT}" && git push origin HEAD:v0.5.x 16 | - cd ${TRAVIS_BUILD_DIR} 17 | - '[ ${TRAVIS_PULL_REQUEST} = ''false'' ] && mvn deploy -Psonatype-oss-release -Dgpg.skip=true 18 | --settings target/travis/settings.xml || mvn verify --settings target/travis/settings.xml' 19 | 20 | env: 21 | global: 22 | - secure: b4X3MJYGJSwOWsGzDHzuh5+cfigEnPHEipg9yn2TFNYQNshQV5v4INxLBXfEGioEdtjcU7RzU9q8VkyCvF/hgEEWZZLQwy+OXMh8l849DO82ywZ9gq584tuu3zaADWKuxSyONW34OEdrdeS66Z0+iM3/8WFlWuYATbpPmSFhwJI= 23 | - secure: Boxz3nNJvOjgcM2ivSSwlBkXQT69KRjI8+KbdmTG80OKfbmoOksHHfUCPainYYUSgcN+U3i8+pG3K61/rrP08RbBKKQO6LKdHZXXq38UE5hdcE6XPBB7DDb7x2wM1YkX+WA0dhw0wTbZGCRxZNeaVhbDSyJkkEr2x6ElEyYi5jE= 24 | - secure: at0JGJV0C2Qsa/pqNfHa8ulDGvvkQB68EgrUBPqr5g6haluJJB0UrQyvon9PVQ6N1f8w86LPCLPMjnDFNRaHRMxiWiY0mTzkSv/KlgPAr/XQBo7+ypUZ5FOJ6yD2dFmnQzjZTLn1aX+gaE5JRaAkHm3VCSuqDyds3ISy+T0S0Qo= -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/ReplyFailure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel; 15 | 16 | public enum ReplyFailure { 17 | TIMEOUT, NO_HANDLERS, RECIPIENT_FAILURE; 18 | 19 | public static ReplyFailure fromInt(int i) { 20 | switch (i) { 21 | case 0: 22 | return TIMEOUT; 23 | case 1: 24 | return NO_HANDLERS; 25 | case 2: 26 | return RECIPIENT_FAILURE; 27 | default: 28 | throw new IllegalStateException("Invalid index " + i); 29 | } 30 | } 31 | 32 | public int toInt() { 33 | switch (this) { 34 | case TIMEOUT: 35 | return 0; 36 | case NO_HANDLERS: 37 | return 1; 38 | case RECIPIENT_FAILURE: 39 | return 2; 40 | default: 41 | throw new IllegalStateException("How did we get here?"); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/com/google/gwt/core/client/js/JsNoExport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.google.gwt.core.client.js; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * JsExport marks a constructor, static method, or static field as creating a an unobfuscated alias 26 | * in the global scope. JsExport acts as an entry-point from the standpoint of the optimizer, 27 | * and all code reachable from an exported method is also considered live, so use with care. 28 | */ 29 | @Retention(RetentionPolicy.RUNTIME) 30 | @Target({ ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD}) 31 | @Documented 32 | public @interface JsNoExport { 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/objc/ObjCWebSocket.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.objc; 15 | 16 | import com.goodow.realtime.channel.State; 17 | import com.goodow.realtime.core.WebSocket; 18 | import com.goodow.realtime.json.JsonObject; 19 | 20 | /** 21 | * ObjC stub implementation of {@link WebSocket}. 22 | * 23 | * @see ObjCWebSocket.m 25 | */ 26 | final class ObjCWebSocket implements WebSocket { 27 | ObjCWebSocket(String url, JsonObject options) { 28 | } 29 | 30 | @Override 31 | public native void close(); 32 | 33 | @Override 34 | public native State getReadyState(); 35 | 36 | @Override 37 | public native void send(String data); 38 | 39 | @Override 40 | public native void setListen(WebSocketHandler handler); 41 | } -------------------------------------------------------------------------------- /src/main/java/com/google/gwt/core/client/js/JsExport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.google.gwt.core.client.js; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * JsExport marks a constructor, static method, or static field as creating a an unobfuscated alias 26 | * in the global scope. JsExport acts as an entry-point from the standpoint of the optimizer, 27 | * and all code reachable from an exported method is also considered live, so use with care. 28 | */ 29 | @Retention(RetentionPolicy.RUNTIME) 30 | @Target({ ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE }) 31 | @Documented 32 | public @interface JsExport { 33 | String value() default ""; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/google/gwt/core/client/js/JsNamespace.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.google.gwt.core.client.js; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * Provides a default namespace for @JsExport annotations which don't specify a value. The 26 | * computed fully qualified export symbol will be a combination of the nearest enclosing 27 | * \@JsNamespace and the Java name of the method or field @JsExport is applied to. If applied to 28 | * package-info.java, applies to all types in a package. 29 | */ 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Target({ElementType.TYPE, ElementType.PACKAGE}) 32 | @Documented 33 | public @interface JsNamespace { 34 | String value() default ""; 35 | } 36 | -------------------------------------------------------------------------------- /gwt-compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | 4 | #mvn compile gwt:compile -Dgwt.module=com.goodow.realtime.channel.ChannelProd \ 5 | # -Dgwt.disableCastChecking=true -Dgwt.disableClassMetadata=true \ 6 | # -Dgwt.compiler.optimizationLevel=9 -Dgwt.compiler.enableClosureCompiler=true 7 | # -Dgwt.draftCompile=true -Dgwt.style=DETAILED -Dgwt.compiler.compileReport=true 8 | 9 | CLASSPATH=./target/classes:./src/main/java 10 | CLASSPATH=$CLASSPATH:$HOME/.m2/repository/com/google/gwt/gwt-dev/2.7.0/gwt-dev-2.7.0.jar 11 | CLASSPATH=$CLASSPATH:$HOME/.m2/repository/org/ow2/asm/asm/5.0.3/asm-5.0.3.jar 12 | CLASSPATH=$CLASSPATH:$HOME/.m2/repository/com/google/gwt/gwt-user/2.7.0/gwt-user-2.7.0.jar 13 | CLASSPATH=$CLASSPATH:$HOME/.m2/repository/javax/validation/validation-api/1.0.0.GA/validation-api-1.0.0.GA.jar 14 | CLASSPATH=$CLASSPATH:$HOME/.m2/repository/javax/validation/validation-api/1.0.0.GA/validation-api-1.0.0.GA-sources.jar 15 | CLASSPATH=$CLASSPATH:$HOME/.m2/repository/com/goodow/realtime/realtime-json/0.5.5-SNAPSHOT/realtime-json-0.5.5-SNAPSHOT.jar 16 | CLASSPATH=$CLASSPATH:$HOME/.m2/repository/com/goodow/realtime/realtime-json/0.5.5-SNAPSHOT/realtime-json-0.5.5-SNAPSHOT-sources.jar 17 | 18 | java -cp $CLASSPATH \ 19 | com.google.gwt.dev.Compiler -war target/realtime-channel-0.5.5-SNAPSHOT \ 20 | -XnoclassMetadata -XnocheckCasts -XjsInteropMode JS -XclosureCompiler -optimize 9 \ 21 | com.goodow.realtime.channel.ChannelProd 22 | 23 | cp target/realtime-channel-0.5.5-SNAPSHOT/channel/channel.nocache.js bower-realtime-channel/realtime-channel.js 24 | -------------------------------------------------------------------------------- /src/main/resources/com/goodow/realtime/channel/Channel.gwt.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/mqtt/packet/MqttPacket.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.mqtt.packet; 15 | 16 | /** 17 | * An on-the-wire representation of an MQTT message. 18 | */ 19 | public abstract class MqttPacket { 20 | public static final byte CONNECT = 1; 21 | public static final byte CONNACK = 2; 22 | public static final byte PUBLISH = 3; 23 | public static final byte PUBACK = 4; 24 | public static final byte PUBREC = 5; 25 | public static final byte PUBREL = 6; 26 | public static final byte PUBCOMP = 7; 27 | public static final byte SUBSCRIBE = 8; 28 | public static final byte SUBACK = 9; 29 | public static final byte UNSUBSCRIBE = 10; 30 | public static final byte UNSUBACK = 11; 31 | public static final byte PINGREQ = 12; 32 | public static final byte PINGRESP = 13; 33 | public static final byte DISCONNECT = 14; 34 | 35 | private boolean dup; 36 | private byte qos; 37 | private boolean retain; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/ReplyException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel; 15 | 16 | public class ReplyException extends RuntimeException { 17 | private static final long serialVersionUID = -4441153344646081242L; 18 | private final ReplyFailure failureType; 19 | private final int failureCode; 20 | 21 | public ReplyException(ReplyFailure failureType) { 22 | super((String) null); 23 | this.failureType = failureType; 24 | this.failureCode = -1; 25 | } 26 | 27 | public ReplyException(ReplyFailure failureType, int failureCode, String message) { 28 | super(message); 29 | this.failureType = failureType; 30 | this.failureCode = failureCode; 31 | } 32 | 33 | public ReplyException(ReplyFailure failureType, String message) { 34 | super(message); 35 | this.failureType = failureType; 36 | this.failureCode = -1; 37 | } 38 | 39 | public int failureCode() { 40 | return failureCode; 41 | } 42 | 43 | public ReplyFailure failureType() { 44 | return failureType; 45 | } 46 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/html/HtmlPlatform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.html; 15 | 16 | import com.goodow.realtime.core.Diff; 17 | import com.goodow.realtime.core.Net; 18 | import com.goodow.realtime.core.Platform; 19 | import com.goodow.realtime.core.Platform.Type; 20 | import com.goodow.realtime.core.PlatformFactory; 21 | import com.goodow.realtime.core.Scheduler; 22 | 23 | class HtmlPlatform implements PlatformFactory { 24 | /** 25 | * Prepares the HTML platform for operation. 26 | */ 27 | public static void register() { 28 | Platform.setFactory(new HtmlPlatform()); 29 | } 30 | 31 | private final HtmlNet net = new HtmlNet(); 32 | private final HtmlScheduler scheduler = new HtmlScheduler(); 33 | private final HtmlDiff diff = new HtmlDiff(); 34 | 35 | @Override 36 | public Diff diff() { 37 | return diff; 38 | } 39 | 40 | @Override 41 | public Net net() { 42 | return net; 43 | } 44 | 45 | @Override 46 | public Scheduler scheduler() { 47 | return scheduler; 48 | } 49 | 50 | @Override 51 | public Type type() { 52 | return Type.HTML; 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/WebSocket.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | import com.goodow.realtime.channel.State; 17 | import com.goodow.realtime.json.JsonObject; 18 | 19 | public interface WebSocket { 20 | /** 21 | * Listens for events on a {@link WebSocket}. 22 | */ 23 | interface WebSocketHandler { 24 | 25 | /** 26 | * Called when the socket is closed. When the socket is closed, it cannot be reopened. 27 | */ 28 | void onClose(JsonObject reason); 29 | 30 | /** 31 | * Called when an error occurs on the socket. 32 | */ 33 | void onError(String error); 34 | 35 | /** 36 | * Called when the socket receives a message. 37 | */ 38 | void onMessage(String message); 39 | 40 | /** 41 | * Called when the socket is ready to receive messages. 42 | */ 43 | void onOpen(); 44 | } 45 | 46 | /** 47 | * Close the socket. The socket cannot be used again after calling close; the server must create a 48 | * new socket. 49 | */ 50 | void close(); 51 | 52 | State getReadyState(); 53 | 54 | void send(String data); 55 | 56 | void setListen(WebSocketHandler handler); 57 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/util/IdGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.util; 15 | 16 | import java.util.Random; 17 | 18 | public class IdGenerator { 19 | 20 | /** valid characters. */ 21 | static final char[] WEB64_ALPHABET = 22 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray(); 23 | static final char[] NUMBERS = "0123456789".toCharArray(); 24 | 25 | private final Random random; 26 | 27 | public IdGenerator() { 28 | this(new Random()); 29 | } 30 | 31 | public IdGenerator(Random random) { 32 | this.random = random; 33 | } 34 | 35 | /** 36 | * Returns a string with {@code length} random characters. 37 | */ 38 | public String next(int length) { 39 | StringBuilder result = new StringBuilder(length); 40 | for (int i = 0; i < length; i++) { 41 | result.append(WEB64_ALPHABET[random.nextInt(64)]); 42 | } 43 | return result.toString(); 44 | } 45 | 46 | public String nextNumbers(int length) { 47 | StringBuilder result = new StringBuilder(length); 48 | for (int i = 0; i < length; i++) { 49 | result.append(NUMBERS[random.nextInt(10)]); 50 | } 51 | return result.toString(); 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/server/ChannelVerticle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server; 15 | 16 | import com.goodow.realtime.channel.server.impl.BridgeHook; 17 | 18 | import org.vertx.java.busmods.BusModBase; 19 | import org.vertx.java.core.AsyncResult; 20 | import org.vertx.java.core.Future; 21 | import org.vertx.java.core.Handler; 22 | import org.vertx.java.core.impl.CountingCompletionHandler; 23 | import org.vertx.java.core.impl.VertxInternal; 24 | 25 | public class ChannelVerticle extends BusModBase { 26 | 27 | @Override 28 | public void start(final Future startedResult) { 29 | super.start(); 30 | final CountingCompletionHandler countDownLatch = 31 | new CountingCompletionHandler((VertxInternal) vertx); 32 | countDownLatch.setHandler(new Handler>() { 33 | @Override 34 | public void handle(AsyncResult ar) { 35 | if (ar.failed()) { 36 | startedResult.setFailure(ar.cause()); 37 | } else if (ar.succeeded()) { 38 | startedResult.setResult(null); 39 | } 40 | } 41 | }); 42 | new ChannelBridge(vertx, config).setHook(new BridgeHook(vertx)).bridge(countDownLatch); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/objc/ObjCPlatform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.objc; 15 | 16 | import com.goodow.realtime.core.Diff; 17 | import com.goodow.realtime.core.Net; 18 | import com.goodow.realtime.core.Platform; 19 | import com.goodow.realtime.core.Platform.Type; 20 | import com.goodow.realtime.core.PlatformFactory; 21 | import com.goodow.realtime.core.Scheduler; 22 | 23 | class ObjCPlatform implements PlatformFactory { 24 | /** 25 | * Registers the Objective-C platform with a default configuration. 26 | */ 27 | public static void register() { 28 | Platform.setFactory(new ObjCPlatform()); 29 | } 30 | 31 | /*-[ 32 | + (void)load { 33 | [ComGoodowRealtimeObjcObjCPlatform register__]; 34 | } 35 | ]-*/; 36 | 37 | private final Net net = new ObjCNet(); 38 | private final ObjCScheduler scheduler = new ObjCScheduler(); 39 | private final ObjCDiff diff = new ObjCDiff(); 40 | 41 | @Override 42 | public Diff diff() { 43 | return diff; 44 | } 45 | 46 | @Override 47 | public Net net() { 48 | return net; 49 | } 50 | 51 | @Override 52 | public Scheduler scheduler() { 53 | return scheduler; 54 | } 55 | 56 | @Override 57 | public Type type() { 58 | return Type.IOS; 59 | } 60 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .java .m 2 | .PHONY: default clean translate link 3 | 4 | include ../resources/make/common.mk 5 | # J2OBJC_DIST = GDChannel/Project/Pods/J2ObjC/dist 6 | 7 | CHANNEL_GEN_DIR = GDChannel/Classes/generated 8 | MAIN_SOURCES = $(subst $(MAIN_SRC_DIR)/,,$(shell find $(MAIN_SRC_DIR) -name *.java ! -path "*/html/*" ! -path "*/server/*")) 9 | MAIN_GEN_SOURCES = $(MAIN_SOURCES:%.java=$(CHANNEL_GEN_DIR)/%.m) 10 | OVERRIDE_GEN_DIR = GDChannel/Classes/override 11 | MAIN_OBJECTS = $(MAIN_SOURCES:%.java=$(BUILD_DIR)/main/%.o) 12 | SUPPORT_LIB = $(BUILD_DIR)/libGDChannel.a 13 | 14 | TEMP_PATH = $(M2_REPO)/com/goodow/realtime/realtime-json/0.5.5-SNAPSHOT/realtime-json-0.5.5-SNAPSHOT.jar 15 | CLASSPATH = $(shell echo $(TEMP_PATH) | sed 's/ //g') 16 | 17 | default: clean translate 18 | 19 | translate: translate_main 20 | 21 | pre_translate_main: $(BUILD_DIR) $(CHANNEL_GEN_DIR) 22 | @rm -f $(MAIN_SOURCE_LIST) 23 | @touch $(MAIN_SOURCE_LIST) 24 | 25 | $(CHANNEL_GEN_DIR)/%.m $(CHANNEL_GEN_DIR)/%.h: $(MAIN_SRC_DIR)/%.java 26 | @echo $? >> $(MAIN_SOURCE_LIST) 27 | 28 | translate_main: pre_translate_main $(MAIN_GEN_SOURCES) 29 | @if [ `cat $(MAIN_SOURCE_LIST) | wc -l` -ge 1 ] ; then \ 30 | $(J2OBJC) -sourcepath $(MAIN_SRC_DIR) -d $(CHANNEL_GEN_DIR) \ 31 | -classpath $(CLASSPATH) \ 32 | `cat $(MAIN_SOURCE_LIST)` ; \ 33 | fi 34 | cp -r $(OVERRIDE_GEN_DIR)/ $(CHANNEL_GEN_DIR) 35 | 36 | $(BUILD_DIR)/main/%.o: $(CHANNEL_GEN_DIR)/%.m $(MAIN_SRC_DIR)/%.java 37 | @mkdir -p `dirname $@` 38 | @$(J2OBJCC) -c $< -o $@ -g -I$(CHANNEL_GEN_DIR) 39 | 40 | $(SUPPORT_LIB): $(MAIN_OBJECTS) 41 | libtool -static -o $(SUPPORT_LIB) $(MAIN_OBJECTS) $(SUPPORT_LIB) 42 | 43 | link: translate $(SUPPORT_LIB) 44 | 45 | $(CHANNEL_GEN_DIR): 46 | @mkdir -p $(CHANNEL_GEN_DIR) 47 | $(BUILD_DIR): 48 | @mkdir -p $(BUILD_DIR)/main 49 | 50 | clean: 51 | @rm -rf $(CHANNEL_GEN_DIR) $(BUILD_DIR) 52 | -------------------------------------------------------------------------------- /src/main/java/com/google/gwt/core/client/js/JsProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.google.gwt.core.client.js; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * JsProperty marks a method in a {@link com.google.gwt.core.client.js.JsType} as a property accessor and recognizes 26 | * JavaBean style naming convention. Instead of translating method calls to JsProperty methods 27 | * as method calls in JS, they will be replaced with dotted property lookups. 28 | *

Examples: 29 | *

    30 | *
  • {@code @JsProperty getX()} translates as this.x 31 | *
  • {@code @JsProperty x()} translates as this.x 32 | *
  • {@code @JsProperty setX(int y)} translates as this.x=y 33 | *
  • {@code @JsProperty x(int x)} translates as this.x=y 34 | *
  • {@code @JsProperty hasX(int x)} translates as x in this 35 | *
36 | *

37 | * In addition, fluent style return this syntax is supported for setters, so 38 | * {@code @JsProperty T setX(int x)} translates as this.x=x, return this. 39 | */ 40 | @Retention(RetentionPolicy.RUNTIME) 41 | @Target(ElementType.METHOD) 42 | @Documented 43 | public @interface JsProperty { 44 | String value() default ""; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/Scheduler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | /** 17 | * This class provides low-level task scheduling primitives. 18 | */ 19 | public interface Scheduler { 20 | /** 21 | * Cancel the timer with the specified {@code id}. Returns {@code} true if the timer was 22 | * successfully cancelled, or {@code false} if the timer does not exist. 23 | */ 24 | boolean cancelTimer(int id); 25 | 26 | void handle(Object handler, Object event); 27 | 28 | /** 29 | * A deferred command is executed after the event loop returns. 30 | */ 31 | void scheduleDeferred(Handler handler); 32 | 33 | /** 34 | * Set a one-shot timer to fire after {@code delayMs} milliseconds, at which point {@code handler} 35 | * will be called. 36 | * 37 | * @return the unique ID of the timer 38 | */ 39 | int scheduleDelay(int delayMs, Handler handler); 40 | 41 | /** 42 | * Schedules a repeating handler that is scheduled with a constant periodicity. That is, the 43 | * handler will be invoked every delayMs milliseconds, regardless of how long the 44 | * previous invocation took to complete. 45 | * 46 | * @param delayMs the period with which the handler is executed 47 | * @param handler the handler to execute 48 | * 49 | * @return the unique ID of the timer 50 | */ 51 | int schedulePeriodic(int delayMs, Handler handler); 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/server/impl/JavaDiff.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server.impl; 15 | 16 | import com.goodow.realtime.core.Diff; 17 | import com.goodow.realtime.json.JsonArray; 18 | 19 | import java.util.Comparator; 20 | import java.util.LinkedList; 21 | 22 | import name.fraser.neil.plaintext.diff_match_patch; 23 | 24 | public class JavaDiff implements Diff { 25 | private final diff_match_patch dmp = new diff_match_patch(); 26 | 27 | @Override 28 | public void diff(String before, String after, ListTarget target) { 29 | LinkedList diffs = dmp.diff_main(before, after); 30 | dmp.diff_cleanupSemantic(diffs); 31 | int cursor = 0; 32 | for (diff_match_patch.Diff diff : diffs) { 33 | String text = diff.text; 34 | int len = text.length(); 35 | switch (diff.operation) { 36 | case EQUAL: 37 | cursor += len; 38 | break; 39 | case INSERT: 40 | target.insert(cursor, text); 41 | cursor += len; 42 | break; 43 | case DELETE: 44 | target.remove(cursor, len); 45 | break; 46 | default: 47 | throw new RuntimeException("Shouldn't reach here!"); 48 | } 49 | } 50 | } 51 | 52 | @Override 53 | public void diff(JsonArray before, JsonArray after, ListTarget target, 54 | Comparator comparator) { 55 | 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/Platform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | /** 17 | * The main Platform interface. The static methods in this class provide access to the various 18 | * available subsystems. 19 | * 20 | *

21 | * You must register a {@link Platform} before calling any of these methods. For example, 22 | * JavaPlatform.register();. 23 | *

24 | */ 25 | public class Platform { 26 | public enum Type { 27 | JAVA, HTML, ANDROID, IOS, FLASH, STUB, VERTX 28 | } 29 | 30 | private static PlatformFactory FACTORY; 31 | 32 | public static Diff diff() { 33 | return get().diff(); 34 | } 35 | 36 | public static Net net() { 37 | return get().net(); 38 | } 39 | 40 | public static Scheduler scheduler() { 41 | return get().scheduler(); 42 | } 43 | 44 | /** 45 | * Configures the current {@link Platform}. Do not call this directly unless you're implementing a 46 | * new platform. 47 | */ 48 | public static void setFactory(PlatformFactory factory) { 49 | FACTORY = factory; 50 | } 51 | 52 | public static Platform.Type type() { 53 | return get().type(); 54 | } 55 | 56 | private static PlatformFactory get() { 57 | assert FACTORY != null : 58 | "You must register a platform first by invoke {Java|Android}Platform.register()"; 59 | return FACTORY; 60 | } 61 | 62 | // Non-instantiable 63 | protected Platform() { 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/html/HtmlDiff.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.html; 15 | 16 | import com.goodow.realtime.core.Diff; 17 | import com.goodow.realtime.json.JsonArray; 18 | 19 | import java.util.Comparator; 20 | 21 | final class HtmlDiff implements Diff { 22 | 23 | protected HtmlDiff() { 24 | } 25 | 26 | @Override 27 | // @formatter:off 28 | public native void diff(String before, String after, ListTarget target) /*-{ 29 | var dmp = new $wnd.diff_match_patch(); 30 | var diffs = dmp.diff_main(before, after); 31 | dmp.diff_cleanupSemantic(diffs); 32 | var cursor = 0; 33 | for (var i in diffs) { 34 | var text = diffs[i][1], len = text.length; 35 | switch (diffs[i][0]) { 36 | case 0: 37 | cursor += len; 38 | break; 39 | case 1: 40 | target.@com.goodow.realtime.core.Diff.ListTarget::insert(ILjava/lang/Object;)(cursor, text); 41 | cursor += len; 42 | break; 43 | case -1: 44 | target.@com.goodow.realtime.core.Diff.ListTarget::remove(II)(cursor, len); 45 | break; 46 | default: 47 | throw @java.lang.RuntimeException::new(Ljava/lang/String;)("Shouldn't reach here!"); 48 | } 49 | } 50 | }-*/; 51 | // @formatter:on 52 | 53 | 54 | @Override 55 | public void diff(JsonArray before, JsonArray after, ListTarget target, 56 | Comparator comparator) { 57 | 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/server/impl/VertxPlatform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server.impl; 15 | 16 | import com.goodow.realtime.core.Diff; 17 | import com.goodow.realtime.core.Net; 18 | import com.goodow.realtime.core.Platform; 19 | import com.goodow.realtime.core.Platform.Type; 20 | import com.goodow.realtime.core.PlatformFactory; 21 | import com.goodow.realtime.core.Scheduler; 22 | import com.goodow.realtime.core.WebSocket; 23 | import com.goodow.realtime.json.JsonObject; 24 | 25 | import org.vertx.java.core.Vertx; 26 | 27 | public class VertxPlatform implements PlatformFactory { 28 | /** 29 | * Registers the Vertx platform with a default configuration. 30 | */ 31 | public static void register(Vertx vertx) { 32 | Platform.setFactory(new VertxPlatform(vertx)); 33 | } 34 | 35 | protected final Scheduler scheduler; 36 | protected final Net net; 37 | private final JavaDiff diff; 38 | 39 | private VertxPlatform(final Vertx vertx) { 40 | scheduler = new VertxScheduler(vertx); 41 | net = new Net() { 42 | @Override 43 | public WebSocket createWebSocket(String url, JsonObject options) { 44 | return new VertxWebSocket(vertx, url); 45 | } 46 | }; 47 | diff = new JavaDiff(); 48 | } 49 | 50 | @Override 51 | public Diff diff() { 52 | return diff; 53 | } 54 | 55 | @Override 56 | public Net net() { 57 | return net; 58 | } 59 | 60 | @Override 61 | public Scheduler scheduler() { 62 | return scheduler; 63 | } 64 | 65 | @Override 66 | public Type type() { 67 | return Type.VERTX; 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/Registrations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core; 15 | 16 | import com.goodow.realtime.json.Json; 17 | import com.goodow.realtime.json.JsonArray; 18 | 19 | /** 20 | * A {@link Registration} that will call {@link Registration#unregister()} on 21 | * all added handlers if {@link Registration#unregister()} is called on this object. 22 | */ 23 | public class Registrations implements Registration { 24 | private JsonArray registrations; 25 | 26 | public Registrations add(Registration registration) { 27 | assert registration != null : "registration shouldn't be null"; 28 | if (registrations == null) { 29 | registrations = Json.createArray(); 30 | } 31 | registrations.push(registration); 32 | return this; 33 | } 34 | 35 | @Override 36 | public void unregister() { 37 | if (registrations != null) { 38 | registrations.forEach(new JsonArray.ListIterator() { 39 | @Override 40 | public void call(int index, Registration value) { 41 | value.unregister(); 42 | } 43 | }); 44 | // make sure we remove the handlers to avoid potential leaks 45 | // if someone fails to null out their reference to us 46 | registrations.clear(); 47 | registrations = null; 48 | } 49 | } 50 | 51 | public Registration wrap(final Registration registration) { 52 | add(registration); 53 | return new Registration() { 54 | @Override 55 | public void unregister() { 56 | registrations.removeValue(registration); 57 | registration.unregister(); 58 | } 59 | }; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/google/gwt/core/client/js/JsType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package com.google.gwt.core.client.js; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * JsType is used to describe the interface of a Javascript object, either one that already 26 | * exists from the external Javascript environment, or one that will be accessible to the 27 | * external Javascript environment. Calls to methods on interfaces marked with this annotation 28 | * are treated specially by the GWT compiler for interoperability purposes. Such methods need 29 | * not be backed by an Overlay type implementation, the GWT compiler will assume that a JS method on 30 | * the prototype of the underlying reference will match the name of the method on this interface. 31 | *

32 | * Furthermore, if the JsType is marked with a prototype reference, then concrete 33 | * implementations of the class emitted by the GWT compiler will use the specified prototype as 34 | * opposed the the ordinary one (e.g. java.lang.Object). 35 | *

36 | * JsTypes act like JavaScriptObject in terms of castability, except when a prototype is 37 | * specified, in which case, cast checks and instanceof checks will be delegated to the native 38 | * JS instanceof operator. 39 | */ 40 | @Retention(RetentionPolicy.RUNTIME) 41 | @Target(ElementType.TYPE) 42 | @Documented 43 | public @interface JsType { 44 | String prototype() default ""; 45 | boolean isNative() default false; 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/server/impl/VertxScheduler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server.impl; 15 | 16 | import com.goodow.realtime.core.Handler; 17 | import com.goodow.realtime.core.Scheduler; 18 | 19 | import org.vertx.java.core.Vertx; 20 | 21 | class VertxScheduler implements Scheduler { 22 | private final Vertx vertx; 23 | 24 | VertxScheduler(Vertx vertx) { 25 | this.vertx = vertx; 26 | } 27 | 28 | @Override 29 | public boolean cancelTimer(int id) { 30 | return vertx.cancelTimer(id); 31 | } 32 | 33 | @SuppressWarnings("unchecked") 34 | @Override 35 | public void handle(Object handler, Object event) { 36 | ((Handler) handler).handle(event); 37 | } 38 | 39 | @Override 40 | public void scheduleDeferred(final Handler handler) { 41 | vertx.runOnContext(new org.vertx.java.core.Handler() { 42 | @Override 43 | public void handle(Void event) { 44 | handler.handle(null); 45 | } 46 | }); 47 | } 48 | 49 | @Override 50 | public int scheduleDelay(int delayMs, final Handler handler) { 51 | return (int) vertx.setTimer(delayMs, new org.vertx.java.core.Handler() { 52 | @Override 53 | public void handle(Long event) { 54 | handler.handle(null); 55 | } 56 | }); 57 | } 58 | 59 | @Override 60 | public int schedulePeriodic(int delayMs, final Handler handler) { 61 | return (int) vertx.setPeriodic(delayMs, new org.vertx.java.core.Handler() { 62 | @Override 63 | public void handle(Long event) { 64 | handler.handle(null); 65 | } 66 | }); 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/impl/BusHookProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.impl; 15 | 16 | import com.goodow.realtime.channel.BusHook; 17 | import com.goodow.realtime.channel.Message; 18 | import com.goodow.realtime.core.Handler; 19 | 20 | public abstract class BusHookProxy implements BusHook { 21 | @Override 22 | public void handleOpened() { 23 | if (delegate() != null) { 24 | delegate().handleOpened(); 25 | } 26 | } 27 | 28 | @Override 29 | public void handlePostClose() { 30 | if (delegate() != null) { 31 | delegate().handlePostClose(); 32 | } 33 | } 34 | 35 | @Override 36 | public boolean handlePreClose() { 37 | return delegate() == null ? true : delegate().handlePreClose(); 38 | } 39 | 40 | @SuppressWarnings("rawtypes") 41 | @Override 42 | public boolean handlePreSubscribe(String topic, Handler handler) { 43 | return delegate() == null ? true : delegate().handlePreSubscribe(topic, handler); 44 | } 45 | 46 | @Override 47 | public boolean handleReceiveMessage(Message message) { 48 | return delegate() == null ? true : delegate().handleReceiveMessage(message); 49 | } 50 | 51 | @Override 52 | public boolean handleSendOrPub(boolean send, String topic, Object msg, 53 | Handler> replyHandler) { 54 | return delegate() == null ? true : delegate().handleSendOrPub(send, topic, msg, 55 | replyHandler); 56 | } 57 | 58 | @Override 59 | public boolean handleUnsubscribe(String topic) { 60 | return delegate() == null ? true : delegate().handleUnsubscribe(topic); 61 | } 62 | 63 | protected abstract BusHook delegate(); 64 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/Message.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel; 15 | 16 | import com.google.gwt.core.client.js.JsNoExport; 17 | import com.google.gwt.core.client.js.JsType; 18 | 19 | import com.goodow.realtime.core.Handler; 20 | 21 | /** 22 | * Represents a message on the event bus. 23 | */ 24 | @JsType 25 | public interface Message { 26 | /** 27 | * The body of the message 28 | */ 29 | T body(); 30 | 31 | /** 32 | * Signal that processing of this message failed. If the message was sent specifying a result 33 | * handler the handler will be called with a failure corresponding to the failure code and message 34 | * specified here 35 | * 36 | * @param failureCode A failure code to pass back to the sender 37 | * @param msg A message to pass back to the sender 38 | */ 39 | void fail(int failureCode, String msg); 40 | 41 | /** 42 | * @return Whether this message originated in the local session. 43 | */ 44 | boolean isLocal(); 45 | 46 | /** 47 | * Reply to this message. If the message was sent specifying a reply handler, that handler will be 48 | * called when it has received a reply. If the message wasn't sent specifying a receipt handler 49 | * this method does nothing. 50 | */ 51 | @JsNoExport 52 | void reply(Object msg); 53 | 54 | /** 55 | * The same as {@code reply(Object msg)} but you can specify handler for the reply - i.e. to 56 | * receive the reply to the reply. 57 | */ 58 | @SuppressWarnings("hiding") 59 | void reply(Object msg, Handler> replyHandler); 60 | 61 | /** 62 | * The reply topic (if any) 63 | */ 64 | String replyTopic(); 65 | 66 | /** 67 | * The topic the message was sent to 68 | */ 69 | String topic(); 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/BusHook.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel; 15 | 16 | import com.goodow.realtime.core.Handler; 17 | 18 | /** 19 | * A hook that you can use to receive various events on the Bus. 20 | */ 21 | public interface BusHook { 22 | /** 23 | * Called when the bus is opened 24 | */ 25 | void handleOpened(); 26 | 27 | /** 28 | * Called when the bus is closed 29 | */ 30 | void handlePostClose(); 31 | 32 | /** 33 | * Called before close the bus 34 | * 35 | * @return true to close the bus, false to reject it 36 | */ 37 | boolean handlePreClose(); 38 | 39 | /** 40 | * Called before register a handler 41 | * 42 | * @param topic The topic 43 | * @param handler The handler 44 | * @return true to let the registration occur, false otherwise 45 | */ 46 | @SuppressWarnings("rawtypes") 47 | boolean handlePreSubscribe(String topic, Handler handler); 48 | 49 | /** 50 | * Called when a message is received 51 | * 52 | * @param message The message 53 | * @return true To allow the message to deliver, false otherwise 54 | */ 55 | boolean handleReceiveMessage(Message message); 56 | 57 | /** 58 | * Called when sending or publishing on the bus 59 | * 60 | * @param send if true it's a send else it's a publish 61 | * @param topic The topic the message is being sent/published to 62 | * @param msg The message 63 | * @param replyHandler Reply handler will be called when any reply from the recipient is received 64 | * @return true To allow the send/publish to occur, false otherwise 65 | */ 66 | boolean handleSendOrPub(boolean send, String topic, Object msg, 67 | Handler> replyHandler); 68 | 69 | /** 70 | * Called when unregistering a handler 71 | * 72 | * @param topic The topic 73 | * @return true to let the unregistration occur, false otherwise 74 | */ 75 | boolean handleUnsubscribe(String topic); 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/html/SockJS.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.html; 15 | 16 | import com.goodow.realtime.channel.State; 17 | import com.goodow.realtime.core.WebSocket; 18 | import com.goodow.realtime.json.JsonObject; 19 | 20 | import com.google.gwt.core.client.JavaScriptObject; 21 | 22 | /** 23 | * SockJS implementation of {@link WebSocket}. 24 | */ 25 | final class SockJS extends JavaScriptObject implements WebSocket { 26 | 27 | // @formatter:off 28 | public static native WebSocket create(String url, Object _reserved, JsonObject options) /*-{ 29 | return new $wnd.SockJS(url, _reserved, options); 30 | }-*/; 31 | 32 | protected SockJS() { 33 | } 34 | 35 | @Override 36 | public native void close() /*-{ 37 | this.close(); 38 | }-*/; 39 | 40 | @Override 41 | public State getReadyState() { 42 | return State.values()[nativeGetReadyState()]; 43 | } 44 | 45 | @Override 46 | public native void send(String data) /*-{ 47 | this.send(data); 48 | }-*/; 49 | 50 | @Override 51 | public native void setListen(WebSocketHandler handler) /*-{ 52 | if (!handler) { 53 | this.onopen = null; 54 | this.onclose = null; 55 | this.onmessage = null; 56 | this.onerror = null; 57 | return; 58 | } 59 | 60 | this.onopen = function(e) { 61 | handler.@com.goodow.realtime.core.WebSocket.WebSocketHandler::onOpen()(); 62 | }; 63 | this.onclose = function(e) { 64 | handler.@com.goodow.realtime.core.WebSocket.WebSocketHandler::onClose(Lcom/goodow/realtime/json/JsonObject;)(e); 65 | }; 66 | this.onmessage = function(e) { 67 | handler.@com.goodow.realtime.core.WebSocket.WebSocketHandler::onMessage(Ljava/lang/String;)(e.data); 68 | }; 69 | this.onerror = function(e) { 70 | handler.@com.goodow.realtime.core.WebSocket.WebSocketHandler::onError(Ljava/lang/String;)(e); 71 | }; 72 | }-*/; 73 | 74 | private native int nativeGetReadyState() /*-{ 75 | return this.readyState; 76 | }-*/; 77 | // @formatter:on 78 | 79 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/impl/MessageImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.impl; 15 | 16 | import com.goodow.realtime.channel.Bus; 17 | import com.goodow.realtime.channel.Message; 18 | import com.goodow.realtime.core.Handler; 19 | 20 | class MessageImpl implements Message { 21 | protected U body; 22 | protected Bus bus; 23 | protected String topic; 24 | protected String replyTopic; 25 | protected boolean send; // Is it a send or a publish? 26 | protected boolean local; 27 | 28 | public MessageImpl(boolean local, boolean send, Bus bus, String topic, String replyTopic, 29 | U body) { 30 | this.local = local; 31 | this.send = send; 32 | this.bus = bus; 33 | this.topic = topic; 34 | this.replyTopic = replyTopic; 35 | this.body = body; 36 | } 37 | 38 | @Override 39 | public String topic() { 40 | return topic; 41 | } 42 | 43 | @Override 44 | public U body() { 45 | return body; 46 | } 47 | 48 | @Override 49 | public void fail(int failureCode, String msg) { 50 | // sendReply(new ReplyException(ReplyFailure.RECIPIENT_FAILURE, failureCode, message), null); 51 | } 52 | 53 | @Override 54 | public boolean isLocal() { 55 | return local; 56 | } 57 | 58 | @Override 59 | public void reply(Object msg) { 60 | sendReply(msg, null); 61 | } 62 | 63 | @Override 64 | public void reply(Object msg, Handler> replyHandler) { 65 | sendReply(msg, replyHandler); 66 | } 67 | 68 | @Override 69 | public String replyTopic() { 70 | return replyTopic; 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return body == null ? null : body.toString(); 76 | } 77 | 78 | private void sendReply(Object msg, Handler> replyHandler) { 79 | if (bus != null && replyTopic != null) { 80 | // Send back reply 81 | if (local) { 82 | bus.sendLocal(replyTopic, msg, replyHandler); 83 | } else { 84 | bus.send(replyTopic, msg, replyHandler); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/impl/BusProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.impl; 15 | 16 | import com.goodow.realtime.channel.Bus; 17 | import com.goodow.realtime.channel.BusHook; 18 | import com.goodow.realtime.channel.Message; 19 | import com.goodow.realtime.channel.State; 20 | import com.goodow.realtime.core.Handler; 21 | import com.goodow.realtime.core.Registration; 22 | 23 | public abstract class BusProxy implements Bus { 24 | protected final Bus delegate; 25 | protected BusHook hook; 26 | 27 | public BusProxy(Bus delegate) { 28 | this.delegate = delegate; 29 | } 30 | 31 | @Override 32 | public void close() { 33 | delegate.close(); 34 | } 35 | 36 | public Bus getDelegate() { 37 | return delegate; 38 | } 39 | 40 | @Override 41 | public State getReadyState() { 42 | return delegate.getReadyState(); 43 | } 44 | 45 | @Override 46 | public String getSessionId() { 47 | return delegate.getSessionId(); 48 | } 49 | 50 | @Override 51 | public Bus publish(String topic, Object msg) { 52 | return delegate.publish(topic, msg); 53 | } 54 | 55 | @Override 56 | public Bus publishLocal(String topic, Object msg) { 57 | return delegate.publishLocal(topic, msg); 58 | } 59 | 60 | @SuppressWarnings("rawtypes") 61 | @Override 62 | public Registration subscribe(String topic, Handler handler) { 63 | return delegate.subscribe(topic, handler); 64 | } 65 | 66 | @SuppressWarnings("rawtypes") 67 | @Override 68 | public Registration subscribeLocal(String topic, Handler handler) { 69 | return delegate.subscribeLocal(topic, handler); 70 | } 71 | 72 | @Override 73 | public Bus send(String topic, Object msg, Handler> replyHandler) { 74 | return delegate.send(topic, msg, replyHandler); 75 | } 76 | 77 | @Override 78 | public Bus sendLocal(String topic, Object msg, Handler> replyHandler) { 79 | return delegate.sendLocal(topic, msg, replyHandler); 80 | } 81 | 82 | @Override 83 | public Bus setHook(BusHook hook) { 84 | this.hook = hook; 85 | return this; 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/server/ChannelBridge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server; 15 | 16 | import org.vertx.java.core.AsyncResult; 17 | import org.vertx.java.core.AsyncResultHandler; 18 | import org.vertx.java.core.Vertx; 19 | import org.vertx.java.core.http.HttpServer; 20 | import org.vertx.java.core.impl.CountingCompletionHandler; 21 | import org.vertx.java.core.json.JsonArray; 22 | import org.vertx.java.core.json.JsonObject; 23 | import org.vertx.java.core.sockjs.EventBusBridgeHook; 24 | import org.vertx.java.core.sockjs.SockJSServer; 25 | 26 | public class ChannelBridge { 27 | private final Vertx vertx; 28 | private final JsonObject config; 29 | private EventBusBridgeHook hook; 30 | 31 | public ChannelBridge(Vertx vertx, JsonObject config) { 32 | this.vertx = vertx; 33 | this.config = config; 34 | } 35 | 36 | public void bridge(final CountingCompletionHandler countDownLatch) { 37 | HttpServer server = vertx.createHttpServer(); 38 | SockJSServer sjsServer = vertx.createSockJSServer(server).setHook(hook); 39 | JsonObject empty = new JsonObject(); 40 | JsonArray all = new JsonArray().add(empty); 41 | JsonArray inboundPermitted = config.getArray("inbound_permitted", all); 42 | JsonArray outboundPermitted = config.getArray("outbound_permitted", all); 43 | 44 | sjsServer.bridge(config.getObject("sjs_config", new JsonObject() 45 | .putString("prefix", "/channel")), inboundPermitted, outboundPermitted, config.getObject( 46 | "bridge_config", empty)); 47 | 48 | countDownLatch.incRequired(); 49 | server.listen(config.getInteger("port", 1986), config.getString("host", "0.0.0.0"), 50 | new AsyncResultHandler() { 51 | @Override 52 | public void handle(AsyncResult ar) { 53 | if (!ar.succeeded()) { 54 | countDownLatch.failed(ar.cause()); 55 | } else { 56 | countDownLatch.complete(); 57 | } 58 | } 59 | }); 60 | } 61 | 62 | public ChannelBridge setHook(EventBusBridgeHook hook) { 63 | this.hook = hook; 64 | return this; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/server/impl/VertxMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server.impl; 15 | 16 | import com.goodow.realtime.channel.BusHook; 17 | import com.goodow.realtime.channel.Message; 18 | import com.goodow.realtime.core.Handler; 19 | 20 | class VertxMessage implements Message { 21 | private final VertxBus bus; 22 | private final org.vertx.java.core.eventbus.Message delegate; 23 | 24 | public VertxMessage(VertxBus bus, org.vertx.java.core.eventbus.Message delegate) { 25 | this.bus = bus; 26 | this.delegate = delegate; 27 | } 28 | 29 | @Override 30 | public String topic() { 31 | return delegate.address(); 32 | } 33 | 34 | @SuppressWarnings("unchecked") 35 | @Override 36 | public T body() { 37 | return (T) VertxBus.unwrapMsg(delegate.body()); 38 | } 39 | 40 | @Override 41 | public void fail(int failureCode, String msg) { 42 | delegate.fail(failureCode, msg); 43 | } 44 | 45 | @Override 46 | public boolean isLocal() { 47 | return false; 48 | } 49 | 50 | @Override 51 | public void reply(Object msg) { 52 | reply(msg, null); 53 | } 54 | 55 | @SuppressWarnings("hiding") 56 | @Override 57 | public void reply(Object msg, final Handler> replyHandler) { 58 | BusHook hook = bus.getHook(); 59 | if (hook == null || hook.handleSendOrPub(true, replyTopic(), msg, replyHandler)) { 60 | org.vertx.java.core.Handler> handler = 61 | replyHandler == null ? null 62 | : new org.vertx.java.core.Handler>() { 63 | @Override 64 | public void handle(org.vertx.java.core.eventbus.Message message) { 65 | VertxMessage event = new VertxMessage(bus, message); 66 | BusHook hook = bus.getHook(); 67 | if (hook == null || hook.handleReceiveMessage(event)) { 68 | replyHandler.handle(event); 69 | } 70 | } 71 | }; 72 | delegate.reply(VertxBus.wrapMsg(msg), handler); 73 | } 74 | } 75 | 76 | @Override 77 | public String replyTopic() { 78 | return delegate.replyAddress(); 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/html/HtmlScheduler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.html; 15 | 16 | import com.goodow.realtime.core.Handler; 17 | import com.goodow.realtime.core.Scheduler; 18 | import com.goodow.realtime.json.Json; 19 | import com.goodow.realtime.json.JsonObject; 20 | 21 | import com.google.gwt.core.client.Scheduler.RepeatingCommand; 22 | import com.google.gwt.core.client.Scheduler.ScheduledCommand; 23 | 24 | class HtmlScheduler implements Scheduler { 25 | // @formatter:off 26 | private static native void nativeHandle(Object handler, T event) /*-{ 27 | handler(event); 28 | }-*/; 29 | // @formatter:on 30 | 31 | private int timerId = -1; 32 | private final JsonObject timers = Json.createObject(); 33 | 34 | @Override 35 | public boolean cancelTimer(int id) { 36 | String key = "" + id; 37 | if (timers.has(key)) { 38 | timers.remove(key); 39 | return true; 40 | } 41 | return false; 42 | } 43 | 44 | @SuppressWarnings("unchecked") 45 | @Override 46 | public void handle(Object handler, Object event) { 47 | if (handler instanceof Handler) { 48 | ((Handler) handler).handle(event); 49 | } else { 50 | nativeHandle(handler, event); 51 | } 52 | } 53 | 54 | @Override 55 | public void scheduleDeferred(final Handler handler) { 56 | com.google.gwt.core.client.Scheduler.get().scheduleDeferred(new ScheduledCommand() { 57 | @Override 58 | public void execute() { 59 | handler.handle(null); 60 | } 61 | }); 62 | } 63 | 64 | @Override 65 | public int scheduleDelay(int delayMs, final Handler handler) { 66 | final String key = "" + (++timerId); 67 | RepeatingCommand cmd = new RepeatingCommand() { 68 | @Override 69 | public boolean execute() { 70 | if (timers.has(key)) { 71 | timers.remove(key); 72 | handler.handle(null); 73 | } 74 | return false; 75 | } 76 | }; 77 | timers.set(key, cmd); 78 | com.google.gwt.core.client.Scheduler.get().scheduleFixedDelay(cmd, delayMs); 79 | return timerId; 80 | } 81 | 82 | @Override 83 | public int schedulePeriodic(int delayMs, final Handler handler) { 84 | final String key = "" + (++timerId); 85 | RepeatingCommand cmd = new RepeatingCommand() { 86 | @Override 87 | public boolean execute() { 88 | if (timers.has(key)) { 89 | handler.handle(null); 90 | return true; 91 | } 92 | return false; 93 | } 94 | }; 95 | timers.set(key, cmd); 96 | com.google.gwt.core.client.Scheduler.get().scheduleFixedPeriod(cmd, delayMs); 97 | return timerId; 98 | } 99 | } -------------------------------------------------------------------------------- /src/test/java/com/goodow/realtime/channel/server/VertxBusTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server; 15 | 16 | import com.goodow.realtime.channel.Bus; 17 | import com.goodow.realtime.channel.Message; 18 | import com.goodow.realtime.channel.MessageHandler; 19 | import com.goodow.realtime.channel.server.impl.VertxBus; 20 | import com.goodow.realtime.core.Handler; 21 | import com.goodow.realtime.json.Json; 22 | import com.goodow.realtime.json.JsonObject; 23 | 24 | import static org.vertx.testtools.VertxAssert.assertNotNull; 25 | import static org.vertx.testtools.VertxAssert.assertTrue; 26 | 27 | import org.junit.Test; 28 | import org.vertx.java.core.AsyncResult; 29 | import org.vertx.java.core.AsyncResultHandler; 30 | import org.vertx.testtools.TestVerticle; 31 | import org.vertx.testtools.VertxAssert; 32 | 33 | public class VertxBusTest extends TestVerticle { 34 | 35 | private Bus bus; 36 | 37 | @Override 38 | public void start() { 39 | initialize(); 40 | 41 | // Deploy the module - the System property `vertx.modulename` will contain the name of the 42 | // module so you don't have to hardecode it in your tests 43 | container.deployModule(System.getProperty("vertx.modulename"), 44 | new AsyncResultHandler() { 45 | @Override 46 | public void handle(AsyncResult asyncResult) { 47 | assertTrue(asyncResult.succeeded()); 48 | assertNotNull("deploymentID should not be null", asyncResult.result()); 49 | 50 | bus = new VertxBus(vertx.eventBus()); 51 | startTests(); 52 | } 53 | }); 54 | } 55 | 56 | @Test 57 | public void test() { 58 | bus.subscribe("somea/topic", new MessageHandler() { 59 | @Override 60 | public void handle(Message message) { 61 | VertxAssert.assertEquals("send1", message.body().getString("text")); 62 | 63 | JsonObject o1 = Json.createObject().set("text", "reply1"); 64 | message.reply(o1, new Handler>() { 65 | @Override 66 | public void handle(Message message) { 67 | VertxAssert.assertEquals("reply2", message.body().getString("text")); 68 | VertxAssert.assertNull(message.replyTopic()); 69 | 70 | bus.close(); 71 | VertxAssert.testComplete(); 72 | } 73 | }); 74 | } 75 | }); 76 | 77 | JsonObject o1 = Json.createObject().set("text", "send1"); 78 | bus.send("somea/topic", o1, new Handler>() { 79 | @Override 80 | public void handle(Message message) { 81 | VertxAssert.assertEquals("reply1", message.body().getString("text")); 82 | 83 | JsonObject o1 = Json.createObject().set("text", "reply2"); 84 | message.reply(o1, null); 85 | } 86 | }); 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/util/FuzzingBackOffGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.util; 15 | 16 | /** 17 | * Simple fibonacci back off with fuzzing. This is important for reconnection so that everyone 18 | * doesn't retry at the same time. 19 | */ 20 | public class FuzzingBackOffGenerator { 21 | public static class BackOffParameters { 22 | public final int targetDelay; 23 | public final int minimumDelay; 24 | 25 | private BackOffParameters(int targetDelay, int minimumDelay) { 26 | this.targetDelay = targetDelay; 27 | this.minimumDelay = minimumDelay; 28 | } 29 | } 30 | 31 | /** Randomization factor. Must be between 0 and 1. */ 32 | private final double randomizationFactor; 33 | /** The first time we back off. */ 34 | private final int initialBackOff; 35 | /** The max back off value, it'll be fuzzed. */ 36 | private final int maxBackOff; 37 | /** The next time we've backed off. */ 38 | private int nextBackOffTime; 39 | /** The current back off time. */ 40 | private int backOffTime; 41 | 42 | /** 43 | * @param initialBackOff Initial value to back off. This class does not interpret the meaning of 44 | * this value. must be > 0 45 | * @param maxBackOff Max value to back off 46 | * @param randomizationFactor between 0 and 1 to control the range of randomness. 47 | */ 48 | public FuzzingBackOffGenerator(int initialBackOff, int maxBackOff, double randomizationFactor) { 49 | if (randomizationFactor < 0 || randomizationFactor > 1) { 50 | throw new IllegalArgumentException("randomizationFactor must be between 0 and 1. actual " 51 | + randomizationFactor); 52 | } 53 | 54 | if (initialBackOff <= 0) { 55 | throw new IllegalArgumentException("initialBackOff must be between 0 and 1. actual " 56 | + initialBackOff); 57 | } 58 | 59 | this.randomizationFactor = randomizationFactor; 60 | this.initialBackOff = initialBackOff; 61 | this.maxBackOff = maxBackOff; 62 | this.nextBackOffTime = initialBackOff; 63 | this.backOffTime = 0; 64 | } 65 | 66 | /** Gets the next back off time. Until maxBackOff is reached. */ 67 | public BackOffParameters next() { 68 | int ret = Math.min(nextBackOffTime, maxBackOff); 69 | nextBackOffTime += backOffTime; 70 | if (nextBackOffTime <= 0) { 71 | nextBackOffTime = Integer.MAX_VALUE; 72 | } 73 | backOffTime = ret; 74 | 75 | int randomizeTime = (int) (backOffTime * (1.0 + (Math.random() * randomizationFactor))); 76 | int minAllowedTime = (int) Math.round(randomizeTime - backOffTime * randomizationFactor); 77 | 78 | return new BackOffParameters(randomizeTime, minAllowedTime); 79 | } 80 | 81 | /** 82 | * Resets the back off. 83 | */ 84 | public void reset() { 85 | nextBackOffTime = initialBackOff; 86 | backOffTime = 0; 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/objc/ObjCScheduler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.objc; 15 | 16 | import com.goodow.realtime.core.Handler; 17 | import com.goodow.realtime.core.Scheduler; 18 | import com.goodow.realtime.json.Json; 19 | import com.goodow.realtime.json.JsonObject; 20 | 21 | import java.util.concurrent.atomic.AtomicInteger; 22 | 23 | /*-[ 24 | #import "GDChannel.h" 25 | ]-*/ 26 | class ObjCScheduler implements Scheduler { 27 | // @formatter:off 28 | private static native void nativeCancelTimer(Object timer) /*-[ 29 | [(NSTimer *)timer invalidate]; 30 | ]-*/; 31 | 32 | private static native void nativeHandle(Object handler, T event) /*-[ 33 | GDCMessageHandler block = (GDCMessageHandler)handler; 34 | block(event); 35 | ]-*/; 36 | 37 | private static native Object nativeScheduleTimer(int delayMs, boolean repeat, 38 | Handler handler) /*-[ 39 | return 40 | [NSTimer scheduledTimerWithTimeInterval:delayMs/1000 41 | target:handler 42 | selector:@selector(handleWithId:) 43 | userInfo:nil 44 | repeats:repeat]; 45 | ]-*/; 46 | // @formatter:on 47 | 48 | private final AtomicInteger timerId = new AtomicInteger(0); 49 | private final JsonObject timers = Json.createObject(); 50 | 51 | @Override 52 | public boolean cancelTimer(int id) { 53 | String key = "" + id; 54 | if (timers.has(key)) { 55 | nativeCancelTimer(timers.get(key)); 56 | timers.remove(key); 57 | return true; 58 | } 59 | return false; 60 | } 61 | 62 | @SuppressWarnings("unchecked") 63 | @Override 64 | public void handle(Object handler, Object event) { 65 | if (handler instanceof Handler) { 66 | ((Handler) handler).handle(event); 67 | } else { 68 | nativeHandle(handler, event); 69 | } 70 | } 71 | 72 | @Override 73 | // @formatter:off 74 | public native void scheduleDeferred(Handler handler) /*-[ 75 | [[NSRunLoop mainRunLoop] performSelector:@selector(handleWithId:) 76 | target:handler 77 | argument:nil 78 | order:0 79 | modes:@[NSDefaultRunLoopMode]]; 80 | ]-*/; 81 | // @formatter:on 82 | 83 | @Override 84 | public int scheduleDelay(int delayMs, final Handler handler) { 85 | final int id = timerId.getAndIncrement(); 86 | timers.set("" + id, nativeScheduleTimer(delayMs, false, handler)); 87 | return id; 88 | } 89 | 90 | @Override 91 | public int schedulePeriodic(int delayMs, Handler handler) { 92 | final int id = timerId.getAndIncrement(); 93 | timers.set("" + id, nativeScheduleTimer(delayMs, true, handler)); 94 | return id; 95 | } 96 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/core/impl/FutureResultImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.core.impl; 15 | 16 | import com.goodow.realtime.core.AsyncResult; 17 | import com.goodow.realtime.core.Future; 18 | import com.goodow.realtime.core.Handler; 19 | 20 | public class FutureResultImpl implements Future { 21 | private boolean failed; 22 | private boolean succeeded; 23 | private Handler> handler; 24 | private T result; 25 | private Throwable throwable; 26 | 27 | /** 28 | * Create a AsyncResult that hasn't completed yet 29 | */ 30 | public FutureResultImpl() { 31 | } 32 | 33 | /** 34 | * Create a AsyncResult that has already succeeded 35 | * 36 | * @param result The result 37 | */ 38 | public FutureResultImpl(T result) { 39 | setResult(result); 40 | } 41 | 42 | /** 43 | * Create a AsyncResult that has already completed 44 | * 45 | * @param t The Throwable or null if succeeded 46 | */ 47 | public FutureResultImpl(Throwable t) { 48 | if (t == null) { 49 | setResult(null); 50 | } else { 51 | setFailure(t); 52 | } 53 | } 54 | 55 | /** 56 | * An exception describing failure. This will be null if the operation succeeded. 57 | */ 58 | @Override 59 | public Throwable cause() { 60 | return throwable; 61 | } 62 | 63 | /** 64 | * Has it completed? 65 | */ 66 | @Override 67 | public boolean complete() { 68 | return failed || succeeded; 69 | } 70 | 71 | /** 72 | * Did it fail? 73 | */ 74 | @Override 75 | public boolean failed() { 76 | return failed; 77 | } 78 | 79 | /** 80 | * The result of the operation. This will be null if the operation failed. 81 | */ 82 | @Override 83 | public T result() { 84 | return result; 85 | } 86 | 87 | /** 88 | * Set the failure. Any handler will be called, if there is one 89 | */ 90 | @Override 91 | public FutureResultImpl setFailure(Throwable throwable) { 92 | this.throwable = throwable; 93 | failed = true; 94 | checkCallHandler(); 95 | return this; 96 | } 97 | 98 | /** 99 | * Set a handler for the result. It will get called when it's complete 100 | */ 101 | @Override 102 | public FutureResultImpl setHandler(Handler> handler) { 103 | this.handler = handler; 104 | checkCallHandler(); 105 | return this; 106 | } 107 | 108 | /** 109 | * Set the result. Any handler will be called, if there is one 110 | */ 111 | @Override 112 | public FutureResultImpl setResult(T result) { 113 | this.result = result; 114 | succeeded = true; 115 | checkCallHandler(); 116 | return this; 117 | } 118 | 119 | /** 120 | * Did it succeeed? 121 | */ 122 | public boolean succeeded() { 123 | return succeeded; 124 | } 125 | 126 | private void checkCallHandler() { 127 | if (handler != null && complete()) { 128 | handler.handle(this); 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/server/impl/BridgeHook.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server.impl; 15 | 16 | import com.goodow.realtime.channel.impl.WebSocketBus; 17 | 18 | import org.vertx.java.core.AsyncResult; 19 | import org.vertx.java.core.Handler; 20 | import org.vertx.java.core.Vertx; 21 | import org.vertx.java.core.eventbus.EventBus; 22 | import org.vertx.java.core.json.JsonObject; 23 | import org.vertx.java.core.shareddata.SharedData; 24 | import org.vertx.java.core.sockjs.EventBusBridgeHook; 25 | import org.vertx.java.core.sockjs.SockJSSocket; 26 | 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | import java.util.Set; 30 | 31 | public class BridgeHook implements EventBusBridgeHook { 32 | public static final String TOPIC = "topic"; 33 | public static final String SESSION_WATCH_ADDR = 34 | WebSocketBus.TOPIC_CHANNEL + "/" + WebSocketBus.SESSION + "/_watch"; 35 | private final EventBus eb; 36 | private final Map connections = new HashMap(); 37 | private final SharedData sharedData; 38 | 39 | public static String getSessionsKey(String topic) { 40 | return WebSocketBus.TOPIC_CHANNEL + "/" + topic + "/" + WebSocketBus.SESSION; 41 | } 42 | 43 | public BridgeHook(Vertx vertx) { 44 | this.eb = vertx.eventBus(); 45 | this.sharedData = vertx.sharedData(); 46 | } 47 | 48 | @Override 49 | public void handlePostRegister(SockJSSocket sock, String topic) { 50 | Set sessionIds = sharedData.getSet(getSessionsKey(topic)); 51 | String sessionId = connections.get(sock.writeHandlerID()); 52 | sessionIds.add(sessionId); 53 | publishPresence(topic, sessionId, true); 54 | } 55 | 56 | @Override 57 | public boolean handlePreRegister(SockJSSocket sock, String topic) { 58 | return true; 59 | } 60 | 61 | @Override 62 | public boolean handleSendOrPub(SockJSSocket sock, boolean send, JsonObject msg, 63 | final String topic) { 64 | if (WebSocketBus.TOPIC_CONNECT.equals(topic)) { 65 | connections.put(sock.writeHandlerID(), msg.getObject("body").getString(WebSocketBus.SESSION)); 66 | } else if (msg.getValue("body") instanceof JsonObject) { 67 | JsonObject body = msg.getObject("body"); 68 | if (!body.containsField(WebSocketBus.SESSION)) { 69 | body.putString(WebSocketBus.SESSION, connections.get(sock.writeHandlerID())); 70 | } 71 | } 72 | return true; 73 | } 74 | 75 | @Override 76 | public void handleSocketClosed(SockJSSocket sock) { 77 | connections.remove(sock.writeHandlerID()); 78 | } 79 | 80 | @Override 81 | public boolean handleSocketCreated(SockJSSocket sock) { 82 | return true; 83 | } 84 | 85 | @Override 86 | public boolean handleUnregister(SockJSSocket sock, String topic) { 87 | Set sessionIds = sharedData.getSet(getSessionsKey(topic)); 88 | String sessionId = connections.get(sock.writeHandlerID()); 89 | sessionIds.remove(sessionId); 90 | publishPresence(topic, sessionId, false); 91 | return true; 92 | } 93 | 94 | @Override 95 | public boolean handleAuthorise(JsonObject message, String sessionID, 96 | Handler> handler) { 97 | return false; 98 | } 99 | 100 | private void publishPresence(final String topic, String sessionId, final boolean isJoined) { 101 | JsonObject msg = new JsonObject().putString(WebSocketBus.SESSION, sessionId).putBoolean( 102 | "isJoined", isJoined).putString(TOPIC, topic); 103 | eb.publish(SESSION_WATCH_ADDR, msg); 104 | } 105 | } -------------------------------------------------------------------------------- /src/test/java/com/goodow/realtime/channel/WebSocketBusTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel; 15 | 16 | import com.goodow.realtime.channel.impl.ReconnectBus; 17 | import com.goodow.realtime.channel.server.impl.VertxPlatform; 18 | import com.goodow.realtime.core.Handler; 19 | import com.goodow.realtime.json.Json; 20 | import com.goodow.realtime.json.JsonObject; 21 | 22 | import static org.vertx.testtools.VertxAssert.assertNotNull; 23 | import static org.vertx.testtools.VertxAssert.assertTrue; 24 | 25 | import org.junit.Test; 26 | import org.vertx.java.core.AsyncResult; 27 | import org.vertx.java.core.AsyncResultHandler; 28 | import org.vertx.testtools.TestVerticle; 29 | import org.vertx.testtools.VertxAssert; 30 | 31 | import java.util.logging.Level; 32 | import java.util.logging.Logger; 33 | 34 | public class WebSocketBusTest extends TestVerticle { 35 | 36 | private static final Logger log = Logger.getLogger(WebSocketBusTest.class.getName()); 37 | private Bus bus; 38 | 39 | @Override 40 | public void start() { 41 | initialize(); 42 | VertxPlatform.register(vertx); 43 | 44 | // Deploy the module - the System property `vertx.modulename` will contain the name of the 45 | // module so you don't have to hardecode it in your tests 46 | container.deployModule(System.getProperty("vertx.modulename"), 47 | new AsyncResultHandler() { 48 | @Override 49 | public void handle(AsyncResult asyncResult) { 50 | assertTrue(asyncResult.succeeded()); 51 | assertNotNull("deploymentID should not be null", asyncResult.result()); 52 | 53 | bus = new ReconnectBus("ws://localhost:1986/channel/websocket", null); 54 | startTests(); 55 | } 56 | }); 57 | } 58 | 59 | @Test 60 | public void test() { 61 | bus.subscribeLocal(Bus.ON_OPEN, new MessageHandler() { 62 | @Override 63 | public void handle(Message message) { 64 | log.info("EventBus opened"); 65 | } 66 | }); 67 | bus.subscribeLocal(Bus.ON_CLOSE, new MessageHandler() { 68 | @Override 69 | public void handle(Message message) { 70 | log.info("EventBus closed"); 71 | } 72 | }); 73 | bus.subscribeLocal(Bus.ON_ERROR, new MessageHandler() { 74 | @Override 75 | public void handle(Message message) { 76 | log.log(Level.SEVERE, "EventBus Error"); 77 | } 78 | }); 79 | 80 | JsonObject o1 = Json.createObject().set("text", "send1"); 81 | bus.send("some/topic", o1, new Handler>() { 82 | @Override 83 | public void handle(Message message) { 84 | VertxAssert.assertEquals("reply1", message.body().getString("text")); 85 | 86 | JsonObject o1 = Json.createObject().set("text", "reply2"); 87 | message.reply(o1, null); 88 | } 89 | }); 90 | 91 | bus.subscribe("some/topic", new MessageHandler() { 92 | @Override 93 | public void handle(Message message) { 94 | VertxAssert.assertEquals("send1", message.body().getString("text")); 95 | 96 | JsonObject o1 = Json.createObject().set("text", "reply1"); 97 | message.reply(o1, new Handler>() { 98 | @Override 99 | public void handle(Message message) { 100 | VertxAssert.assertEquals("reply2", message.body().getString("text")); 101 | VertxAssert.assertNull(message.replyTopic()); 102 | 103 | bus.close(); 104 | VertxAssert.testComplete(); 105 | } 106 | }); 107 | } 108 | }); 109 | } 110 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/server/impl/VertxWebSocket.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server.impl; 15 | 16 | import com.goodow.realtime.channel.State; 17 | import com.goodow.realtime.core.WebSocket; 18 | import com.goodow.realtime.json.Json; 19 | 20 | import org.vertx.java.core.Handler; 21 | import org.vertx.java.core.Vertx; 22 | import org.vertx.java.core.buffer.Buffer; 23 | import org.vertx.java.core.http.HttpClient; 24 | 25 | import java.net.URI; 26 | import java.net.URISyntaxException; 27 | import java.util.logging.Level; 28 | import java.util.logging.Logger; 29 | 30 | class VertxWebSocket implements WebSocket { 31 | private static final Logger log = Logger.getLogger(VertxWebSocket.class.getName()); 32 | 33 | private org.vertx.java.core.http.WebSocket socket; 34 | private WebSocketHandler eventHandler; 35 | private State state; 36 | 37 | VertxWebSocket(Vertx vertx, String uri) { 38 | state = State.CONNECTING; 39 | URI serverUri = null; 40 | try { 41 | serverUri = new URI(uri); 42 | } catch (URISyntaxException e) { 43 | throw new RuntimeException(e); 44 | } 45 | 46 | HttpClient client = 47 | vertx.createHttpClient().setHost(serverUri.getHost()).setPort(serverUri.getPort()); 48 | 49 | client.connectWebsocket(serverUri.getPath(), new Handler() { 50 | @Override 51 | public void handle(org.vertx.java.core.http.WebSocket ws) { 52 | state = State.OPEN; 53 | socket = ws; 54 | log.info("Websocket Connected"); 55 | 56 | socket.closeHandler(new Handler() { 57 | @Override 58 | public void handle(Void event) { 59 | log.info("WebSocket closed"); 60 | state = State.CLOSED; 61 | if (eventHandler == null) { 62 | return; 63 | } 64 | eventHandler.onClose(Json.createObject()); 65 | socket = null; 66 | } 67 | }); 68 | 69 | socket.dataHandler(new Handler() { 70 | @Override 71 | public void handle(Buffer buffer) { 72 | log.finest("Websocket received: " + buffer); 73 | if (eventHandler == null) { 74 | return; 75 | } 76 | eventHandler.onMessage(buffer.toString()); 77 | } 78 | }); 79 | 80 | socket.exceptionHandler(new Handler() { 81 | @Override 82 | public void handle(Throwable e) { 83 | log.log(Level.SEVERE, "Websocket Failed With Exception", e); 84 | state = State.CLOSING; 85 | if (eventHandler == null) { 86 | return; 87 | } 88 | String message = e.getMessage(); 89 | eventHandler.onError(message == null ? e.getClass().getSimpleName() : message); 90 | } 91 | }); 92 | 93 | if (eventHandler != null) { 94 | eventHandler.onOpen(); 95 | } 96 | } 97 | }); 98 | } 99 | 100 | @Override 101 | public void close() { 102 | state = State.CLOSING; 103 | if (socket != null) { 104 | socket.close(); 105 | } 106 | } 107 | 108 | @Override 109 | public State getReadyState() { 110 | return state; 111 | } 112 | 113 | @Override 114 | public void send(String data) { 115 | if (socket == null) { 116 | log.warning("WebSocket is closed"); 117 | return; 118 | } 119 | try { 120 | log.finest("Websocket send: " + data); 121 | socket.writeTextFrame(data); 122 | } catch (Throwable e) { 123 | throw new RuntimeException(e); 124 | } 125 | } 126 | 127 | @Override 128 | public void setListen(WebSocketHandler handler) { 129 | this.eventHandler = handler; 130 | } 131 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/impl/ReconnectBus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.impl; 15 | 16 | import com.google.gwt.core.client.js.JsExport; 17 | import com.google.gwt.core.client.js.JsNamespace; 18 | 19 | import com.goodow.realtime.channel.Bus; 20 | import com.goodow.realtime.channel.BusHook; 21 | import com.goodow.realtime.channel.State; 22 | import com.goodow.realtime.channel.util.FuzzingBackOffGenerator; 23 | import com.goodow.realtime.core.Handler; 24 | import com.goodow.realtime.core.Platform; 25 | import com.goodow.realtime.json.Json; 26 | import com.goodow.realtime.json.JsonArray; 27 | import com.goodow.realtime.json.JsonObject; 28 | 29 | @JsNamespace("$wnd.realtime.channel") 30 | @JsExport 31 | public class ReconnectBus extends WebSocketBus { 32 | public static final String AUTO_RECONNECT = "reconnect"; 33 | private final FuzzingBackOffGenerator backOffGenerator; 34 | private BusHook hook; 35 | private boolean reconnect; 36 | private final JsonArray queuedMessages = Json.createArray(); // ArrayList() 37 | private final JsonObject options; 38 | 39 | @JsExport 40 | public ReconnectBus(String serverUri, JsonObject options) { 41 | super(serverUri, options); 42 | this.options = options; 43 | reconnect = 44 | options == null || !options.has(AUTO_RECONNECT) ? true : options.getBoolean(AUTO_RECONNECT); 45 | backOffGenerator = new FuzzingBackOffGenerator(1 * 1000, 30 * 60 * 1000, 0.5); 46 | 47 | super.setHook(new BusHookProxy() { 48 | @Override 49 | public void handleOpened() { 50 | backOffGenerator.reset(); 51 | 52 | handlerCount.keys().forEach(new JsonArray.ListIterator() { 53 | @Override 54 | public void call(int index, String topic) { 55 | assert handlerCount.getNumber(topic) > 0 : "Handlers registried on " + topic 56 | + " shouldn't be empty"; 57 | sendUnsubscribe(topic); 58 | sendSubscribe(topic); 59 | } 60 | }); 61 | 62 | if (queuedMessages.length() > 0) { 63 | JsonArray copy = queuedMessages.copy(); 64 | queuedMessages.clear(); 65 | // Drain any messages that came in while the channel was not open. 66 | copy.forEach(new JsonArray.ListIterator() { 67 | @Override 68 | public void call(int index, JsonObject msg) { 69 | send(msg); 70 | } 71 | }); 72 | } 73 | super.handleOpened(); 74 | } 75 | 76 | @Override 77 | public void handlePostClose() { 78 | if (reconnect) { 79 | Platform.scheduler().scheduleDelay(backOffGenerator.next().targetDelay, 80 | new Handler() { 81 | @Override 82 | public void handle(Void event) { 83 | if (reconnect) { 84 | reconnect(); 85 | } 86 | } 87 | }); 88 | } 89 | super.handlePostClose(); 90 | } 91 | 92 | @Override 93 | protected BusHook delegate() { 94 | return hook; 95 | } 96 | }); 97 | } 98 | 99 | public void reconnect() { 100 | if (getReadyState() == State.OPEN || getReadyState() == State.CONNECTING) { 101 | return; 102 | } 103 | if (webSocket != null) { 104 | webSocket.close(); 105 | } 106 | 107 | connect(serverUri, options); 108 | } 109 | 110 | @Override 111 | public Bus setHook(BusHook hook) { 112 | this.hook = hook; 113 | return this; 114 | } 115 | 116 | @Override 117 | protected void doClose() { 118 | reconnect = false; 119 | backOffGenerator.reset(); 120 | queuedMessages.clear(); 121 | super.doClose(); 122 | } 123 | 124 | @Override 125 | protected void send(JsonObject msg) { 126 | if (getReadyState() == State.OPEN) { 127 | super.send(msg); 128 | return; 129 | } 130 | if (reconnect) { 131 | reconnect(); 132 | } 133 | String type = msg.getString(WebSocketBus.TYPE); 134 | if ("ping".equals(type) || "register".equals(type)) { 135 | return; 136 | } 137 | queuedMessages.push(msg); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/Bus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel; 15 | 16 | import com.google.gwt.core.client.js.JsType; 17 | 18 | import com.goodow.realtime.core.Handler; 19 | import com.goodow.realtime.core.Registration; 20 | 21 | /** 22 | * A distributed lightweight event bus which can encompass multiple machines. The event bus 23 | * implements publish/subscribe, point to point messaging and request-response messaging.

24 | * Messages sent over the event bus are represented by instances of the {@link Message} class.

25 | * For publish/subscribe, messages can be published to a topic using one of the {@link #publish} 26 | * methods. A topic is a simple {@code String} instance.

27 | * Handlers are registered against a topic. There can be multiple handlers registered against 28 | * each topic, and a particular handler can be registered against multiple topics. The event 29 | * bus will route a sent message to all handlers which are registered against that topic.

30 | * For point to point messaging, messages can be sent to a topic using one of the {@link #send} 31 | * methods. The messages will be delivered to a single handler, if one is registered on that 32 | * topic. If more than one handler is registered on the same topic, the bus will choose one and 33 | * deliver the message to that. The bus will aim to fairly distribute messages in a round-robin way, 34 | * but does not guarantee strict round-robin under all circumstances.

35 | * The order of messages received by any specific handler from a specific sender should match the 36 | * order of messages sent from that sender.

37 | * When sending a message, a reply handler can be provided. If so, it will be called when the reply 38 | * from the receiver has been received. Reply messages can also be replied to, etc, ad infinitum

39 | * Different event bus instances can be clustered together over a network, to give a single logical 40 | * event bus.

41 | */ 42 | @JsType 43 | public interface Bus { 44 | String ON_OPEN = "@realtime/bus/onOpen"; 45 | String ON_CLOSE = "@realtime/bus/onClose"; 46 | String ON_ERROR = "@realtime/bus/onError"; 47 | 48 | /** 49 | * Close the Bus and release all resources. 50 | */ 51 | void close(); 52 | 53 | /* The state of the Bus. */ 54 | State getReadyState(); 55 | 56 | /* Returns the session ID used by this bus. */ 57 | String getSessionId(); 58 | 59 | /** 60 | * Publish a message 61 | * 62 | * @param topic The topic to publish it to 63 | * @param msg The message 64 | */ 65 | Bus publish(String topic, Object msg); 66 | 67 | /** 68 | * Publish a local message 69 | * 70 | * @param topic The topic to publish it to 71 | * @param msg The message 72 | */ 73 | Bus publishLocal(String topic, Object msg); 74 | 75 | /** 76 | * Send a message 77 | * 78 | * @param topic The topic to send it to 79 | * @param msg The message 80 | * @param replyHandler Reply handler will be called when any reply from the recipient is received 81 | */ 82 | Bus send(String topic, Object msg, Handler> replyHandler); 83 | 84 | /** 85 | * Send a local message 86 | * 87 | * @param topic The topic to send it to 88 | * @param msg The message 89 | * @param replyHandler Reply handler will be called when any reply from the recipient is received 90 | */ 91 | Bus sendLocal(String topic, Object msg, Handler> replyHandler); 92 | 93 | /** 94 | * Set a BusHook on the Bus 95 | * 96 | * @param hook The hook 97 | */ 98 | Bus setHook(BusHook hook); 99 | 100 | /** 101 | * Registers a handler against the specified topic 102 | * 103 | * @param topic The topic to register it at 104 | * @param handler The handler 105 | * @return the handler registration, can be stored in order to unregister the handler later 106 | */ 107 | @SuppressWarnings("rawtypes") 108 | Registration subscribe(String topic, Handler handler); 109 | 110 | /** 111 | * Registers a local handler against the specified topic. The handler info won't be propagated 112 | * across the cluster 113 | * 114 | * @param topic The topic to register it at 115 | * @param handler The handler 116 | */ 117 | @SuppressWarnings("rawtypes") 118 | Registration subscribeLocal(String topic, Handler handler); 119 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/server/impl/VertxBus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.server.impl; 15 | 16 | import com.goodow.realtime.channel.Bus; 17 | import com.goodow.realtime.channel.BusHook; 18 | import com.goodow.realtime.channel.Message; 19 | import com.goodow.realtime.channel.State; 20 | import com.goodow.realtime.channel.impl.SimpleBus; 21 | import com.goodow.realtime.core.Handler; 22 | import com.goodow.realtime.core.Registration; 23 | import com.goodow.realtime.json.impl.JreJsonArray; 24 | import com.goodow.realtime.json.impl.JreJsonObject; 25 | 26 | import org.vertx.java.core.AsyncResult; 27 | import org.vertx.java.core.eventbus.EventBus; 28 | import org.vertx.java.core.json.JsonArray; 29 | import org.vertx.java.core.json.JsonObject; 30 | 31 | import java.util.logging.Level; 32 | import java.util.logging.Logger; 33 | 34 | public class VertxBus implements Bus { 35 | private static final Logger log = Logger.getLogger(VertxBus.class.getName()); 36 | 37 | @SuppressWarnings("unchecked") 38 | static Object unwrapMsg(Object vertxMessage) { 39 | if (vertxMessage instanceof JsonObject) { 40 | return new JreJsonObject(((JsonObject) vertxMessage).toMap()); 41 | } else if (vertxMessage instanceof JsonArray) { 42 | return new JreJsonArray(((JsonArray) vertxMessage).toList()); 43 | } else { 44 | return vertxMessage; 45 | } 46 | } 47 | 48 | static Object wrapMsg(Object realtimeMessage) { 49 | if (realtimeMessage instanceof JreJsonObject) { 50 | return new JsonObject(((JreJsonObject) realtimeMessage).toNative()); 51 | } else if (realtimeMessage instanceof JreJsonArray) { 52 | return new JsonArray(((JreJsonArray) realtimeMessage).toNative()); 53 | } else { 54 | return realtimeMessage; 55 | } 56 | } 57 | 58 | private final Bus localBus; 59 | private final EventBus eb; 60 | private State state; 61 | private BusHook hook; 62 | 63 | public VertxBus(EventBus eb) { 64 | this.eb = eb; 65 | state = State.OPEN; 66 | localBus = new SimpleBus(); 67 | } 68 | 69 | @Override 70 | public void close() { 71 | if (hook == null || hook.handlePreClose()) { 72 | state = State.CLOSING; 73 | localBus.close(); 74 | eb.close(new org.vertx.java.core.Handler>() { 75 | @Override 76 | public void handle(AsyncResult ar) { 77 | if (ar.succeeded()) { 78 | state = State.CLOSED; 79 | if (hook != null) { 80 | hook.handlePostClose(); 81 | } 82 | } else { 83 | log.log(Level.SEVERE, "Failed to close EventBus", ar.cause()); 84 | } 85 | } 86 | }); 87 | } 88 | } 89 | 90 | @Override 91 | public State getReadyState() { 92 | return state; 93 | } 94 | 95 | @Override 96 | public String getSessionId() { 97 | return "vertx"; 98 | } 99 | 100 | @Override 101 | public VertxBus publish(String topic, Object msg) { 102 | if (hook == null || hook.handleSendOrPub(false, topic, msg, null)) { 103 | eb.publish(topic, wrapMsg(msg)); 104 | } 105 | return this; 106 | } 107 | 108 | @Override 109 | public Bus publishLocal(String topic, Object msg) { 110 | return localBus.publishLocal(topic, msg); 111 | } 112 | 113 | @SuppressWarnings("rawtypes") 114 | @Override 115 | public Registration subscribe(final String topic, 116 | final Handler handler) { 117 | if (hook != null && !hook.handlePreSubscribe(topic, handler)) { 118 | return Registration.EMPTY; 119 | } 120 | final org.vertx.java.core.Handler vertxHandler = 121 | wrapHandler(handler); 122 | eb.registerHandler(topic, vertxHandler, new org.vertx.java.core.Handler>() { 123 | @Override 124 | public void handle(AsyncResult ar) { 125 | if (ar.failed()) { 126 | log.log(Level.SEVERE, "Failed to register handler on event bus", ar.cause()); 127 | } 128 | } 129 | }); 130 | return new Registration() { 131 | @Override 132 | public void unregister() { 133 | if (hook == null || hook.handleUnsubscribe(topic)) { 134 | eb.unregisterHandler(topic, vertxHandler, 135 | new org.vertx.java.core.Handler>() { 136 | @Override 137 | public void handle(AsyncResult ar) { 138 | if (ar.failed()) { 139 | log.log(Level.SEVERE, "Failed to unregister handler on event bus", ar.cause()); 140 | } 141 | } 142 | }); 143 | } 144 | } 145 | }; 146 | } 147 | 148 | @SuppressWarnings("rawtypes") 149 | @Override 150 | public Registration subscribeLocal(final String topic, 151 | Handler handler) { 152 | return localBus.subscribeLocal(topic, handler); 153 | } 154 | 155 | @Override 156 | public VertxBus send(String topic, Object msg, final Handler> replyHandler) { 157 | if (hook == null || hook.handleSendOrPub(true, topic, msg, replyHandler)) { 158 | eb.send(topic, wrapMsg(msg), wrapHandler(replyHandler)); 159 | } 160 | return this; 161 | } 162 | 163 | @Override 164 | public Bus sendLocal(String topic, Object msg, Handler> replyHandler) { 165 | return localBus.sendLocal(topic, msg, replyHandler); 166 | } 167 | 168 | @Override 169 | public VertxBus setHook(BusHook hook) { 170 | this.hook = hook; 171 | if (hook != null && state == State.OPEN) { 172 | hook.handleOpened(); 173 | } 174 | return this; 175 | } 176 | 177 | BusHook getHook() { 178 | return hook; 179 | } 180 | 181 | @SuppressWarnings("rawtypes") 182 | private org.vertx.java.core.Handler wrapHandler( 183 | final Handler handler) { 184 | return handler == null ? null 185 | : new org.vertx.java.core.Handler() { 186 | @SuppressWarnings("unchecked") 187 | @Override 188 | public void handle(org.vertx.java.core.eventbus.Message message) { 189 | VertxMessage event = new VertxMessage(VertxBus.this, message); 190 | if (hook == null || hook.handleReceiveMessage(event)) { 191 | ((Handler) handler).handle(event); 192 | } 193 | } 194 | }; 195 | } 196 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.goodow.realtime 5 | realtime-parent 6 | 0.5.5-SNAPSHOT 7 | 8 | realtime-channel 9 | https://github.com/goodow/realtime-channel/ 10 | 2013 11 | 12 | 13 | 1.6 14 | 1.6 15 | 16 | false 17 | false 18 | 19 | 20 | 21 | 22 | sonatype-nexus-snapshots 23 | Sonatype Nexus Snapshots 24 | https://oss.sonatype.org/content/repositories/snapshots 25 | 26 | false 27 | 28 | 29 | true 30 | 31 | 32 | 33 | google-diff-match-patch 34 | http://google-diff-match-patch.googlecode.com/svn/trunk/maven/ 35 | 36 | false 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | com.goodow.realtime 45 | realtime-json 46 | ${project.version} 47 | 48 | 49 | com.goodow.realtime 50 | realtime-json 51 | ${project.version} 52 | sources 53 | provided 54 | 55 | 56 | 57 | 58 | io.vertx 59 | vertx-platform 60 | 61 | 62 | 63 | diff_match_patch 64 | diff_match_patch 65 | current 66 | 67 | 68 | 69 | 70 | io.vertx 71 | testtools 72 | 73 | 74 | 75 | 76 | com.google.gwt 77 | gwt-user 78 | 79 | 80 | com.google.gwt 81 | gwt-codeserver 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | io.vertx 90 | vertx-maven-plugin 91 | 92 | src/main/resources/channel.conf 93 | 94 | 95 | 96 | PullInDeps 97 | prepare-package 98 | 99 | pullInDeps 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | maven-resources-plugin 108 | 109 | 110 | copy-mod-to-target 111 | process-classes 112 | 113 | copy-resources 114 | 115 | 116 | true 117 | ${vertx.mods.directory}/${vertx.module.name} 118 | 119 | 120 | target/classes 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | maven-dependency-plugin 129 | 130 | 131 | copy-mod-dependencies-to-target 132 | process-classes 133 | 134 | copy-dependencies 135 | 136 | 137 | ${vertx.mods.directory}/${vertx.module.name}/lib 138 | runtime 139 | com.goodow.realtime,com.fasterxml.jackson.core 140 | 141 | 142 | 143 | copy-mod-dependencies-to-target-dependencies 144 | process-classes 145 | 146 | copy-dependencies 147 | 148 | 149 | target/dependencies 150 | runtime 151 | 152 | 153 | 154 | 155 | 156 | maven-assembly-plugin 157 | 158 | 159 | src/main/assembly/mod.xml 160 | 161 | 162 | 163 | 164 | assemble 165 | package 166 | 167 | single 168 | 169 | 170 | 171 | 172 | 173 | maven-jar-plugin 174 | 175 | 176 | mod.json 177 | 178 | 179 | 180 | 181 | 182 | maven-source-plugin 183 | 184 | 185 | 186 | org.codehaus.mojo 187 | gwt-maven-plugin 188 | 189 | 190 | 191 | 192 | 0.0.0.0 193 | ${project.groupId}.channel.Channel 194 | false 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | org.eclipse.m2e 204 | lifecycle-mapping 205 | 1.0.0 206 | 207 | 208 | 209 | 210 | 211 | org.apache.maven.plugins 212 | maven-dependency-plugin 213 | [2.7,) 214 | 215 | copy-dependencies 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/impl/WebSocketBus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.impl; 15 | 16 | import com.goodow.realtime.channel.Bus; 17 | import com.goodow.realtime.channel.Message; 18 | import com.goodow.realtime.channel.State; 19 | import com.goodow.realtime.core.Handler; 20 | import com.goodow.realtime.core.Platform; 21 | import com.goodow.realtime.core.WebSocket; 22 | import com.goodow.realtime.json.Json; 23 | import com.goodow.realtime.json.JsonObject; 24 | 25 | @SuppressWarnings("rawtypes") 26 | public class WebSocketBus extends SimpleBus { 27 | public static final String SESSION = "_session"; 28 | public static final String USERNAME = "username"; 29 | public static final String PASSWORD = "password"; 30 | public static final String PING_INTERVAL = "vertxbus_ping_interval"; 31 | public static final String TOPIC_CHANNEL = "realtime/channel"; 32 | public static final String TOPIC_CONNECT = TOPIC_CHANNEL + "/_CONNECT"; 33 | 34 | protected static final String BODY = "body"; 35 | protected static final String TOPIC = "address"; 36 | protected static final String REPLY_TOPIC = "replyAddress"; 37 | protected static final String TYPE = "type"; 38 | 39 | private final WebSocket.WebSocketHandler webSocketHandler; 40 | String serverUri; 41 | WebSocket webSocket; 42 | private int pingInterval; 43 | private int pingTimerID = -1; 44 | private String sessionId; 45 | private String username; 46 | private String password; 47 | final JsonObject handlerCount = Json.createObject(); 48 | 49 | public WebSocketBus(String serverUri, JsonObject options) { 50 | webSocketHandler = new WebSocket.WebSocketHandler() { 51 | @Override 52 | public void onClose(JsonObject reason) { 53 | Platform.scheduler().cancelTimer(pingTimerID); 54 | publishLocal(ON_CLOSE, reason); 55 | if (hook != null) { 56 | hook.handlePostClose(); 57 | } 58 | } 59 | 60 | @Override 61 | public void onError(String error) { 62 | publishLocal(ON_ERROR, Json.createObject().set("message", error)); 63 | } 64 | 65 | @Override 66 | public void onMessage(String msg) { 67 | JsonObject json = Json. parse(msg); 68 | @SuppressWarnings({"unchecked"}) 69 | MessageImpl message = 70 | new MessageImpl(false, false, WebSocketBus.this, json.getString(TOPIC), json 71 | .getString(REPLY_TOPIC), json.get(BODY)); 72 | internalHandleReceiveMessage(message); 73 | } 74 | 75 | @Override 76 | public void onOpen() { 77 | sendConnect(); 78 | // Send the first ping then send a ping every 5 seconds 79 | sendPing(); 80 | pingTimerID = Platform.scheduler().schedulePeriodic(pingInterval, new Handler() { 81 | @Override 82 | public void handle(Void ignore) { 83 | sendPing(); 84 | } 85 | }); 86 | if (hook != null) { 87 | hook.handleOpened(); 88 | } 89 | publishLocal(ON_OPEN, null); 90 | } 91 | }; 92 | 93 | connect(serverUri, options); 94 | } 95 | 96 | public void connect(String serverUri, JsonObject options) { 97 | this.serverUri = serverUri; 98 | pingInterval = 99 | options == null || !options.has(PING_INTERVAL) ? 5 * 1000 : (int) options 100 | .getNumber(PING_INTERVAL); 101 | sessionId = options == null || !options.has(SESSION) ? idGenerator.next(23) : options.getString( 102 | SESSION); 103 | username = options == null || !options.has(USERNAME) ? null : options.getString(USERNAME); 104 | password = options == null || !options.has(PASSWORD) ? null : options.getString(PASSWORD); 105 | 106 | webSocket = Platform.net().createWebSocket(serverUri, options); 107 | webSocket.setListen(webSocketHandler); 108 | } 109 | 110 | @Override 111 | public State getReadyState() { 112 | return webSocket.getReadyState(); 113 | } 114 | 115 | @Override 116 | public String getSessionId() { 117 | return sessionId; 118 | } 119 | 120 | @Override 121 | protected void doClose() { 122 | subscribeLocal(Bus.ON_CLOSE, new Handler>() { 123 | @Override 124 | public void handle(Message event) { 125 | clearHandlers(); 126 | handlerCount.clear(); 127 | } 128 | }); 129 | webSocket.close(); 130 | } 131 | 132 | @Override 133 | protected boolean doSubscribe(boolean local, String topic, 134 | Handler handler) { 135 | boolean subscribed = super.doSubscribe(local, topic, handler); 136 | if (local || !subscribed || (hook != null && !hook.handlePreSubscribe(topic, handler))) { 137 | return false; 138 | } 139 | if (handlerCount.has(topic)) { 140 | handlerCount.set(topic, handlerCount.getNumber(topic) + 1); 141 | return false; 142 | } 143 | handlerCount.set(topic, 1); 144 | sendSubscribe(topic); 145 | return true; 146 | } 147 | 148 | @Override 149 | protected void doSendOrPub(boolean local, boolean send, String topic, Object msg, 150 | Handler> replyHandler) { 151 | checkNotNull(TOPIC, topic); 152 | if (local) { 153 | super.doSendOrPub(local, send, topic, msg, replyHandler); 154 | return; 155 | } 156 | JsonObject envelope = Json.createObject().set(TYPE, send ? "send" : "publish"); 157 | envelope.set(TOPIC, topic).set(BODY, msg); 158 | if (replyHandler != null) { 159 | String replyTopic = makeUUID(); 160 | envelope.set(REPLY_TOPIC, replyTopic); 161 | replyHandlers.set(replyTopic, replyHandler); 162 | } 163 | send(envelope); 164 | } 165 | 166 | @Override 167 | protected boolean doUnsubscribe(boolean local, String topic, 168 | Handler handler) { 169 | boolean unsubscribed = super.doUnsubscribe(local, topic, handler); 170 | if (local || !unsubscribed || (hook != null && !hook.handleUnsubscribe(topic))) { 171 | return false; 172 | } 173 | handlerCount.set(topic, handlerCount.getNumber(topic) - 1); 174 | if (handlerCount.getNumber(topic) == 0) { 175 | handlerCount.remove(topic); 176 | sendUnsubscribe(topic); 177 | return true; 178 | } 179 | return false; 180 | } 181 | 182 | protected void send(JsonObject msg) { 183 | if (getReadyState() != State.OPEN) { 184 | throw new IllegalStateException("INVALID_STATE_ERR"); 185 | } 186 | webSocket.send(msg.toJsonString()); 187 | } 188 | 189 | protected void sendConnect() { 190 | JsonObject msg = Json.createObject().set(SESSION, sessionId); 191 | if(username != null) { 192 | msg.set(USERNAME, username); 193 | if(password != null) { 194 | msg.set(PASSWORD, password); 195 | } 196 | } 197 | send(TOPIC_CONNECT, msg, new Handler>() { 198 | @Override 199 | public void handle(Message message) { 200 | if (message.body() != null && message.body().getNumber("code") != 0) { 201 | throw new RuntimeException(message.body().toJsonString()); 202 | } 203 | } 204 | }); 205 | } 206 | 207 | protected void sendPing() { 208 | send(Json.createObject().set(TYPE, "ping")); 209 | } 210 | 211 | /* 212 | * First handler for this topic so we should register the connection 213 | */ 214 | protected void sendSubscribe(String topic) { 215 | assert topic != null : "topic shouldn't be null"; 216 | JsonObject msg = Json.createObject().set(TYPE, "register").set(TOPIC, topic); 217 | send(msg); 218 | } 219 | 220 | /* 221 | * No more handlers so we should unregister the connection 222 | */ 223 | protected void sendUnsubscribe(String topic) { 224 | JsonObject msg = Json.createObject().set(TYPE, "unregister").set(TOPIC, topic); 225 | send(msg); 226 | } 227 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/impl/SimpleBus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.impl; 15 | 16 | import com.goodow.realtime.channel.Bus; 17 | import com.goodow.realtime.channel.BusHook; 18 | import com.goodow.realtime.channel.Message; 19 | import com.goodow.realtime.channel.State; 20 | import com.goodow.realtime.channel.util.IdGenerator; 21 | import com.goodow.realtime.core.Handler; 22 | import com.goodow.realtime.core.Platform; 23 | import com.goodow.realtime.core.Registration; 24 | import com.goodow.realtime.json.Json; 25 | import com.goodow.realtime.json.JsonArray; 26 | import com.goodow.realtime.json.JsonObject; 27 | 28 | import java.util.logging.Level; 29 | import java.util.logging.Logger; 30 | 31 | @SuppressWarnings("rawtypes") 32 | public class SimpleBus implements Bus { 33 | private static final Logger log = Logger.getLogger(SimpleBus.class.getName()); 34 | 35 | static void checkNotNull(String paramName, Object param) { 36 | if (param == null) { 37 | throw new IllegalArgumentException("Parameter " + paramName + " must be specified"); 38 | } 39 | } 40 | 41 | private JsonObject handlerMap; // LinkedHashMap>> 42 | final JsonObject replyHandlers; // LinkedHashMap> 43 | BusHook hook; 44 | final IdGenerator idGenerator; 45 | 46 | public SimpleBus() { 47 | handlerMap = Json.createObject(); 48 | replyHandlers = Json.createObject(); 49 | idGenerator = new IdGenerator(); 50 | } 51 | 52 | @Override 53 | public void close() { 54 | if (hook == null || hook.handlePreClose()) { 55 | doClose(); 56 | } 57 | } 58 | 59 | @Override 60 | public State getReadyState() { 61 | return handlerMap == null ? State.CLOSED : State.OPEN; 62 | } 63 | 64 | @Override 65 | public String getSessionId() { 66 | return "@"; 67 | } 68 | 69 | @Override 70 | public Bus publish(String topic, Object msg) { 71 | internalHandleSendOrPub(false, false, topic, msg, null); 72 | return this; 73 | } 74 | 75 | @Override 76 | public Bus publishLocal(String topic, Object msg) { 77 | internalHandleSendOrPub(true, false, topic, msg, null); 78 | return this; 79 | } 80 | 81 | @Override 82 | public Registration subscribe(final String topic, 83 | final Handler handler) { 84 | return subscribeImpl(false, topic, handler); 85 | } 86 | 87 | @Override 88 | public Registration subscribeLocal(final String topic, 89 | final Handler handler) { 90 | return subscribeImpl(true, topic, handler); 91 | } 92 | 93 | @Override 94 | public Bus send(String topic, Object msg, Handler> replyHandler) { 95 | internalHandleSendOrPub(false, true, topic, msg, replyHandler); 96 | return this; 97 | } 98 | 99 | @Override 100 | public Bus sendLocal(String topic, Object msg, Handler> replyHandler) { 101 | internalHandleSendOrPub(true, true, topic, msg, replyHandler); 102 | return this; 103 | } 104 | 105 | @Override 106 | public Bus setHook(BusHook hook) { 107 | this.hook = hook; 108 | return this; 109 | } 110 | 111 | protected void doClose() { 112 | publishLocal(ON_CLOSE, null); 113 | clearHandlers(); 114 | if (hook != null) { 115 | hook.handlePostClose(); 116 | } 117 | } 118 | 119 | protected boolean doSubscribe(boolean local, String topic, 120 | Handler handler) { 121 | checkNotNull("topic", topic); 122 | checkNotNull("handler", handler); 123 | JsonArray handlers = handlerMap.getArray(topic); 124 | if (handlers == null) { 125 | handlerMap.set(topic, Json.createArray().push(handler)); 126 | return true; 127 | } 128 | if (handlers.indexOf(handler) == -1) { 129 | handlers.push(handler); 130 | return true; 131 | } 132 | return false; 133 | } 134 | 135 | @SuppressWarnings("unchecked") 136 | protected void doSendOrPub(boolean local, boolean send, String topic, Object msg, 137 | Handler> replyHandler) { 138 | checkNotNull("topic", topic); 139 | String replyTopic = null; 140 | if (replyHandler != null) { 141 | replyTopic = makeUUID(); 142 | replyHandlers.set(replyTopic, replyHandler); 143 | } 144 | MessageImpl message = new MessageImpl(local, send, this, topic, replyTopic, msg); 145 | if (!internalHandleReceiveMessage(message) && replyTopic != null) { 146 | replyHandlers.remove(replyTopic); 147 | } 148 | } 149 | 150 | protected boolean doUnsubscribe(boolean local, String topic, 151 | Handler handler) { 152 | checkNotNull("topic", topic); 153 | checkNotNull("handler", handler); 154 | JsonArray handlers = handlerMap.getArray(topic); 155 | if (handlers == null) { 156 | return false; 157 | } 158 | boolean removed = handlers.removeValue(handler); 159 | if (handlers.length() == 0) { 160 | handlerMap.remove(topic); 161 | } 162 | return removed; 163 | } 164 | 165 | void clearHandlers() { 166 | replyHandlers.clear(); 167 | handlerMap.clear(); 168 | handlerMap = null; 169 | } 170 | 171 | boolean internalHandleReceiveMessage(Message message) { 172 | if (message.isLocal() || hook == null || hook.handleReceiveMessage(message)) { 173 | doReceiveMessage(message); 174 | return true; 175 | } 176 | return false; 177 | } 178 | 179 | void internalHandleSendOrPub(boolean local, boolean send, String topic, Object msg, 180 | Handler> replyHandler) { 181 | if (local || hook == null || hook.handleSendOrPub(send, topic, msg, replyHandler)) { 182 | doSendOrPub(local, send, topic, msg, replyHandler); 183 | } 184 | } 185 | 186 | String makeUUID() { 187 | return idGenerator.next(36); 188 | } 189 | 190 | private void doReceiveMessage(final Message message) { 191 | final String topic = message.topic(); 192 | JsonArray handlers = handlerMap.getArray(topic); 193 | if (handlers != null) { 194 | // We make a copy since the handler might get unregistered from within the handler itself, 195 | // which would screw up our iteration 196 | final JsonArray copy = Json.createArray(); 197 | handlers.forEach(new JsonArray.ListIterator() { 198 | @Override 199 | public void call(int index, Object value) { 200 | copy.push(value); 201 | } 202 | }); 203 | copy.forEach(new JsonArray.ListIterator() { 204 | @Override 205 | public void call(int index, Object value) { 206 | scheduleHandle(topic, value, message); 207 | } 208 | }); 209 | } else { 210 | // Might be a reply message 211 | Object handler = replyHandlers.get(topic); 212 | if (handler != null) { 213 | replyHandlers.remove(topic); 214 | scheduleHandle(topic, handler, message); 215 | } 216 | } 217 | } 218 | 219 | private void handle(String topic, Object handler, Object message) { 220 | try { 221 | Platform.scheduler().handle(handler, message); 222 | } catch (Throwable e) { 223 | log.log(Level.WARNING, "Failed to handle on topic: " + topic, e); 224 | publishLocal(ON_ERROR, Json.createObject().set("topic", topic) 225 | .set("message", message).set("cause", e)); 226 | } 227 | } 228 | 229 | private void scheduleHandle(final String topic, final Object handler, final Message message) { 230 | if (message.isLocal()) { 231 | handle(topic, handler, message); 232 | } else { 233 | Platform.scheduler().scheduleDeferred(new Handler() { 234 | @Override 235 | public void handle(Void ignore) { 236 | SimpleBus.this.handle(topic, handler, message); 237 | } 238 | }); 239 | } 240 | } 241 | 242 | private Registration subscribeImpl(final boolean local, final String topic, 243 | final Handler handler) { 244 | doSubscribe(local, topic, handler); 245 | return new Registration() { 246 | @Override 247 | public void unregister() { 248 | doUnsubscribe(local, topic, handler); 249 | } 250 | }; 251 | } 252 | } -------------------------------------------------------------------------------- /src/main/java/com/goodow/realtime/channel/impl/ReliableSubscribeBus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Goodow.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package com.goodow.realtime.channel.impl; 15 | 16 | import com.goodow.realtime.channel.Bus; 17 | import com.goodow.realtime.channel.BusHook; 18 | import com.goodow.realtime.channel.Message; 19 | import com.goodow.realtime.core.Handler; 20 | import com.goodow.realtime.core.Platform; 21 | import com.goodow.realtime.json.Json; 22 | import com.goodow.realtime.json.JsonArray; 23 | import com.goodow.realtime.json.JsonObject; 24 | 25 | import java.util.logging.Level; 26 | import java.util.logging.Logger; 27 | 28 | /** 29 | * Converts a stream of possibly-missing, possibly-unordered, possibly-duplicated messages into a 30 | * stream of in-order, consecutive, no-dup messages. 31 | */ 32 | public class ReliableSubscribeBus extends BusProxy { 33 | public static final String SEQUENCE_NUMBER = "sequence_number_key"; 34 | public static final String PUBLISH_CHANNEL = "publish_channel"; 35 | public static final String ACKNOWLEDGE_DELAY_MILLIS = "acknowledgeDelayMillis"; 36 | 37 | private static final Logger log = Logger.getLogger(ReliableSubscribeBus.class.getName()); 38 | private final String sequenceNumberKey; 39 | private final String publishChannel; 40 | /** 41 | * Delay acknowledgment in case we receive operations in the meantime. 42 | */ 43 | private final int acknowledgeDelayMillis; 44 | private final JsonObject pendings; // {topic: {sequence: Message}} 45 | private final JsonObject currentSequences; 46 | private final JsonObject knownHeadSequences; 47 | private final JsonObject acknowledgeScheduled; 48 | private final JsonObject acknowledgeNumbers; 49 | 50 | public ReliableSubscribeBus(Bus delegate, JsonObject options) { 51 | super(delegate); 52 | sequenceNumberKey = 53 | options == null || !options.has(SEQUENCE_NUMBER) ? "v" : options.getString(SEQUENCE_NUMBER); 54 | publishChannel = 55 | options == null || !options.has(PUBLISH_CHANNEL) ? "realtime/store" : options 56 | .getString(PUBLISH_CHANNEL); 57 | acknowledgeDelayMillis = 58 | options == null || !options.has(ACKNOWLEDGE_DELAY_MILLIS) ? 3 * 1000 : (int) options 59 | .getNumber(ACKNOWLEDGE_DELAY_MILLIS); 60 | pendings = Json.createObject(); 61 | currentSequences = Json.createObject(); 62 | knownHeadSequences = Json.createObject(); 63 | acknowledgeScheduled = Json.createObject(); 64 | acknowledgeNumbers = Json.createObject(); 65 | 66 | delegate.setHook(new BusHookProxy() { 67 | @Override 68 | public boolean handleReceiveMessage(Message message) { 69 | if (hook != null && !hook.handleReceiveMessage(message)) { 70 | return false; 71 | } 72 | return onReceiveMessage(message); 73 | } 74 | 75 | @Override 76 | public boolean handleUnsubscribe(String topic) { 77 | if (needProcess(topic)) { 78 | pendings.remove(topic); 79 | currentSequences.remove(topic); 80 | knownHeadSequences.remove(topic); 81 | acknowledgeScheduled.remove(topic); 82 | acknowledgeNumbers.remove(topic); 83 | } 84 | return super.handleUnsubscribe(topic); 85 | } 86 | 87 | @Override 88 | protected BusHook delegate() { 89 | return hook; 90 | } 91 | }); 92 | } 93 | 94 | @Override 95 | public void close() { 96 | super.close(); 97 | pendings.clear(); 98 | currentSequences.clear(); 99 | knownHeadSequences.clear(); 100 | acknowledgeScheduled.clear(); 101 | acknowledgeNumbers.clear(); 102 | } 103 | 104 | public void synchronizeSequenceNumber(String topic, double initialSequenceNumber) { 105 | assert !currentSequences.has(topic) && !knownHeadSequences.has(topic) 106 | && !pendings.has(topic); 107 | initSequenceNumber(topic, initialSequenceNumber); 108 | // Send the first acknowledgment immediately, to quickly catch up any initial missing messages, 109 | // which might happen if the topic is currently active. 110 | catchup(topic, initialSequenceNumber); 111 | } 112 | 113 | protected void catchup(final String topic, double currentSequence) { 114 | String id = topic.substring(publishChannel.length() + 1); 115 | id = id.substring(0, id.lastIndexOf("/_watch")); 116 | delegate.send(publishChannel + "/_ops", 117 | Json.createObject().set("id", id) .set("from", currentSequence + 1), 118 | new Handler>() { 119 | @SuppressWarnings({"rawtypes", "unchecked"}) 120 | @Override 121 | public void handle(Message message) { 122 | final String replyTopic = message.replyTopic(); 123 | message.body().forEach(new JsonArray.ListIterator() { 124 | @Override 125 | public void call(int index, Object value) { 126 | onReceiveMessage(new MessageImpl(false, false, ReliableSubscribeBus.this, 127 | topic, replyTopic, value)); 128 | } 129 | }); 130 | } 131 | }); 132 | } 133 | 134 | protected double getSequenceNumber(String topic, Object body) { 135 | return ((JsonObject) body).getNumber(sequenceNumberKey); 136 | } 137 | 138 | protected boolean needProcess(String topic) { 139 | return topic.startsWith(publishChannel + "/") && topic.endsWith("/_watch") && 140 | !topic.contains("/_presence/"); 141 | } 142 | 143 | protected boolean onReceiveMessage(Message message) { 144 | String topic = message.topic(); 145 | Object body = message.body(); 146 | if (!needProcess(topic)) { 147 | return true; 148 | } 149 | double sequence = getSequenceNumber(topic, body); 150 | if (!currentSequences.has(topic)) { 151 | initSequenceNumber(topic, sequence); 152 | return true; 153 | } 154 | 155 | double currentSequence = currentSequences.getNumber(topic); 156 | if (sequence <= currentSequence) { 157 | log.log(Level.CONFIG, "Old dup at sequence " + sequence + ", current is now " 158 | + currentSequence); 159 | return false; 160 | } 161 | JsonObject pending = pendings.getObject(topic); 162 | Message existing = pending.get("" + sequence); 163 | if (existing != null) { 164 | // Should not have pending data at a sequence we could have pushed out. 165 | assert sequence > currentSequence + 1 : "should not have pending data"; 166 | log.log(Level.CONFIG, "Dup message: " + message); 167 | return false; 168 | } 169 | 170 | knownHeadSequences.set(topic, Math.max(knownHeadSequences.getNumber(topic), sequence)); 171 | 172 | if (sequence > currentSequence + 1) { 173 | pending.set("" + sequence, message); 174 | log.log(Level.CONFIG, "Missed message, current sequence=" + currentSequence 175 | + " incoming sequence=" + sequence); 176 | scheduleAcknowledgment(topic); 177 | return false; 178 | } 179 | 180 | assert sequence == currentSequence + 1 : "other cases should have been caught"; 181 | String next; 182 | JsonArray messages = Json.createArray(); 183 | while (true) { 184 | messages.push(message); 185 | currentSequences.set(topic, ++currentSequence); 186 | next = currentSequence + 1 + ""; 187 | message = pending.get(next); 188 | if (message != null) { 189 | pending.remove(next); 190 | } else { 191 | break; 192 | } 193 | } 194 | scheduleMessages(messages); 195 | assert !pending.has(next); 196 | return false; 197 | } 198 | 199 | private void initSequenceNumber(String topic, double initialSequenceNumber) { 200 | currentSequences.set(topic, initialSequenceNumber); 201 | knownHeadSequences.set(topic, initialSequenceNumber); 202 | pendings.set(topic, Json.createObject()); 203 | } 204 | 205 | /** 206 | * Acknowledgment Number is the next sequence number that the receiver is expecting 207 | */ 208 | private void scheduleAcknowledgment(final String topic) { 209 | if (!acknowledgeScheduled.has(topic)) { 210 | acknowledgeScheduled.set(topic, true); 211 | Platform.scheduler().scheduleDelay(acknowledgeDelayMillis, new Handler() { 212 | @Override 213 | public void handle(Void event) { 214 | if (acknowledgeScheduled.has(topic)) { 215 | acknowledgeScheduled.remove(topic); 216 | // Check we're still out of date, and not already catching up. 217 | double knownHeadSequence = knownHeadSequences.getNumber(topic); 218 | double currentSequence = currentSequences.getNumber(topic); 219 | if (knownHeadSequence > currentSequence 220 | && (!acknowledgeNumbers.has(topic) || knownHeadSequence > acknowledgeNumbers 221 | .getNumber(topic))) { 222 | acknowledgeNumbers.set(topic, knownHeadSequence); 223 | log.log(Level.CONFIG, "Catching up to " + knownHeadSequence); 224 | catchup(topic, currentSequence); 225 | } else { 226 | log.log(Level.FINE, "No need to catchup"); 227 | } 228 | } 229 | } 230 | }); 231 | } 232 | } 233 | 234 | private void scheduleMessages(final JsonArray messages) { 235 | Platform.scheduler().scheduleDeferred(new Handler() { 236 | @Override 237 | public void handle(Void event) { 238 | messages.forEach(new JsonArray.ListIterator>() { 239 | @Override 240 | public void call(int index, Message message) { 241 | delegate.publishLocal(message.topic(), message.body()); 242 | } 243 | }); 244 | } 245 | }); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/main/resources/com/google/gwt/core/linker/SingleScriptTemplate.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | function __MODULE_FUNC__() { 18 | // ---------------- INTERNAL GLOBALS ---------------- 19 | 20 | // Cache symbols locally for good obfuscation 21 | var $wnd = window 22 | ,$doc = document 23 | 24 | // These variables gate calling gwtOnLoad; all must be true to start 25 | ,gwtOnLoad, bodyDone 26 | 27 | // If non-empty, an alternate base url for this module 28 | ,base = '' 29 | 30 | // A map of properties that were declared in meta tags 31 | ,metaProps = {} 32 | 33 | // Maps property names onto sets of legal values for that property. 34 | ,values = [] 35 | 36 | // Maps property names onto a function to compute that property. 37 | ,providers = [] 38 | 39 | // A multi-tier lookup map that uses actual property values to quickly find 40 | // the strong name of the cache.js file to load. 41 | ,answers = [] 42 | 43 | // Provides the module with the soft permutation id 44 | ,softPermutationId = 0 45 | 46 | // Error functions. Default unset in compiled mode, may be set by meta props. 47 | ,onLoadErrorFunc, propertyErrorFunc 48 | 49 | ; // end of global vars 50 | 51 | // ------------------ TRUE GLOBALS ------------------ 52 | 53 | // Maps to synchronize the loading of styles and scripts; resources are loaded 54 | // only once, even when multiple modules depend on them. This API must not 55 | // change across GWT versions. 56 | if (!$wnd.__gwt_stylesLoaded) { $wnd.__gwt_stylesLoaded = {}; } 57 | if (!$wnd.__gwt_scriptsLoaded) { $wnd.__gwt_scriptsLoaded = {}; } 58 | 59 | // --------------- INTERNAL FUNCTIONS --------------- 60 | 61 | function isHostedMode() { 62 | var result = false; 63 | try { 64 | var query = $wnd.location.search; 65 | return (query.indexOf('gwt.codesvr=') != -1 66 | || query.indexOf('gwt.hosted=') != -1 67 | || ($wnd.external && $wnd.external.gwtOnLoad)) && 68 | (query.indexOf('gwt.hybrid') == -1); 69 | } catch (e) { 70 | // Defensive: some versions of IE7 reportedly can throw an exception 71 | // evaluating "external.gwtOnLoad". 72 | } 73 | isHostedMode = function() { return result; }; 74 | return result; 75 | } 76 | 77 | // Called by onScriptLoad() and onload(). It causes 78 | // the specified module to be cranked up. 79 | // 80 | function maybeStartModule() { 81 | // TODO: it may not be necessary to check gwtOnLoad here. 82 | if (gwtOnLoad) { 83 | gwtOnLoad(onLoadErrorFunc, '__MODULE_NAME__', base, softPermutationId); 84 | } 85 | } 86 | 87 | // Determine our own script's URL via magic :) 88 | // This function produces one side-effect, it sets base to the module's 89 | // base url. 90 | // 91 | function computeScriptBase() { 92 | var thisScript 93 | ,markerId = "__gwt_marker___MODULE_NAME__" 94 | ,markerScript; 95 | 96 | $doc.write(''); 97 | markerScript = $doc.getElementById(markerId); 98 | 99 | // Our script element is assumed to be the closest previous script element 100 | // to the marker, so start at the marker and walk backwards until we find 101 | // a script. 102 | thisScript = markerScript && markerScript.previousSibling; 103 | while (thisScript && thisScript.tagName != 'SCRIPT') { 104 | thisScript = thisScript.previousSibling; 105 | } 106 | 107 | // Gets the part of a url up to and including the 'path' portion. 108 | function getDirectoryOfFile(path) { 109 | // Truncate starting at the first '?' or '#', whichever comes first. 110 | var hashIndex = path.lastIndexOf('#'); 111 | if (hashIndex == -1) { 112 | hashIndex = path.length; 113 | } 114 | var queryIndex = path.indexOf('?'); 115 | if (queryIndex == -1) { 116 | queryIndex = path.length; 117 | } 118 | var slashIndex = path.lastIndexOf('/', Math.min(queryIndex, hashIndex)); 119 | return (slashIndex >= 0) ? path.substring(0, slashIndex + 1) : ''; 120 | }; 121 | 122 | if (thisScript && thisScript.src) { 123 | // Compute our base url 124 | base = getDirectoryOfFile(thisScript.src); 125 | } 126 | 127 | // Make the base URL absolute 128 | if (base == '') { 129 | // If there's a base tag, use it. 130 | var baseElements = $doc.getElementsByTagName('base'); 131 | if (baseElements.length > 0) { 132 | // It's always the last parsed base tag that will apply to this script. 133 | base = baseElements[baseElements.length - 1].href; 134 | } else { 135 | // No base tag; the base must be the same as the document location. 136 | base = getDirectoryOfFile($doc.location.href); 137 | } 138 | } else if ((base.match(/^\w+:\/\//))) { 139 | // If the URL is obviously absolute, do nothing. 140 | } else { 141 | // Probably a relative URL; use magic to make the browser absolutify it. 142 | // I wish there were a better way to do this, but this seems the only 143 | // sure way! (A side benefit is it preloads clear.cache.gif) 144 | // Note: this trick is harmless if the URL was really already absolute. 145 | var img = $doc.createElement("img"); 146 | img.src = base + 'clear.cache.gif'; 147 | base = getDirectoryOfFile(img.src); 148 | } 149 | 150 | if (markerScript) { 151 | // remove the marker element 152 | markerScript.parentNode.removeChild(markerScript); 153 | } 154 | } 155 | 156 | // Called to slurp up all tags: 157 | // gwt:property, gwt:onPropertyErrorFn, gwt:onLoadErrorFn 158 | // 159 | function processMetas() { 160 | var metas = document.getElementsByTagName('meta'); 161 | for (var i = 0, n = metas.length; i < n; ++i) { 162 | var meta = metas[i], name = meta.getAttribute('name'), content; 163 | 164 | if (name) { 165 | if (name == 'gwt:property') { 166 | content = meta.getAttribute('content'); 167 | if (content) { 168 | var value, eq = content.indexOf('='); 169 | if (eq >= 0) { 170 | name = content.substring(0, eq); 171 | value = content.substring(eq+1); 172 | } else { 173 | name = content; 174 | value = ''; 175 | } 176 | metaProps[name] = value; 177 | } 178 | } else if (name == 'gwt:onPropertyErrorFn') { 179 | content = meta.getAttribute('content'); 180 | if (content) { 181 | try { 182 | propertyErrorFunc = eval(content); 183 | } catch (e) { 184 | alert('Bad handler \"' + content + 185 | '\" for \"gwt:onPropertyErrorFn\"'); 186 | } 187 | } 188 | } else if (name == 'gwt:onLoadErrorFn') { 189 | content = meta.getAttribute('content'); 190 | if (content) { 191 | try { 192 | onLoadErrorFunc = eval(content); 193 | } catch (e) { 194 | alert('Bad handler \"' + content + '\" for \"gwt:onLoadErrorFn\"'); 195 | } 196 | } 197 | } 198 | } 199 | } 200 | } 201 | 202 | /** 203 | * Determines whether or not a particular property value is allowed. Called by 204 | * property providers. 205 | * 206 | * @param propName the name of the property being checked 207 | * @param propValue the property value being tested 208 | */ 209 | __gwt_isKnownPropertyValue = function(propName, propValue) { 210 | return propValue in values[propName]; 211 | } 212 | 213 | /** 214 | * Returns a meta property value, if any. Used by DefaultPropertyProvider. 215 | */ 216 | __gwt_getMetaProperty = function(name) { 217 | var value = metaProps[name]; 218 | return (value == null) ? null : value; 219 | } 220 | 221 | // Deferred-binding mapper function. Sets a value into the several-level-deep 222 | // answers map. The keys are specified by a non-zero-length propValArray, 223 | // which should be a flat array target property values. Used by the generated 224 | // PERMUTATIONS code. 225 | // 226 | function unflattenKeylistIntoAnswers(propValArray, value) { 227 | var answer = answers; 228 | for (var i = 0, n = propValArray.length - 1; i < n; ++i) { 229 | // lazy initialize an empty object for the current key if needed 230 | answer = answer[propValArray[i]] || (answer[propValArray[i]] = []); 231 | } 232 | // set the final one to the value 233 | answer[propValArray[n]] = value; 234 | } 235 | 236 | // Computes the value of a given property. propName must be a valid property 237 | // name. Used by the generated PERMUTATIONS code. 238 | // 239 | function computePropValue(propName) { 240 | var value = providers[propName](), allowedValuesMap = values[propName]; 241 | if (value in allowedValuesMap) { 242 | return value; 243 | } 244 | var allowedValuesList = []; 245 | for (var k in allowedValuesMap) { 246 | allowedValuesList[allowedValuesMap[k]] = k; 247 | } 248 | if (propertyErrorFunc) { 249 | propertyErrorFunc(propName, allowedValuesList, value); 250 | } 251 | throw null; 252 | } 253 | 254 | // --------------- PROPERTY PROVIDERS --------------- 255 | 256 | // __PROPERTIES_BEGIN__ 257 | // __PROPERTIES_END__ 258 | 259 | // --------------- EXPOSED FUNCTIONS ---------------- 260 | 261 | // Called when the compiled script identified by moduleName is done loading. 262 | // 263 | __MODULE_FUNC__.onScriptLoad = function(gwtOnLoadFunc) { 264 | // remove this whole function from the global namespace to allow GC 265 | __MODULE_FUNC__ = null; 266 | gwtOnLoad = gwtOnLoadFunc; 267 | maybeStartModule(); 268 | } 269 | 270 | // --------------- STRAIGHT-LINE CODE --------------- 271 | 272 | if (isHostedMode()) { 273 | alert("Single-script hosted mode not yet implemented. See issue " + 274 | "http://code.google.com/p/google-web-toolkit/issues/detail?id=2079"); 275 | return; 276 | } 277 | 278 | // do it early for compile/browse rebasing 279 | computeScriptBase(); 280 | processMetas(); 281 | 282 | // --------------- WINDOW ONLOAD HOOK --------------- 283 | 284 | try { 285 | var strongName; 286 | // __PERMUTATIONS_BEGIN__ 287 | // Permutation logic 288 | // __PERMUTATIONS_END__ 289 | var idx = strongName.indexOf(':'); 290 | if (idx != -1) { 291 | softPermutationId = Number(strongName.substring(idx + 1)); 292 | } 293 | } catch (e) { 294 | // intentionally silent on property failure 295 | return; 296 | } 297 | 298 | /* 299 | var onBodyDoneTimerId; 300 | function onBodyDone() { 301 | if (!bodyDone) { 302 | bodyDone = true; 303 | // __MODULE_STYLES_BEGIN__ 304 | // Style resources are injected here to prevent operation aborted errors on ie 305 | // __MODULE_STYLES_END__ 306 | maybeStartModule(); 307 | 308 | if ($doc.removeEventListener) { 309 | $doc.removeEventListener("DOMContentLoaded", onBodyDone, false); 310 | } 311 | if (onBodyDoneTimerId) { 312 | clearInterval(onBodyDoneTimerId); 313 | } 314 | } 315 | } 316 | 317 | // For everyone that supports DOMContentLoaded. 318 | if ($doc.addEventListener) { 319 | $doc.addEventListener("DOMContentLoaded", function() { 320 | onBodyDone(); 321 | }, false); 322 | } 323 | 324 | // Fallback. If onBodyDone() gets fired twice, it's not a big deal. 325 | var onBodyDoneTimerId = setInterval(function() { 326 | if (/loaded|complete/.test($doc.readyState)) { 327 | onBodyDone(); 328 | } 329 | }, 50); 330 | */ 331 | 332 | // __MODULE_SCRIPTS_BEGIN__ 333 | // Script resources are injected here 334 | // __MODULE_SCRIPTS_END__ 335 | } 336 | 337 | __MODULE_FUNC__(); 338 | --------------------------------------------------------------------------------