Source code here
Basically, if you change data in your database, it can notify web browser clients, without polling.
Technologies used:
- pgjdbc-ng JDBC driver, as it supports asynchronous notification without polling
- PostgreSQL 9.3 database server (may work down to 8.4)
- Spring Web MVC 4 and Spring WebSocket
- Jackson 2 for JSON databinding
- SockJS with the STOMP protocol
- Tomcat 7.0.52 embedded web server (that version or higher required for proper websocket handing)
The system works like this:
Client subscribes to a WebSocket topic...
NOTIFY event on database server ->
PGNotificationListener on web server ->
Send Websocket notification on server ->
Receive Websocket event on browser.
PGNotificationListener on web server ->
Send Websocket notification on server ->
Receive Websocket event on browser.
With the code below, if you call NOTIFY dml_events, 'some message'; in Postgres, it will be broadcast to all WebSocket clients.
Follow this answer regarding proper listener setup
Project Structure:
Follow this answer regarding proper listener setup
Project Structure:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.databasepatterns</groupId> <artifactId>pg-notify-websocket</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <url>http://blog.databasepatterns.com/2014/04/postgresql-nofify-websocket-spring-mvc.html</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.0.5.RELEASE</spring.version> </properties> <dependencies> <dependency> <!-- PostgreSQL JDBC driver --> <groupId>com.impossibl.pgjdbc-ng</groupId> <artifactId>pgjdbc-ng</artifactId> <version>0.3</version> <classifier>complete</classifier> </dependency> <dependency> <!-- Your JSON library --> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <path>/</path> </configuration> <dependencies> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> <version>7.0.52</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>
PGNotifyToWebSocket.java:
import com.impossibl.postgres.api.jdbc.PGConnection; import com.impossibl.postgres.api.jdbc.PGNotificationListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import javax.sql.DataSource; import java.sql.Statement; /** * @since 7/22/2014 */ public class PGNotifyToWebSocket { @Autowired private SimpMessagingTemplate messagingTemplate; private PGConnection pgConnection; public PGNotifyToWebSocket(DataSource dataSource) throws Throwable { pgConnection = (PGConnection) dataSource.getConnection(); pgConnection.addNotificationListener(new PGNotificationListener() { @Override public void notification(int processId, String channelName, String payload) { messagingTemplate.convertAndSend("/channels/" + channelName, payload); } }); } public void init() throws Throwable { Statement statement = pgConnection.createStatement(); statement.execute("LISTEN dml_events"); statement.close(); } public void destroy() throws Throwable { Statement statement = pgConnection.createStatement(); statement.execute("UNLISTEN dml_events"); statement.close(); } }
mvc-dispatcher-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd "> <context:property-placeholder/> <context:component-scan base-package="com.databasepatterns"/> <mvc:annotation-driven/> <mvc:view-controller path="/" view-name="/WEB-INF/index.jsp"/> <bean class="com.impossibl.postgres.jdbc.PGDataSource" id="dataSource"> <property name="host" value="${PGHOST:localhost}"/> <property name="database" value="${PGDATABASE:postgres}"/> <property name="password" value="${PGPASSWORD}"/> <property name="port" value="${PGPORT:5432}"/> <property name="user" value="${PGUSER:postgres}"/> </bean> <bean class="PGNotifyToWebSocket" init-method="init" destroy-method="destroy"> <constructor-arg ref="dataSource"/> </bean> <websocket:message-broker application-destination-prefix="/app"> <websocket:stomp-endpoint path="/hello"> <websocket:sockjs/> </websocket:stomp-endpoint> <websocket:simple-broker prefix="/channels"/> </websocket:message-broker> </beans>
index.jsp:
<html> <body> <p>Run <kbd>NOTIFY dml_events 'some message';</kbd> in Postgres (in the <code>$PGDATABASE</code> or <code>postgres</code> database). Tested with PG 9.3, on Windows 7, Chrome 36.</p> <div id="out"></div> <script src="//cdnjs.cloudflare.com/ajax/libs/sockjs-client/0.3.4/sockjs.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script> <script> var socket = new SockJS("/hello"); var stompClient = Stomp.over(socket); stompClient.connect( {}, function(frame) { stompClient.subscribe("/channels/dml_events", function(response) { document.getElementById("out").innerText += response + "\r\n\r\n"; }); }); </script> </body> </html>
web.xml:
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd "> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
7 comments:
Can you do this with Oracle or any other database?
@Rodrick you can use DBMS_ALERT in Oracle to do something similar:
http://docs.oracle.com/cd/E11882_01/appdev.112/e16760/d_alert.htm#ARPLS351
I tried this. It did not work. I did not see any error message. How should I debug this thing?
My default PostgreSQL driver from now on, thanks.
This article demonstrates to you generally accepted methods to actualize IVR expansions with your twilio telephone number.ivr telephony
Post a Comment