Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Events Push - server-side

  • Loading branch information...
commit 6b8bf062a57788b6f59b6cf4c5ab7d97c2f37920 1 parent 499f367
@smaldini authored
View
23 EventsPushGrailsPlugin.groovy
@@ -1,3 +1,20 @@
+/* Copyright 2011-2012 the original author or authors:
+ *
+ * Marc Palmer (marc@grailsrocks.com)
+ * Stéphane Maldini (stephane.maldini@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
import org.grails.plugin.platform.events.push.EventsPushHandler
class EventsPushGrailsPlugin {
@@ -9,9 +26,13 @@ class EventsPushGrailsPlugin {
def dependsOn = [:]
// resources that are excluded from plugin packaging
def pluginExcludes = [
- "grails-app/views/error.gsp"
+ "grails-app/views/error.gsp",
+ "grails-app/views/index.gsp",
+ "grails-app/conf/Test*.groovy",
+ "grails-app/services/**/Test*.groovy"
]
+
def title = "Events Push Plugin" // Headline display name of the plugin
def author = "Stephane Maldini"
def authorEmail = "stephane.maldini@gmail.com"
View
17 HEADER.TXT
@@ -0,0 +1,17 @@
+/* Copyright 2011-2012 the original author or authors:
+ *
+ * Marc Palmer (marc@grailsrocks.com)
+ * Stéphane Maldini (stephane.maldini@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
View
1  grails-app/conf/Config.groovy
@@ -18,6 +18,7 @@ log4j = {
}
debug 'org.atmosphere'
+ debug 'org.grails.plugin.platform'
error 'org.codehaus.groovy.grails.web.servlet', // controllers
View
23 grails-app/conf/TestPushEvents.groovy
@@ -0,0 +1,23 @@
+/* Copyright 2011-2012 the original author or authors:
+ *
+ * Marc Palmer (marc@grailsrocks.com)
+ * Stéphane Maldini (stephane.maldini@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import org.grails.plugin.platform.test.SampleService
+
+events = {
+ "sampleHello" scope:'browser'
+ //"sampleBro" scope:'browser', clientBroadcast:true
+}
View
39 grails-app/services/org/groovy/grails/platform/push/TestService.groovy
@@ -0,0 +1,39 @@
+/* Copyright 2011-2012 the original author or authors:
+ *
+ * Marc Palmer (marc@grailsrocks.com)
+ * Stéphane Maldini (stephane.maldini@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.groovy.grails.platform.push
+
+import grails.events.Listener
+
+/**
+ * @file
+ * @author Stephane Maldini <smaldini@doc4web.com>
+ * @version 1.0
+ * @date 21/05/12
+
+ * @section DESCRIPTION
+ *
+ * [Does stuff]
+ */
+class TestService {
+
+ @Listener('sampleHello')
+ def testEvent(test) {
+ println """--> $test"""
+ new grails.events.BroadcastOrder('{ "message":"hello !!!!" }')
+ }
+}
View
14 grails-app/views/index.gsp
@@ -37,11 +37,9 @@
request.onMessage = function (response) {
detectedTransport = response.transport;
- if (response.status == 200) {
- var data = response.responseBody;
- if (data.length > 0) {
- $('ul').prepend($('<li></li>').text(" Message Received: " + data + " but detected transport is " + detectedTransport));
- }
+ if (response.status == 200 && response.responseBody.length > 0) {
+ var data = $.parseJSON(response.responseBody);
+ $('ul').prepend($('<li></li>').text(" Message Received: " + data.message + " but detected transport is " + detectedTransport));
}
};
@@ -54,7 +52,7 @@
}
function connect() {
- unsubscribe();
+ //unsubscribe();
getElementById('phrase').value = '';
getElementById('sendMessage').className = '';
getElementById('phrase').focus();
@@ -90,7 +88,7 @@
m = " sent trying to use " + detectedTransport;
}
- subSocket.push({data:'message=' + getElementByIdValue('phrase') + m});
+ subSocket.push({data:$.stringifyJSON({message:getElementByIdValue('phrase') + m})});
getElementById('phrase').value = '';
return false;
@@ -110,7 +108,7 @@
m = " sent trying to use " + detectedTransport;
}
- subSocket.push({data:'message=' + getElementByIdValue('phrase') + m});
+ subSocket.push({data:$.stringifyJSON({message:getElementByIdValue('phrase') + m})});
getElementById('phrase').value = '';
return false;
View
17 scripts/_Events.groovy
@@ -1,3 +1,20 @@
+/* Copyright 2011-2012 the original author or authors:
+ *
+ * Marc Palmer (marc@grailsrocks.com)
+ * Stéphane Maldini (stephane.maldini@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
import groovy.xml.StreamingMarkupBuilder
import org.codehaus.groovy.grails.commons.ConfigurationHolder
View
40 src/java/grails/events/BroadcastOrder.java
@@ -0,0 +1,40 @@
+/* Copyright 2011-2012 the original author or authors:
+ *
+ * Marc Palmer (marc@grailsrocks.com)
+ * Stéphane Maldini (stephane.maldini@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package grails.events;
+
+/**
+ * @author Stephane Maldini <smaldini@doc4web.com>
+ * @version 1.0
+ * @file
+ * @date 21/05/12
+ * @section DESCRIPTION
+ * <p/>
+ * [Does stuff]
+ */
+public class BroadcastOrder {
+
+ private Object data;
+
+ public BroadcastOrder(Object data){
+ this.data = data;
+ }
+
+ public Object getData() {
+ return data;
+ }
+}
View
163 src/java/org/grails/plugin/platform/events/push/EventsPushHandler.java
@@ -1,12 +1,45 @@
+/* Copyright 2011-2012 the original author or authors:
+ *
+ * Marc Palmer (marc@grailsrocks.com)
+ * Stéphane Maldini (stephane.maldini@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package org.grails.plugin.platform.events.push;
+import grails.events.BroadcastOrder;
import org.atmosphere.cpr.*;
import org.atmosphere.websocket.WebSocketEventListenerAdapter;
+import org.codehaus.groovy.grails.commons.ApplicationAttributes;
+import org.codehaus.groovy.grails.commons.GrailsApplication;
+import org.grails.plugin.platform.events.EventDefinition;
+import org.grails.plugin.platform.events.EventReply;
+import org.grails.plugin.platform.events.EventsImpl;
+import org.grails.plugin.platform.events.registry.EventsRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.util.ReflectionUtils;
+import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
/**
* @author Stephane Maldini <smaldini@doc4web.com>
@@ -18,17 +51,113 @@
* [Does stuff]
*/
public class EventsPushHandler extends HttpServlet {
+
+ static private Logger log = LoggerFactory.getLogger(EventsPushHandler.class);
+
+ private EventsImpl grailsEvents;
+ private EventsRegistry eventsRegistry;
+ private GrailsApplication grailsApplication;
+ private BroadcasterFactory broadcasterFactory;
+
+ public static final String ID_GRAILSEVENTS = "grailsEvents";
+ public static final String PUSH_SCOPE = "browser";
+ public static final String CLIENT_BROADCAST_PARAM = "clientBroadcast";
+ public static final String MESSAGE_PARAM = "message";
+
+ @Override
+ public void init() throws ServletException {
+ super.init();
+
+ broadcasterFactory = BroadcasterFactory.getDefault();
+
+ ApplicationContext applicationContext = null;
+ try {
+ applicationContext =
+ ((ApplicationContext) getServletContext().getAttribute(ApplicationAttributes.APPLICATION_CONTEXT));
+ } catch (Exception c) {
+ log.error("Couldn't manage to retrieve appContext, servlet ordering problem ?", c);
+ }
+
+ if (applicationContext != null) {
+ try {
+ grailsApplication = applicationContext.getBean("grailsApplication", GrailsApplication.class);
+ grailsEvents = applicationContext.getBean(ID_GRAILSEVENTS, EventsImpl.class);
+ eventsRegistry = grailsEvents.getGrailsEventsRegistry();
+ } catch (Exception c) {
+ log.error("Couldn't manage to retrieve beans", c);
+ }
+ }
+
+ if (grailsEvents != null && eventsRegistry != null) {
+ registerTopics(eventsRegistry, grailsEvents);
+ }
+ }
+
+ static public void registerTopics(EventsRegistry eventsRegistry, EventsImpl grailsEvents) {
+ Method m = ReflectionUtils.findMethod(BroadcastEventWrapper.class, "broadcastEvent", Object.class);
+ List<String> doneTopics = new ArrayList<String>();
+ Object broadcastClient = null;
+
+ for (EventDefinition eventDefinition : grailsEvents.getEventDefinitions()) {
+ String topic = eventDefinition.getListenerId().getTopic();
+ broadcastClient = eventDefinition.getOthersAttributes() != null ? eventDefinition.getOthersAttributes().get(CLIENT_BROADCAST_PARAM) : false;
+ broadcastClient = broadcastClient != null ? broadcastClient : false;
+
+ if (topic != null && ((Boolean) broadcastClient || eventDefinition.getScope().equalsIgnoreCase(PUSH_SCOPE)) &&
+ !doneTopics.contains(topic)) {
+ eventsRegistry.addListener(PUSH_SCOPE, topic, new BroadcastEventWrapper(topic, (Boolean) broadcastClient), m);
+ doneTopics.add(topic);
+ }
+ }
+ }
+
+ private static class BroadcastEventWrapper {
+ private Broadcaster b;
+ private boolean clientBroadcast = false;
+
+ public BroadcastEventWrapper(String topic, boolean clientBroadcast) {
+ this.b = BroadcasterFactory.getDefault().lookup(topic, true);
+ this.clientBroadcast = clientBroadcast;
+ }
+
+ public void broadcastEvent(Object message) {
+ if (message != null && (clientBroadcast || !message.getClass().isAssignableFrom(AtmosphereRequest.class))) {
+ if (message.getClass().isAssignableFrom(AtmosphereRequest.class)) {
+ try {
+ StringBuffer sb = new StringBuffer();
+ String buffer = null;
+ while ((buffer = ((AtmosphereRequest) message).getReader().readLine()) != null) {
+ sb.append(buffer);
+ }
+ message = sb.toString();
+ } catch (Exception e) {
+ log.error("", e);
+ }
+ }
+
+ b.broadcast(message);
+ }
+ }
+ }
+
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
// Create a Meteor
Meteor m = Meteor.build(req);
// Log all events on the console, including WebSocket events.
- m.addListener(new WebSocketEventListenerAdapter());
+ if (log.isDebugEnabled())
+ m.addListener(new WebSocketEventListenerAdapter());
//res.setContentType("text/html;charset=ISO-8859-1");
- Broadcaster b = lookupBroadcaster(req.getPathInfo());
+
+ Broadcaster b = broadcasterFactory.lookup(extractTopic(req.getPathInfo()));
+ if(b == null){
+ res.sendError(403);
+ return;
+ }
+
m.setBroadcaster(b);
String header = req.getHeader(HeaderConfig.X_ATMOSPHERE_TRANSPORT);
@@ -42,18 +171,32 @@ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOExce
}
public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
- Broadcaster b = lookupBroadcaster(req.getPathInfo());
+ String topic = extractTopic(req.getPathInfo());
+ EventReply reply = grailsEvents._event(PUSH_SCOPE, topic, req);
+ if (reply.size() > 0) {
+ List<String> toBroadcast = new ArrayList<String>();
+ try {
+ for (Object data : reply.getValues()) {
+ if (BroadcastOrder.class.isAssignableFrom(data.getClass())) {
+ toBroadcast.add(((BroadcastOrder)data).getData().toString());
+ }
+ }
+ } catch (ExecutionException e) {
+ log.error("", e);
+ } catch (InterruptedException e) {
+ log.error("", e);
+ }
+ if (toBroadcast.size() > 0) {
+ Broadcaster b = broadcasterFactory.lookup(topic);
+ b.broadcast(toBroadcast);
+ }
- String message = req.getReader().readLine();
-
- if (message != null && message.indexOf("message") != -1) {
- b.broadcast(message.substring("message=".length()));
}
+
}
- Broadcaster lookupBroadcaster(String pathInfo) {
+ private String extractTopic(String pathInfo) {
String[] decodedPath = pathInfo.split("/");
- Broadcaster b = BroadcasterFactory.getDefault().lookup(decodedPath[decodedPath.length - 1], true);
- return b;
+ return decodedPath[decodedPath.length - 1];
}
}
View
12 web-app/js/grails/grailsEvents.js
@@ -16,4 +16,14 @@
* Licensed under the Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
- */
+ */
+(function() {
+ var grailsEvents;
+ return grailsEvents = (function() {
+
+ function grailsEvents() {}
+
+ return grailsEvents;
+
+ })();
+});
Please sign in to comment.
Something went wrong with that request. Please try again.