├── .gitattributes
├── .github
└── FUNDING.yml
├── .gitignore
├── client
└── ajax-pushlet-client.js
├── demo
├── UserUnicasttDemo.war
└── UserUnicasttDemo
│ ├── .classpath
│ ├── .project
│ ├── .settings
│ ├── .jsdtscope
│ ├── org.eclipse.jdt.core.prefs
│ ├── org.eclipse.wst.common.component
│ ├── org.eclipse.wst.common.project.facet.core.xml
│ ├── org.eclipse.wst.jsdt.ui.superType.container
│ └── org.eclipse.wst.jsdt.ui.superType.name
│ ├── WebRoot
│ ├── META-INF
│ │ └── MANIFEST.MF
│ ├── WEB-INF
│ │ ├── classes
│ │ │ ├── log4j.properties
│ │ │ ├── pushlet.properties
│ │ │ ├── servlet
│ │ │ │ └── MsgPushServlet.class
│ │ │ └── sources.properties
│ │ ├── lib
│ │ │ ├── log4j.jar
│ │ │ └── pushlet-userunicast.jar
│ │ └── web.xml
│ ├── index.jsp
│ ├── js
│ │ └── ajax-pushlet-client.js
│ └── publish.jsp
│ └── src
│ ├── log4j.properties
│ ├── pushlet.properties
│ ├── servlet
│ └── MsgPushServlet.java
│ └── sources.properties
├── doc
├── readme-en.md
└── readme-zh.md
├── lib
├── log4j.jar
└── pushlet-userunicast.jar
├── readme.md
├── resource
├── log4j.properties
├── pushlet.properties
└── sources.properties
└── src
├── nl
└── justobjects
│ └── pushlet
│ ├── Version.java
│ ├── client
│ ├── PushletClient.java
│ ├── PushletClient.java.patch
│ └── PushletClientListener.java
│ ├── core
│ ├── BrowserAdapter.java
│ ├── ClientAdapter.java
│ ├── Command.java
│ ├── Config.java
│ ├── ConfigDefs.java
│ ├── Controller.java
│ ├── Dispatcher.java
│ ├── Event.java
│ ├── EventParser.java
│ ├── EventPullSource.java
│ ├── EventQueue.java
│ ├── EventSource.java
│ ├── EventSourceManager.java
│ ├── Protocol.java
│ ├── SerializedAdapter.java
│ ├── Session.java
│ ├── SessionManager.java
│ ├── Subscriber.java
│ ├── Subscription.java
│ └── XMLAdapter.java
│ ├── servlet
│ └── Pushlet.java
│ ├── test
│ ├── PushletApplet.java
│ ├── PushletPingApplication.java
│ ├── SimpleListener.java
│ ├── StressTester.java
│ ├── TestEventPullSources.java
│ └── TestEventPushSources.java
│ └── util
│ ├── DefaultLogger.java
│ ├── Log.java
│ ├── Log4jLogger.java
│ ├── PushletException.java
│ ├── PushletLogger.java
│ ├── Rand.java
│ ├── Servlets.java
│ └── Sys.java
└── 修改说明.txt
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ['http://www.easyproject.cn/donation']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # =========================
18 | # Operating System Files
19 | # =========================
20 |
21 | # OSX
22 | # =========================
23 |
24 | .DS_Store
25 | .AppleDouble
26 | .LSOverride
27 |
28 | # Icon must ends with two \r.
29 | Icon
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear on external disk
35 | .Spotlight-V100
36 | .Trashes
37 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo.war:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/demo/UserUnicasttDemo.war
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | pushletDemo3
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.wst.jsdt.core.javascriptValidator
10 |
11 |
12 |
13 |
14 | org.eclipse.jdt.core.javabuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.wst.common.project.facet.core.builder
20 |
21 |
22 |
23 |
24 | org.eclipse.wst.validation.validationbuilder
25 |
26 |
27 |
28 |
29 | com.genuitec.eclipse.j2eedt.core.DeploymentDescriptorValidator
30 |
31 |
32 |
33 |
34 | com.genuitec.eclipse.ast.deploy.core.DeploymentBuilder
35 |
36 |
37 |
38 |
39 |
40 | org.eclipse.jem.workbench.JavaEMFNature
41 | org.eclipse.wst.common.modulecore.ModuleCoreNature
42 | org.eclipse.wst.common.project.facet.core.nature
43 | org.eclipse.jdt.core.javanature
44 | org.eclipse.wst.jsdt.core.jsNature
45 |
46 |
47 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/.settings/.jsdtscope:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
4 | org.eclipse.jdt.core.compiler.compliance=1.6
5 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
6 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
7 | org.eclipse.jdt.core.compiler.source=1.6
8 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/.settings/org.eclipse.wst.common.component:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/.settings/org.eclipse.wst.common.project.facet.core.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/.settings/org.eclipse.wst.jsdt.ui.superType.container:
--------------------------------------------------------------------------------
1 | org.eclipse.wst.jsdt.launching.baseBrowserLibrary
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/.settings/org.eclipse.wst.jsdt.ui.superType.name:
--------------------------------------------------------------------------------
1 | Window
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Class-Path:
3 |
4 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/WEB-INF/classes/log4j.properties:
--------------------------------------------------------------------------------
1 | # Sample log4j config, adapt to your own logging needs
2 | # $Id: log4j.properties,v 1.1 2007/12/07 12:57:40 justb Exp $
3 | #
4 | # ***** Set root logger level to WARN and its two appenders to stdout and R.
5 | log4j.rootLogger=warn, stdout
6 |
7 | # ***** stdout is set to be a ConsoleAppender.
8 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
9 | # ***** stdout uses PatternLayout.
10 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
11 | # ***** Pattern to output the caller's file name and line number.
12 | log4j.appender.stdout.layout.ConversionPattern=%d{yy-MM-dd@HH:mm:ss} %-5p (pushlet) - %m%n
13 |
14 | # ***** R is set to be a RollingFileAppender.
15 | # log4j.appender.R=org.apache.log4j.RollingFileAppender
16 | # log4j.appender.R.File=/Users/just/example.log
17 | # ***** Max file size is set to 100KB
18 | # log4j.appender.R.MaxFileSize=100KB
19 | # ***** Keep one backup file
20 | # log4j.appender.R.MaxBackupIndex=1
21 | # ***** R uses PatternLayout.
22 | # log4j.appender.R.layout=org.apache.log4j.PatternLayout
23 | # log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
24 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/WEB-INF/classes/pushlet.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Pushlet configuration.
3 | # Place this file in the CLASSPATH (e.g. WEB-INF/classes) or directly under WEB-INF.
4 | #
5 | # $Id: pushlet.properties,v 1.13 2007/12/07 12:57:40 justb Exp $
6 | #
7 |
8 | #
9 | #
10 | #
11 | config.version=1.0.2
12 |
13 | #
14 | # CLASS FACTORY SPECIFICATION
15 | #
16 | # Change these if you want to override any of the core classes
17 | # within the Pushlet framework with your own custom classes.
18 | #
19 | # Examples:
20 | # - custom SessionManager for authorisation
21 | # - maintain lists of active subjects (topics)
22 | # - send events on subscription
23 | # - plug in custom logging like log4j
24 | # Note that you must maintain the semantics of each class !
25 | # Below are the default properties for the core classes.
26 | controller.class=nl.justobjects.pushlet.core.Controller
27 | dispatcher.class=nl.justobjects.pushlet.core.Dispatcher
28 | logger.class=nl.justobjects.pushlet.util.Log4jLogger
29 | # logger.class=nl.justobjects.pushlet.util.DefaultLogger
30 | sessionmanager.class=nl.justobjects.pushlet.core.SessionManager
31 | session.class=nl.justobjects.pushlet.core.Session
32 | subscriber.class=nl.justobjects.pushlet.core.Subscriber
33 | subscription.class=nl.justobjects.pushlet.core.Subscription
34 |
35 | # sessionmanager.maxsessions=200
36 |
37 | #
38 | # DISPATCHER
39 | #
40 |
41 |
42 | # TODO: allow properties to be maintained in
43 | # a user dir
44 | # config.redirect=/etc/pushlet.properties
45 |
46 | #
47 | # LOGGING
48 | #
49 |
50 | # log level (trace(6) debug(5) info (4), warn(3), error(2), fatal(1))
51 | # default is info(4)
52 | log.level=4
53 |
54 | #
55 | # LOCAL EVENT SOURCES
56 | #
57 |
58 | # should local sources be loaded ?
59 | sources.activate=true
60 |
61 | #
62 | # SESSION
63 | #
64 |
65 |
66 | # algoritm to generate session key:
67 | # values: "randomstring" (default) or "uuid".
68 | # session.id.generation=uuid
69 | session.id.generation=randomstring
70 |
71 | # length of generated session key when using "randomstring" generation
72 | session.id.size=10
73 |
74 | # Overall session lease time in minutes
75 | # Mainly used for clients that do not perform
76 | # listening, e.g. when publishing only.
77 | session.timeout.mins=5
78 |
79 | #
80 | # EVENT QUEUE
81 | #
82 | # Properties for per-client data event queue
83 |
84 | # Size for
85 | queue.size=24
86 | queue.read.timeout.millis=20000
87 | queue.write.timeout.millis=20
88 |
89 | #
90 | # LISTENING MODE
91 | #
92 |
93 | # You may force all clients to use pull mode
94 | # for scalability
95 | listen.force.pull.all=false
96 |
97 | #
98 | # Comma-separated list of User Agent substrings.
99 | # Force these browsers to use pull mode, since they
100 | # don't support JS streaming, matching is done using
101 | # String.indexOf() with lowercased agent strings
102 | # use multiple criteria with &.
103 | #
104 | listen.force.pull.agents=safari
105 |
106 | #
107 | # PULL MODE
108 | #
109 |
110 | # time server should wait on refresing pull client
111 | pull.refresh.timeout.millis=45000
112 |
113 | # minimum/maximum wait time client should wait before refreshing
114 | # server provides a random time between these values
115 | pull.refresh.wait.min.millis=2000
116 | pull.refresh.wait.max.millis=6000
117 |
118 | #
119 | # POLL MODE
120 | #
121 |
122 | # time server should wait on refresing poll client
123 | poll.refresh.timeout.millis=60000
124 |
125 | # minimum/maximum wait time client should wait before refreshing
126 | # server provides a random time between these values
127 | poll.refresh.wait.min.millis=6000
128 | poll.refresh.wait.max.millis=10000
129 | unicast.type=first
130 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/WEB-INF/classes/servlet/MsgPushServlet.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/demo/UserUnicasttDemo/WebRoot/WEB-INF/classes/servlet/MsgPushServlet.class
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/WEB-INF/classes/sources.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Properties file for EventSource objects to be instantiated.
3 | #
4 | # Place this file in the CLASSPATH (e.g. WEB-INF/classes) or directly under WEB-INF.
5 | #
6 | # $Id: sources.properties,v 1.2 2007/11/10 14:12:16 justb Exp $
7 | #
8 | # Each EventSource is defined as =
9 | # 1. should be unique within this file but may be any name
10 | # 2. is the full class name
11 | #
12 | #
13 | # Define Pull Sources here. These classes must be derived from
14 | # nl.justobjects.pushlet.core.EventPullSource
15 | # Inner classes are separated with a $ sign from the outer class.
16 | #source1=nl.justobjects.pushlet.test.TestEventPullSources$TemperatureEventPullSource
17 | #source2=nl.justobjects.pushlet.test.TestEventPullSources$SystemStatusEventPullSource
18 | #source3=nl.justobjects.pushlet.test.TestEventPullSources$PushletStatusEventPullSource
19 | #source4=nl.justobjects.pushlet.test.TestEventPullSources$AEXStocksEventPullSource
20 | #source5=nl.justobjects.pushlet.test.TestEventPullSources$WebPresentationEventPullSource
21 | #source6=nl.justobjects.pushlet.test.TestEventPullSources$PingEventPullSource
22 |
23 | # TO BE DONE IN NEXT VERSION
24 | # define Push Sources here. These must implement the interface
25 | # nl.justobjects.pushlet.core.EventSource
26 |
27 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/WEB-INF/lib/log4j.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/demo/UserUnicasttDemo/WebRoot/WEB-INF/lib/log4j.jar
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/WEB-INF/lib/pushlet-userunicast.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/demo/UserUnicasttDemo/WebRoot/WEB-INF/lib/pushlet-userunicast.jar
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | pushlet
5 |
6 | nl.justobjects.pushlet.servlet.Pushlet
7 |
8 | 1
9 |
10 |
11 | pushlet
12 | /pushlet.srv
13 |
14 |
15 | MsgPushServlet
16 | servlet.MsgPushServlet
17 |
18 |
19 | MsgPushServlet
20 | /servlet/MsgPushServlet
21 |
22 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/index.jsp:
--------------------------------------------------------------------------------
1 | <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 | <%
3 | String path = request.getContextPath();
4 | String basePath = request.getScheme() + "://"
5 | + request.getServerName() + ":" + request.getServerPort()
6 | + path + "/";
7 | %>
8 |
9 |
10 |
11 |
12 |
13 |
14 | 用户接收消息页面
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
38 |
39 |
40 |
41 | 连接服务器中....
42 |
43 |
44 |
45 |
46 | 后台推送消息页面
47 |
48 |
49 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/WebRoot/publish.jsp:
--------------------------------------------------------------------------------
1 | <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
2 | <%
3 | String path = request.getContextPath();
4 | String basePath = request.getScheme() + "://"
5 | + request.getServerName() + ":" + request.getServerPort()
6 | + path + "/";
7 | %>
8 |
9 |
10 |
11 |
12 |
13 |
14 | 发布推送消息
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 | 发布需要推送的信息:
28 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/src/log4j.properties:
--------------------------------------------------------------------------------
1 | # Sample log4j config, adapt to your own logging needs
2 | # $Id: log4j.properties,v 1.1 2007/12/07 12:57:40 justb Exp $
3 | #
4 | # ***** Set root logger level to WARN and its two appenders to stdout and R.
5 | log4j.rootLogger=warn, stdout
6 |
7 | # ***** stdout is set to be a ConsoleAppender.
8 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
9 | # ***** stdout uses PatternLayout.
10 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
11 | # ***** Pattern to output the caller's file name and line number.
12 | log4j.appender.stdout.layout.ConversionPattern=%d{yy-MM-dd@HH:mm:ss} %-5p (pushlet) - %m%n
13 |
14 | # ***** R is set to be a RollingFileAppender.
15 | # log4j.appender.R=org.apache.log4j.RollingFileAppender
16 | # log4j.appender.R.File=/Users/just/example.log
17 | # ***** Max file size is set to 100KB
18 | # log4j.appender.R.MaxFileSize=100KB
19 | # ***** Keep one backup file
20 | # log4j.appender.R.MaxBackupIndex=1
21 | # ***** R uses PatternLayout.
22 | # log4j.appender.R.layout=org.apache.log4j.PatternLayout
23 | # log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
24 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/src/pushlet.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Pushlet configuration.
3 | # Place this file in the CLASSPATH (e.g. WEB-INF/classes) or directly under WEB-INF.
4 | #
5 | # $Id: pushlet.properties,v 1.13 2007/12/07 12:57:40 justb Exp $
6 | #
7 |
8 | #
9 | #
10 | #
11 | config.version=1.0.2
12 |
13 | #
14 | # CLASS FACTORY SPECIFICATION
15 | #
16 | # Change these if you want to override any of the core classes
17 | # within the Pushlet framework with your own custom classes.
18 | #
19 | # Examples:
20 | # - custom SessionManager for authorisation
21 | # - maintain lists of active subjects (topics)
22 | # - send events on subscription
23 | # - plug in custom logging like log4j
24 | # Note that you must maintain the semantics of each class !
25 | # Below are the default properties for the core classes.
26 | controller.class=nl.justobjects.pushlet.core.Controller
27 | dispatcher.class=nl.justobjects.pushlet.core.Dispatcher
28 | logger.class=nl.justobjects.pushlet.util.Log4jLogger
29 | # logger.class=nl.justobjects.pushlet.util.DefaultLogger
30 | sessionmanager.class=nl.justobjects.pushlet.core.SessionManager
31 | session.class=nl.justobjects.pushlet.core.Session
32 | subscriber.class=nl.justobjects.pushlet.core.Subscriber
33 | subscription.class=nl.justobjects.pushlet.core.Subscription
34 |
35 | # sessionmanager.maxsessions=200
36 |
37 | #
38 | # DISPATCHER
39 | #
40 |
41 |
42 | # TODO: allow properties to be maintained in
43 | # a user dir
44 | # config.redirect=/etc/pushlet.properties
45 |
46 | #
47 | # LOGGING
48 | #
49 |
50 | # log level (trace(6) debug(5) info (4), warn(3), error(2), fatal(1))
51 | # default is info(4)
52 | log.level=4
53 |
54 | #
55 | # LOCAL EVENT SOURCES
56 | #
57 |
58 | # should local sources be loaded ?
59 | sources.activate=true
60 |
61 | #
62 | # SESSION
63 | #
64 |
65 |
66 | # algoritm to generate session key:
67 | # values: "randomstring" (default) or "uuid".
68 | # session.id.generation=uuid
69 | session.id.generation=randomstring
70 |
71 | # length of generated session key when using "randomstring" generation
72 | session.id.size=10
73 |
74 | # Overall session lease time in minutes
75 | # Mainly used for clients that do not perform
76 | # listening, e.g. when publishing only.
77 | session.timeout.mins=5
78 |
79 | #
80 | # EVENT QUEUE
81 | #
82 | # Properties for per-client data event queue
83 |
84 | # Size for
85 | queue.size=24
86 | queue.read.timeout.millis=20000
87 | queue.write.timeout.millis=20
88 |
89 | #
90 | # LISTENING MODE
91 | #
92 |
93 | # You may force all clients to use pull mode
94 | # for scalability
95 | listen.force.pull.all=false
96 |
97 | #
98 | # Comma-separated list of User Agent substrings.
99 | # Force these browsers to use pull mode, since they
100 | # don't support JS streaming, matching is done using
101 | # String.indexOf() with lowercased agent strings
102 | # use multiple criteria with &.
103 | #
104 | listen.force.pull.agents=safari
105 |
106 | #
107 | # PULL MODE
108 | #
109 |
110 | # time server should wait on refresing pull client
111 | pull.refresh.timeout.millis=45000
112 |
113 | # minimum/maximum wait time client should wait before refreshing
114 | # server provides a random time between these values
115 | pull.refresh.wait.min.millis=2000
116 | pull.refresh.wait.max.millis=6000
117 |
118 | #
119 | # POLL MODE
120 | #
121 |
122 | # time server should wait on refresing poll client
123 | poll.refresh.timeout.millis=60000
124 |
125 | # minimum/maximum wait time client should wait before refreshing
126 | # server provides a random time between these values
127 | poll.refresh.wait.min.millis=6000
128 | poll.refresh.wait.max.millis=10000
129 | unicast.type=first
130 |
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/src/servlet/MsgPushServlet.java:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/demo/UserUnicasttDemo/src/servlet/MsgPushServlet.java
--------------------------------------------------------------------------------
/demo/UserUnicasttDemo/src/sources.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Properties file for EventSource objects to be instantiated.
3 | #
4 | # Place this file in the CLASSPATH (e.g. WEB-INF/classes) or directly under WEB-INF.
5 | #
6 | # $Id: sources.properties,v 1.2 2007/11/10 14:12:16 justb Exp $
7 | #
8 | # Each EventSource is defined as =
9 | # 1. should be unique within this file but may be any name
10 | # 2. is the full class name
11 | #
12 | #
13 | # Define Pull Sources here. These classes must be derived from
14 | # nl.justobjects.pushlet.core.EventPullSource
15 | # Inner classes are separated with a $ sign from the outer class.
16 | #source1=nl.justobjects.pushlet.test.TestEventPullSources$TemperatureEventPullSource
17 | #source2=nl.justobjects.pushlet.test.TestEventPullSources$SystemStatusEventPullSource
18 | #source3=nl.justobjects.pushlet.test.TestEventPullSources$PushletStatusEventPullSource
19 | #source4=nl.justobjects.pushlet.test.TestEventPullSources$AEXStocksEventPullSource
20 | #source5=nl.justobjects.pushlet.test.TestEventPullSources$WebPresentationEventPullSource
21 | #source6=nl.justobjects.pushlet.test.TestEventPullSources$PingEventPullSource
22 |
23 | # TO BE DONE IN NEXT VERSION
24 | # define Push Sources here. These must implement the interface
25 | # nl.justobjects.pushlet.core.EventSource
26 |
27 |
--------------------------------------------------------------------------------
/doc/readme-en.md:
--------------------------------------------------------------------------------
1 | # pushlet-UserUnicast Manual
2 |
3 | ---------------
4 |
5 | pushlet-UserUnicast is based on a modified extension `JAVA comet` pushlet after implementation framework. Major expansion of the `unicast` real demand push function.
6 |
7 | > The method `unicast` pushlet can push data to the specified session (` SessionID`), the data is pushed to the specified user, since the `SessionID` can be randomly generated, can not be associated with a user, it is not according to the service field (such as the user ID, user name) and so pushed to the specified user.
8 |
9 | **Core implementation features:**
10 |
11 | - Push messages directly to a specified user by user ID (user name, primary key ID and other characters can be identified)
12 | - Supports multi-user connections push and control: `all`,` first`, `last` three types of switching
13 | - use `Event` be` setField () ` no need to convert the character encoding to support other character,
14 | - Easy to use
15 |
16 | **Explanation:**
17 |
18 | - Read this document, use pushlet-UserUnicast, need to master pushlet basis of lack of understanding pushlet unicast
19 |
20 | - This document includes only: How to use pushlet-UserUnicast achieve the specified user demand for data
21 |
22 | - This document does not include: Introduction pushlet's, pushlet use, pushlet the API. For more information, please consult other documentation.
23 |
24 | **Note:**
25 |
26 | - Due to restrictions push mechanism and the technology itself, `first` and` last` push is not absolutely accurate. This is because: After the user disconnects, and can not immediately detect and remove users from the queue (with a certain time lag), so if a user is disconnected immediately `first` or` last` push, might pushed to disconnected users. But in the time difference within the allowable range is safe, but `all` way when push absolutely safe.
27 |
28 | ## pushlet-userunicast API:
29 |
30 | ### Client:
31 | - `PL.userId = "userId"`:
32 | Use `PL.userId` attribute specifies the string that identifies the client, the server-side push based on this data to identify the client. Use the `; in` PL._init (). Such as:
33 | ```JS
34 | PL._init();
35 | PL.userId="JAY";
36 | ```
37 |
38 | ### Server:
39 |
40 | - `Dispatcher.getInstance().unicastUserId(Event, userId); `: Push data to the specified user ID client.
41 | - `Event`: Release event object and data
42 | - `userId`: User ID specified in the corresponding client `PL.userId`
43 | For example:
44 | ```JAVA
45 | String userId="JAY";
46 | //unicastUserId Pushed to the specified user
47 | Dispatcher.getInstance().unicastUserId(event, userId);
48 | ```
49 |
50 | - `Dispatcher.getInstance().unicastUserId(Event, userId, unicastType); `: In accordance with the specified user multiple connections unicastType push type, push data to the specified user ID client.
51 | - `Event`: Event objects and data release
52 | - `userId`: User ID specified in the corresponding client `PL.userId`
53 | - `unicastType`: Multi-user connections push type, optional value `all`,` first`, `last`(See Role: Multi-user connections configuration parameter). You can use constants, said: `Sys.USERUNICAST_ALL`, `Sys.USERUNICAST_First`, `Sys.USERUNICAST_Last`
54 | For example:
55 | ```JAVA
56 | String userId="JAY";
57 | //unicastUserId Push all connections to the specified user
58 | Dispatcher.getInstance().unicastUserId(event, userId, "all");
59 | ```
60 |
61 | ### Multi-user connection configuration parameters:
62 |
63 | When multiple clients to connect simultaneously use the same user ID `PL.userId` can be more connected to the push type parameters by` unicast.type` user, `unicastUserId (Event, userId)` method uses multiple connections that value as the user push the way.
64 |
65 | In `pushlet.properties` can configure` unicast.type` parameters, optional value:
66 | - `all`: The default value, the presence of the same user multiple client connections, the client connections to all push messages.
67 | - `first`: Only to the first client requests a connection push message, after connecting clients can not receive the message (when no other users connected to the same name, only to join the push, existing user connections, after connecting the user does not push).
68 | - `last`: Only client connection requests to the last push message.
69 |
70 |
71 |
72 | ## pushlet-userunicast Users push the use of steps and examples:
73 |
74 | 1. Jar package introduced for the project under lib:
75 | **pushlet-userunicast.jar**
76 | **log4j.jar**
77 |
78 | 2. Join the configuration file resource under the `src` or` WEB-INF` in:
79 | **log4j.properties**
80 | **pushlet.properties**
81 | **sources.properties**
82 |
83 | 3. Need to subscribe to an event added to the Web project client under the client JS file:
84 | **ajax-pushlet-client.js**
85 |
86 | 4. In `web.xml` core Servlet controller configuration` pushlet`
87 | ```XML
88 |
89 | pushlet
90 |
91 | nl.justobjects.pushlet.servlet.Pushlet
92 |
93 | 1
94 |
95 |
96 | pushlet
97 | /pushlet.srv
98 |
99 | ```
100 |
101 | 5. Clients subscribe to receive messages index.jsp
102 | Use `PL.userId` attribute specifies the string that identifies the client, server-side push data in accordance with this identity to the client
103 | ```JS
104 |
105 |
106 |
107 | Users receive a message page
108 |
109 |
110 |
126 |
127 |
128 |
129 | Please wait, Server Connecting....
130 |
131 |
132 |
133 |
134 | Push back page news
135 |
136 |
137 | ```
138 |
139 | 6. Servlet server-side push test data
140 | Use `Dispatcher.getInstance () unicastUserId (Event, userId user ID);` push data to the specified client user ID, `userId` user ID corresponding `PL.userId`.
141 | ```JAVA
142 | package servlet;
143 |
144 | import java.io.IOException;
145 |
146 | import javax.servlet.ServletException;
147 | import javax.servlet.http.HttpServlet;
148 | import javax.servlet.http.HttpServletRequest;
149 | import javax.servlet.http.HttpServletResponse;
150 |
151 | import nl.justobjects.pushlet.core.Dispatcher;
152 | import nl.justobjects.pushlet.core.Event;
153 |
154 | public class MsgPushServlet extends HttpServlet {
155 |
156 | @Override
157 | protected void service(HttpServletRequest req, HttpServletResponse resp)
158 | throws ServletException, IOException {
159 | req.setCharacterEncoding("utf-8");
160 | // Push message
161 | String msg=req.getParameter("msg");
162 | // String userId="JAY";
163 | // Push users
164 | String userId=req.getParameter("userId");
165 | // Push-user multi-way connection
166 | String type=req.getParameter("type");
167 |
168 | // Event objects and data
169 | Event event = Event.createDataEvent("/push/hello");
170 | event.setField("msg", msg); //Chinese do not need to be converted to ISO-8859-1
171 |
172 | // According unicast.type push parameter values to the specified user pushlet.properties
173 | // Dispatcher.getInstance().unicastUserId(event,userId);
174 |
175 | // Pushed to the specified user from the specified type type
176 | Dispatcher.getInstance().unicastUserId(event, userId, type);
177 | }
178 | }
179 | ```
180 | The servlet web.xml configuration
181 | ```XML
182 |
183 | MsgPushServlet
184 | servlet.MsgPushServlet
185 |
186 |
187 |
188 | MsgPushServlet
189 | /servlet/MsgPushServlet
190 |
191 | ```
192 |
193 | 7. Call Servlet, publish pages publish.jsp push message
194 | ```HTML
195 |
196 |
197 |
198 | Published push message
199 |
200 |
201 |
202 | Published need to push the message:
203 |
221 |
222 |
223 |
224 |
225 | ```
226 |
227 |
228 |
229 |
230 | ## End
231 |
232 | [Demo online](http://www.easyproject.cn/easyunicast/en/index.jsp#demo 'Demo online')
233 |
234 | [Comments](http://www.easyproject.cn/easyunicast/en/index.jsp#about 'Comments')
235 |
236 | If you have more comments, suggestions or ideas, please contact me.
237 |
238 |
239 | Contact, feedback, customization, training Email:
240 |
241 |
242 |
250 |
251 |
252 | [http://www.easyproject.cn](http://www.easyproject.cn "EasyProject Home")
--------------------------------------------------------------------------------
/doc/readme-zh.md:
--------------------------------------------------------------------------------
1 | # pushlet-UserUnicast 使用手册
2 |
3 | ---------------
4 |
5 | pushlet-UserUnicast 是一个基于pushlet修改扩展之后的`JAVA comet`实现框架。主要扩展实了`unicast`点播推送功能。
6 |
7 | > pushlet中的 `unicast` 方法可以实现向指定会话(`SessionID`)推送数据,将数据推送到指定用户,可以由于该 `SessionID` 是随机生成,无法与用户关联,所以无法根据业务字段(如用户ID,用户名)等推送给指定用户。
8 |
9 | **核心实现特点:**
10 |
11 | - 可以通过用户标识(用户名,主键ID等字符标识均可)向指定用户直接推送消息
12 | - 支持用户多连接的推送和控制:`all`, `first`, `last` 三种类型切换
13 | - 支持中文,使用 `Event` 进行`setField()`时无需转换字符编码
14 | - 简单易用
15 |
16 | **说明:**
17 |
18 | - 阅读本文档、使用 pushlet-UserUnicast,需要掌握 pushlet 基础,了解 pushlet unicast 的不足
19 |
20 | - 本文档仅包括:如何利用 pushlet-UserUnicast 实现对指定用户点播数据
21 |
22 | - 本文档不包括:pushlet的介绍、pushlet使用方法、pushlet的API。如需了解,请查阅其他文档。
23 |
24 | **注意:**
25 |
26 | - 由于推送机制和技术本身的限制,`first`和`last`推送并非是绝对精确的。这是因为:用户断开后,并不能立即检测到并从队列移除用户(有一定时间差),所以如果在某个用户断开连接后,立即进行`first`或`last`推送,可能会推送给已断开的用户。但在时间差允许范围内是安全的,而`all`方式推送时绝对安全的。
27 |
28 | ## pushlet-userunicast API:
29 |
30 | ### 客户端:
31 | - `PL.userId = "userId"`:
32 | 使用`PL.userId`属性指定客户端的字符串标识,服务器端根据此标识推送数据到该客户端。在 `PL._init();` 后使用。例如:
33 | ```JS
34 | PL._init();
35 | PL.userId="JAY";
36 | ```
37 |
38 | ### 服务端:
39 |
40 | - `Dispatcher.getInstance().unicastUserId(Event, userId); `:推送数据到指定用户标识的客户端。
41 | - `Event`: 发布的事件对象和数据
42 | - `userId`: 用户标识,对应客户端指定的 `PL.userId`
43 | 例如:
44 | ```JAVA
45 | String userId="JAY";
46 | //unicastUserId 推送给指定用户
47 | Dispatcher.getInstance().unicastUserId(event, userId);
48 | ```
49 |
50 | - `Dispatcher.getInstance().unicastUserId(Event, userId, unicastType); `:按照unicastType指定的用户多连接推送类型,推送数据到指定用户标识的客户端。
51 | - `Event`: 发布的事件对象和数据
52 | - `userId`: 用户标识,对应客户端指定的 `PL.userId`
53 | - `unicastType`: 用户多连接推送类型,可选值为 `all`, `first`, `last`(作用参见:用户多连接配置参数)。可使用常量表示:`Sys.USERUNICAST_ALL`, `Sys.USERUNICAST_First`, `Sys.USERUNICAST_Last`
54 | 例如:
55 | ```JAVA
56 | String userId="JAY";
57 | //unicastUserId 推送给指定用户的所有连接
58 | Dispatcher.getInstance().unicastUserId(event, userId, "all");
59 | ```
60 |
61 | ### 用户多连接配置参数:
62 |
63 | 当多个客户端连接同时使用相同`PL.userId`用户标识时,可以通过 `unicast.type` 参数用户多连接推送类型,`unicastUserId(Event, userId)`方法会使用该值作为用户多连接推送方式。
64 |
65 | 在 `pushlet.properties` 中可以配置 `unicast.type` 参数,可选值为:
66 | - `all`: 默认值,同一个用户存在多个客户端连接时,向全部客户端连接都推送消息。
67 | - `first`: 仅向最先请求连接的客户端推送消息,后连接的客户端无法接收到消息(当没有其他同名用户连接时,才加入推送,已有用户连接,后连接用户不推送)。
68 | - `last`: 仅向最后请求连接的客户端推消息。
69 |
70 |
71 |
72 | ## pushlet-userunicast 用户推送使用步骤和示例:
73 |
74 | 1. 为项目引入lib下的jar包:
75 | **pushlet-userunicast.jar**
76 | **log4j.jar**
77 |
78 | 2. 在 `src` 或 `WEB-INF` 中加入resource下的配置文件:
79 | **log4j.properties**
80 | **pushlet.properties**
81 | **sources.properties**
82 |
83 | 3. 在需要订阅事件的 Web 项目中加入client下的客户端JS文件:
84 | **ajax-pushlet-client.js**
85 |
86 | 4. 在 `web.xml` 配置 `pushlet` 核心Servlet控制器
87 | ```XML
88 |
240 |
241 |
242 |
243 | 支付宝钱包扫一扫捐助:
244 |
245 |
246 |
247 |
248 |
249 |
250 | [http://www.easyproject.cn](http://www.easyproject.cn "EasyProject Home")
--------------------------------------------------------------------------------
/lib/log4j.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/lib/log4j.jar
--------------------------------------------------------------------------------
/lib/pushlet-userunicast.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/lib/pushlet-userunicast.jar
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # pushlet-UserUnicast
2 |
3 | ---------------
4 |
5 | 基于pushlet的用户点播JAVA comet实现框架。
6 |
7 | - ### [中文](doc/readme-zh.md)
8 |
9 |
10 | JAVA COMET UserUnicast framework based on Pushlet.
11 |
12 | - ### [English](doc/readme-en.md)
13 |
14 |
15 | ##
16 |
17 |
18 | 如果您有更好意见,建议或想法,请联系我。
19 |
20 | If you have more comments, suggestions or ideas, please contact me.
21 |
22 | Email:
23 |
24 | [http://www.easyproject.cn](http://www.easyproject.cn "EasyProject Home")
--------------------------------------------------------------------------------
/resource/log4j.properties:
--------------------------------------------------------------------------------
1 | # Sample log4j config, adapt to your own logging needs
2 | # $Id: log4j.properties,v 1.1 2007/12/07 12:57:40 justb Exp $
3 | #
4 | # ***** Set root logger level to WARN and its two appenders to stdout and R.
5 | log4j.rootLogger=warn, stdout
6 |
7 | # ***** stdout is set to be a ConsoleAppender.
8 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
9 | # ***** stdout uses PatternLayout.
10 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
11 | # ***** Pattern to output the caller's file name and line number.
12 | log4j.appender.stdout.layout.ConversionPattern=%d{yy-MM-dd@HH:mm:ss} %-5p (pushlet) - %m%n
13 |
14 | # ***** R is set to be a RollingFileAppender.
15 | # log4j.appender.R=org.apache.log4j.RollingFileAppender
16 | # log4j.appender.R.File=/Users/just/example.log
17 | # ***** Max file size is set to 100KB
18 | # log4j.appender.R.MaxFileSize=100KB
19 | # ***** Keep one backup file
20 | # log4j.appender.R.MaxBackupIndex=1
21 | # ***** R uses PatternLayout.
22 | # log4j.appender.R.layout=org.apache.log4j.PatternLayout
23 | # log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
24 |
--------------------------------------------------------------------------------
/resource/pushlet.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Pushlet configuration.
3 | # Place this file in the CLASSPATH (e.g. WEB-INF/classes) or directly under WEB-INF.
4 | #
5 | # $Id: pushlet.properties,v 1.13 2007/12/07 12:57:40 justb Exp $
6 | #
7 |
8 | #
9 | #
10 | #
11 | config.version=1.0.2
12 |
13 | #
14 | # CLASS FACTORY SPECIFICATION
15 | #
16 | # Change these if you want to override any of the core classes
17 | # within the Pushlet framework with your own custom classes.
18 | #
19 | # Examples:
20 | # - custom SessionManager for authorisation
21 | # - maintain lists of active subjects (topics)
22 | # - send events on subscription
23 | # - plug in custom logging like log4j
24 | # Note that you must maintain the semantics of each class !
25 | # Below are the default properties for the core classes.
26 | controller.class=nl.justobjects.pushlet.core.Controller
27 | dispatcher.class=nl.justobjects.pushlet.core.Dispatcher
28 | logger.class=nl.justobjects.pushlet.util.Log4jLogger
29 | # logger.class=nl.justobjects.pushlet.util.DefaultLogger
30 | sessionmanager.class=nl.justobjects.pushlet.core.SessionManager
31 | session.class=nl.justobjects.pushlet.core.Session
32 | subscriber.class=nl.justobjects.pushlet.core.Subscriber
33 | subscription.class=nl.justobjects.pushlet.core.Subscription
34 |
35 | # sessionmanager.maxsessions=200
36 |
37 | #
38 | # DISPATCHER
39 | #
40 |
41 |
42 | # TODO: allow properties to be maintained in
43 | # a user dir
44 | # config.redirect=/etc/pushlet.properties
45 |
46 | #
47 | # LOGGING
48 | #
49 |
50 | # log level (trace(6) debug(5) info (4), warn(3), error(2), fatal(1))
51 | # default is info(4)
52 | log.level=4
53 |
54 | #
55 | # LOCAL EVENT SOURCES
56 | #
57 |
58 | # should local sources be loaded ?
59 | sources.activate=true
60 |
61 | #
62 | # SESSION
63 | #
64 |
65 |
66 | # algoritm to generate session key:
67 | # values: "randomstring" (default) or "uuid".
68 | # session.id.generation=uuid
69 | session.id.generation=randomstring
70 |
71 | # length of generated session key when using "randomstring" generation
72 | session.id.size=10
73 |
74 | # Overall session lease time in minutes
75 | # Mainly used for clients that do not perform
76 | # listening, e.g. when publishing only.
77 | session.timeout.mins=5
78 |
79 | #
80 | # EVENT QUEUE
81 | #
82 | # Properties for per-client data event queue
83 |
84 | # Size for
85 | queue.size=24
86 | queue.read.timeout.millis=20000
87 | queue.write.timeout.millis=20
88 |
89 | #
90 | # LISTENING MODE
91 | #
92 |
93 | # You may force all clients to use pull mode
94 | # for scalability
95 | listen.force.pull.all=false
96 |
97 | #
98 | # Comma-separated list of User Agent substrings.
99 | # Force these browsers to use pull mode, since they
100 | # don't support JS streaming, matching is done using
101 | # String.indexOf() with lowercased agent strings
102 | # use multiple criteria with &.
103 | #
104 | listen.force.pull.agents=safari
105 |
106 | #
107 | # PULL MODE
108 | #
109 |
110 | # time server should wait on refresing pull client
111 | pull.refresh.timeout.millis=45000
112 |
113 | # minimum/maximum wait time client should wait before refreshing
114 | # server provides a random time between these values
115 | pull.refresh.wait.min.millis=2000
116 | pull.refresh.wait.max.millis=6000
117 |
118 | #
119 | # POLL MODE
120 | #
121 |
122 | # time server should wait on refresing poll client
123 | poll.refresh.timeout.millis=60000
124 |
125 | # minimum/maximum wait time client should wait before refreshing
126 | # server provides a random time between these values
127 | poll.refresh.wait.min.millis=6000
128 | poll.refresh.wait.max.millis=10000
129 | unicast.type=all
130 |
--------------------------------------------------------------------------------
/resource/sources.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Properties file for EventSource objects to be instantiated.
3 | #
4 | # Place this file in the CLASSPATH (e.g. WEB-INF/classes) or directly under WEB-INF.
5 | #
6 | # $Id: sources.properties,v 1.2 2007/11/10 14:12:16 justb Exp $
7 | #
8 | # Each EventSource is defined as =
9 | # 1. should be unique within this file but may be any name
10 | # 2. is the full class name
11 | #
12 | #
13 | # Define Pull Sources here. These classes must be derived from
14 | # nl.justobjects.pushlet.core.EventPullSource
15 | # Inner classes are separated with a $ sign from the outer class.
16 | #source1=nl.justobjects.pushlet.test.TestEventPullSources$TemperatureEventPullSource
17 | #source2=nl.justobjects.pushlet.test.TestEventPullSources$SystemStatusEventPullSource
18 | #source3=nl.justobjects.pushlet.test.TestEventPullSources$PushletStatusEventPullSource
19 | #source4=nl.justobjects.pushlet.test.TestEventPullSources$AEXStocksEventPullSource
20 | #source5=nl.justobjects.pushlet.test.TestEventPullSources$WebPresentationEventPullSource
21 | #source6=nl.justobjects.pushlet.test.TestEventPullSources$PingEventPullSource
22 |
23 | # TO BE DONE IN NEXT VERSION
24 | # define Push Sources here. These must implement the interface
25 | # nl.justobjects.pushlet.core.EventSource
26 |
27 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/Version.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet;
5 |
6 | /**
7 | * Version info class.
8 | *
9 | * Purpose
10 | * Extract version info from jar manifest file.
11 | *
12 | * @author Just van den Broecke
13 | * @version $Id: Version.java,v 1.4 2006/05/06 00:10:11 justb Exp $
14 | */
15 |
16 | public class Version {
17 | /** Version info extracted from the .jar manifest file (see build.xml and build.properties). */
18 | public static final String SOFTWARE_VERSION = Version.class.getPackage().getSpecificationVersion();
19 | public static final String BUILD_DATE = Version.class.getPackage().getImplementationVersion();
20 | }
21 |
22 | /*
23 | * $Log: Version.java,v $
24 | * Revision 1.4 2006/05/06 00:10:11 justb
25 | * various chgs but not too serious...
26 | *
27 | * Revision 1.3 2004/02/08 16:07:55 justb
28 | * *** empty log message ***
29 | *
30 | * Revision 1.2 2003/08/15 08:37:40 justb
31 | * fix/add Copyright+LGPL file headers and footers
32 | *
33 | * Revision 1.1 2003/08/11 21:29:48 justb
34 | * first checkin
35 | *
36 | *
37 | */
38 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/client/PushletClient.java.patch:
--------------------------------------------------------------------------------
1 | --- PushletClient.java 6 May 2005 20:08:20 -0000 1.16
2 | +++ PushletClient.java 28 Oct 2006 23:19:12 -0000
3 | @@ -142,7 +142,13 @@
4 | }
5 |
6 | dataEventListener = new DataEventListener(aListener, listenURL);
7 | - dataEventListener.start();
8 | + synchronized (dataEventListener) {
9 | + dataEventListener.start();
10 | + try {
11 | + dataEventListener.wait();
12 | + } catch (InterruptedException e) {
13 | + }
14 | + }
15 | }
16 |
17 | /** Immediate listener. */
18 | @@ -354,6 +360,11 @@
19 | // Connect to server
20 | reader = openURL(listenURL);
21 |
22 | + synchronized (this) {
23 | + // Inform the calling thread we're ready to receive events.
24 | + this.notify();
25 | + }
26 | +
27 | // Get events while we're alive.
28 | while (receiveThread != null && receiveThread.isAlive()) {
29 | Event event = null;
30 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/client/PushletClientListener.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.client;
5 |
6 | import nl.justobjects.pushlet.core.Event;
7 | import nl.justobjects.pushlet.core.Protocol;
8 |
9 | /**
10 | * Interface for listener of the PushletClient object.
11 | *
12 | * @version $Id: PushletClientListener.java,v 1.5 2005/02/21 11:50:37 justb Exp $
13 | * @author Just van den Broecke - Just Objects ©
14 | **/
15 | public interface PushletClientListener extends Protocol {
16 | /** Abort event from server. */
17 | public void onAbort(Event theEvent);
18 |
19 | /** Data event from server. */
20 | public void onData(Event theEvent);
21 |
22 | /** Heartbeat event from server. */
23 | public void onHeartbeat(Event theEvent);
24 |
25 | /** Error occurred. */
26 | public void onError(String message);
27 | }
28 |
29 | /*
30 | * $Log: PushletClientListener.java,v $
31 | * Revision 1.5 2005/02/21 11:50:37 justb
32 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
33 | *
34 | * Revision 1.4 2005/02/15 15:46:31 justb
35 | * client API improves
36 | *
37 | * Revision 1.3 2004/10/24 12:58:18 justb
38 | * revised client and test classes for new protocol
39 | *
40 | * Revision 1.2 2004/09/03 22:35:37 justb
41 | * Almost complete rewrite, just checking in now
42 | *
43 | * Revision 1.1 2004/03/10 20:14:17 justb
44 | * renamed all *JavaPushletClient* to *PushletClient*
45 | *
46 | * Revision 1.3 2003/08/15 08:37:40 justb
47 | * fix/add Copyright+LGPL file headers and footers
48 | *
49 | *
50 | */
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/BrowserAdapter.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.Log;
7 |
8 | import javax.servlet.http.HttpServletResponse;
9 | import java.io.IOException;
10 | import java.io.PrintWriter;
11 | import java.util.Iterator;
12 |
13 | /**
14 | * Generic implementation of ClientAdapter for browser clients.
15 | *
16 | * @author Just van den Broecke - Just Objects ©
17 | * @version $Id: BrowserAdapter.java,v 1.6 2007/11/09 13:15:35 justb Exp $
18 | */
19 | public class BrowserAdapter implements ClientAdapter, Protocol {
20 |
21 | public static final String START_DOCUMENT =
22 | ""
23 | + ""
24 | + "\n";
25 | public static final String END_DOCUMENT = "";
26 |
27 | private PrintWriter servletOut;
28 | private HttpServletResponse servletRsp;
29 | private int bytesSent;
30 |
31 | /**
32 | * Constructor.
33 | */
34 | public BrowserAdapter(HttpServletResponse aServletResponse) {
35 | servletRsp = aServletResponse;
36 | }
37 |
38 | /**
39 | * Generic init.
40 | */
41 | public void start() throws IOException {
42 | // Keep servlet request/response objects until page ends in stop()
43 | // Content type as HTML
44 | servletRsp.setStatus(HttpServletResponse.SC_OK);
45 | servletRsp.setContentType("text/html;charset=UTF-8");
46 |
47 | // http://www.junlu.com/msg/45902.html
48 | // Log.debug("bufsize=" + aRsp.getBufferSize());
49 | servletOut = servletRsp.getWriter();
50 | send(START_DOCUMENT);
51 | }
52 |
53 | /**
54 | * Push Event to client.
55 | */
56 | public void push(Event anEvent) throws IOException {
57 | Log.debug("BCA event=" + anEvent.toXML());
58 |
59 | // Check if we should refresh
60 | if (anEvent.getEventType().equals(Protocol.E_REFRESH)) {
61 | // Append refresh and tail of HTML document
62 | // Construct the JS callback line to be sent as last line of doc.
63 | // This will refresh the request using the unique id to determine
64 | // the subscriber instance on the server. The client will wait for
65 | // a number of milliseconds.
66 | long refreshWaitMillis = Long.parseLong(anEvent.getField(P_WAIT));
67 |
68 | // Create servlet request for requesting next events (refresh)
69 | String url = anEvent.getField(P_URL);
70 | String jsRefreshTrigger = "\n";
71 |
72 |
73 | send(jsRefreshTrigger + END_DOCUMENT);
74 | } else {
75 | send(event2JavaScript(anEvent));
76 | }
77 | }
78 |
79 | /**
80 | * End HTML page in client browser.
81 | */
82 | public void stop() {
83 | // To be garbage collected if adapter remains active
84 | servletOut = null;
85 | }
86 |
87 | /**
88 | * Send any string to browser.
89 | */
90 | protected void send(String s) throws IOException {
91 | // Send string to browser.
92 | // Log.debug("Adapter: sending: " + s);
93 | if (servletOut == null) {
94 | throw new IOException("Client adapter was stopped");
95 | }
96 |
97 | servletOut.print(s);
98 |
99 | servletOut.flush();
100 |
101 | // Note: this doesn't seem to have effect
102 | // in Tomcat 4/5 if the client already disconnected.
103 | servletRsp.flushBuffer();
104 |
105 | bytesSent += s.length();
106 | Log.debug("bytesSent= " + bytesSent);
107 | // Log.debug("BCA sent event: " + s);
108 | }
109 |
110 | /**
111 | * Converts the Java Event to a JavaScript function call in browser page.
112 | */
113 | protected String event2JavaScript(Event event) throws IOException {
114 |
115 | // Convert the event to a comma-separated string.
116 | String jsArgs = "";
117 | for (Iterator iter = event.getFieldNames(); iter.hasNext();) {
118 | String name = (String) iter.next();
119 | String value = event.getField(name);
120 | String nextArgument = (jsArgs.equals("") ? "" : ",") + "'" + name + "'" + ", \"" + value + "\"";
121 | jsArgs += nextArgument;
122 | }
123 |
124 | // Construct and return the function call */
125 | return "";
126 | }
127 |
128 | }
129 |
130 | /*
131 | * $Log: BrowserAdapter.java,v $
132 | * Revision 1.6 2007/11/09 13:15:35 justb
133 | * add charset=UTF-8 in returned HTTP content types
134 | *
135 | * Revision 1.5 2006/05/15 11:52:53 justb
136 | * updates mainly for AJAX client
137 | *
138 | * Revision 1.4 2006/05/06 00:10:11 justb
139 | * various chgs but not too serious...
140 | *
141 | * Revision 1.3 2005/02/28 12:45:59 justb
142 | * introduced Command class
143 | *
144 | * Revision 1.2 2005/02/21 11:50:44 justb
145 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
146 | *
147 | * Revision 1.1 2005/02/18 10:07:23 justb
148 | * many renamings of classes (make names compact)
149 | *
150 | * Revision 1.12 2005/02/15 13:30:23 justb
151 | * changes for Tomcat buffering (now working in tc4 and 5.0)
152 | *
153 | * Revision 1.11 2005/01/24 22:45:58 justb
154 | * getting safari to work
155 | *
156 | * Revision 1.10 2005/01/18 16:46:27 justb
157 | * buffer size setting ignored by tomcat workings
158 | *
159 | * Revision 1.9 2004/10/24 12:58:18 justb
160 | * revised client and test classes for new protocol
161 | *
162 | * Revision 1.8 2004/09/20 22:01:38 justb
163 | * more changes for new protocol
164 | *
165 | * Revision 1.7 2004/09/03 22:35:37 justb
166 | * Almost complete rewrite, just checking in now
167 | *
168 | * Revision 1.6 2004/08/15 16:00:15 justb
169 | * enhancements to pull mode
170 | *
171 | * Revision 1.5 2004/08/13 23:36:05 justb
172 | * rewrite of Pullet into Pushlet "pull" mode
173 | *
174 | * Revision 1.4 2003/08/15 08:37:40 justb
175 | * fix/add Copyright+LGPL file headers and footers
176 | *
177 | * Revision 1.3 2003/08/12 09:57:05 justb
178 | * replaced all print statements to Log.*() calls
179 | *
180 | * Revision 1.2 2003/05/18 16:15:07 justb
181 | * support for XML encoded Events
182 | *
183 | * Revision 1.1.1.1 2002/09/24 21:02:30 justb
184 | * import to sourceforge
185 | *
186 | * Revision 1.1.1.1 2002/09/20 22:48:17 justb
187 | * import to SF
188 | *
189 | * Revision 1.1.1.1 2002/09/20 14:19:02 justb
190 | * first import into SF
191 | *
192 | * Revision 1.5 2002/04/15 20:42:41 just
193 | * reformatting and renaming GuardedQueue to EventQueue
194 | *
195 | * Revision 1.4 2000/12/27 22:39:35 just
196 | * no message
197 | *
198 | * Revision 1.3 2000/10/30 14:15:47 just
199 | * no message
200 | *
201 | *
202 | */
203 |
204 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/ClientAdapter.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import java.io.IOException;
7 |
8 | /**
9 | * Adapter interface for encapsulation of specific HTTP clients.
10 | *
11 | * @author Just van den Broecke - Just Objects ©
12 | * @version $Id: ClientAdapter.java,v 1.8 2007/11/23 14:33:07 justb Exp $
13 | */
14 | public interface ClientAdapter {
15 |
16 | /**
17 | * Start event push.
18 | */
19 | public void start() throws IOException;
20 |
21 | /**
22 | * Push single Event to client.
23 | */
24 | public void push(Event anEvent) throws IOException;
25 |
26 | /**
27 | * Stop event push.
28 | */
29 | public void stop() throws IOException;
30 | }
31 |
32 | /*
33 | * $Log: ClientAdapter.java,v $
34 | * Revision 1.8 2007/11/23 14:33:07 justb
35 | * core classes now configurable through factory
36 | *
37 | * Revision 1.7 2005/02/28 12:45:59 justb
38 | * introduced Command class
39 | *
40 | * Revision 1.6 2005/02/21 11:50:45 justb
41 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
42 | *
43 | * Revision 1.5 2005/02/18 10:07:23 justb
44 | * many renamings of classes (make names compact)
45 | *
46 | * Revision 1.4 2004/09/03 22:35:37 justb
47 | * Almost complete rewrite, just checking in now
48 | *
49 | * Revision 1.3 2003/08/15 08:37:40 justb
50 | * fix/add Copyright+LGPL file headers and footers
51 | *
52 | * Revision 1.2 2003/05/18 16:15:08 justb
53 | * support for XML encoded Events
54 | *
55 | * Revision 1.1.1.1 2002/09/24 21:02:30 justb
56 | * import to sourceforge
57 | *
58 | * Revision 1.1.1.1 2002/09/20 22:48:17 justb
59 | * import to SF
60 | *
61 | * Revision 1.1.1.1 2002/09/20 14:19:03 justb
62 | * first import into SF
63 | *
64 | * Revision 1.3 2002/04/15 20:42:41 just
65 | * reformatting and renaming GuardedQueue to EventQueue
66 | *
67 | * Revision 1.2 2000/08/21 20:48:29 just
68 | * added CVS log and id tags plus copyrights
69 | *
70 | *
71 | */
72 |
73 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/Command.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.PushletException;
7 | import nl.justobjects.pushlet.util.Servlets;
8 |
9 | import javax.servlet.http.HttpServletRequest;
10 | import javax.servlet.http.HttpServletResponse;
11 |
12 | /**
13 | * Wraps pushlet request/response data.
14 | *
15 | * @author Just van den Broecke - Just Objects ©
16 | * @version $Id: Command.java,v 1.4 2007/11/23 14:33:07 justb Exp $
17 | */
18 | public class Command implements Protocol {
19 |
20 | /**
21 | * Pushlet request event.
22 | */
23 | public final Event reqEvent;
24 |
25 | /**
26 | * Pushlet response event.
27 | */
28 | private Event rspEvent;
29 |
30 | /**
31 | * HTTP Servlet GET/POST request.
32 | */
33 | public final HttpServletRequest httpReq;
34 |
35 | /**
36 | * HTTP Servlet GET/POST response.
37 | */
38 | public final HttpServletResponse httpRsp;
39 |
40 | /**
41 | * Pushlet session.
42 | */
43 | public final Session session;
44 |
45 | /**
46 | * Per-response client adapter.
47 | */
48 | private ClientAdapter clientAdapter;
49 |
50 | /**
51 | * Constructor.
52 | */
53 | private Command(Session aSession, Event aRequestEvent, HttpServletRequest aHTTPReq, HttpServletResponse aHTTPRsp) {
54 | session = aSession;
55 | reqEvent = aRequestEvent;
56 | httpReq = aHTTPReq;
57 | httpRsp = aHTTPRsp;
58 | }
59 |
60 | /**
61 | * Create new Command object.
62 | */
63 | public static Command create(Session aSession, Event aReqEvent, HttpServletRequest aHTTPReq, HttpServletResponse aHTTPRsp) {
64 | return new Command(aSession, aReqEvent, aHTTPReq, aHTTPRsp);
65 | }
66 |
67 | /**
68 | * Set pushlet response event.
69 | */
70 | public void setResponseEvent(Event aResponseEvent) {
71 | rspEvent = aResponseEvent;
72 | }
73 |
74 | /**
75 | * Get pushlet response event.
76 | */
77 | public Event getResponseEvent() {
78 | return rspEvent;
79 | }
80 |
81 | /**
82 | * Get client adapter for request.
83 | */
84 | public ClientAdapter getClientAdapter() throws PushletException {
85 | if (clientAdapter == null) {
86 | // Create and initialize client-specific adapter.
87 | clientAdapter = createClientAdapter();
88 | }
89 | return clientAdapter;
90 | }
91 |
92 | /**
93 | * Create client notifier based on "format" parameter passed in request.
94 | */
95 | protected ClientAdapter createClientAdapter() throws PushletException {
96 |
97 | // Assumed to be set by parent.
98 | String outputFormat = session.getFormat();
99 |
100 | // Determine client adapter to create.
101 | if (outputFormat.equals(FORMAT_JAVASCRIPT)) {
102 | // Client expects to receive Events as JavaScript dispatch calls..
103 | return new BrowserAdapter(httpRsp);
104 | } else if (outputFormat.equals(FORMAT_SERIALIZED_JAVA_OBJECT)) {
105 | // Client expects to receive Events as Serialized Java Objects.
106 | return new SerializedAdapter(httpRsp);
107 | } else if (outputFormat.equals(FORMAT_XML)) {
108 | // Client expects to receive Events as stream of XML docs.
109 | return new XMLAdapter(httpRsp);
110 | } else if (outputFormat.equals(FORMAT_XML_STRICT)) {
111 | // Client expects to receive Events embedded in single XML doc.
112 | return new XMLAdapter(httpRsp, true);
113 | } else {
114 | throw new PushletException("Null or invalid output format: " + outputFormat);
115 | }
116 | }
117 |
118 | /**
119 | * Sends HTTP response headers.
120 | */
121 | protected void sendResponseHeaders() {
122 | // Just to try to prevent caching in any form.
123 | Servlets.setNoCacheHeaders(httpRsp);
124 |
125 | // Close connection for Java enabled browsers
126 | if (session.getUserAgent().indexOf("java") > 0) {
127 | // The connection should be closed after this request
128 | // NB: this allows sending a "long response". Some clients
129 | // in particular java.net.URL in VMs > 1.1 that use HTTP/1.1
130 | // will block if
131 | // - the content length is not sent
132 | // - if Connection: close HTTP header is not sent.
133 | //
134 | // Since we don't know the content length we will assume
135 | // the underlying servlet engine will use chunked encoding.
136 | httpRsp.setHeader("Connection", "close");
137 | }
138 | }
139 |
140 |
141 | }
142 |
143 | /*
144 | * $Log: Command.java,v $
145 | * Revision 1.4 2007/11/23 14:33:07 justb
146 | * core classes now configurable through factory
147 | *
148 | * Revision 1.3 2005/05/06 19:44:00 justb
149 | * added xml-strict format
150 | *
151 | * Revision 1.2 2005/02/28 17:25:15 justb
152 | * commented
153 | *
154 | * Revision 1.1 2005/02/28 12:45:59 justb
155 | * introduced Command class
156 | *
157 | *
158 | */
159 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/Config.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.Log;
7 | import nl.justobjects.pushlet.util.PushletException;
8 | import nl.justobjects.pushlet.util.Sys;
9 |
10 | import java.io.File;
11 | import java.util.Properties;
12 |
13 | /**
14 | * Loads and maintains overall configuration.
15 | *
16 | * @author Just van den Broecke - Just Objects ©
17 | * @version $Id: Config.java,v 1.5 2007/11/23 21:10:17 justb Exp $
18 | */
19 | public class Config implements ConfigDefs {
20 | private static final String PROPERTIES_FILE = "pushlet.properties";
21 | private static Properties properties;
22 |
23 | /**
24 | * Factory method: create object from property denoting class name.
25 | *
26 | * @param aClassNameProp property name e.g. "session.class"
27 | * @return an instance of class denoted by property
28 | * @throws PushletException when class cannot be instantiated
29 | */
30 | public static Object createObject(String aClassNameProp, String aDefault) throws PushletException {
31 | Class clazz = getClass(aClassNameProp, aDefault);
32 | try {
33 | return clazz.newInstance();
34 | } catch (Throwable t) {
35 | // Usually a misconfiguration
36 | throw new PushletException("Cannot instantiate class for " + aClassNameProp + "=" + clazz, t);
37 | }
38 | }
39 |
40 | /**
41 | * Factory method: create object from property denoting class name.
42 | *
43 | * @param aClassNameProp property name e.g. "session.class"
44 | * @return a Class object denoted by property
45 | * @throws PushletException when class cannot be instantiated
46 | */
47 | public static Class getClass(String aClassNameProp, String aDefault) throws PushletException {
48 | // Singleton + factory pattern: create object instance
49 | // from configured class name
50 | String clazz = (aDefault == null ? getProperty(aClassNameProp) : getProperty(aClassNameProp, aDefault));
51 |
52 | try {
53 | return Class.forName(clazz);
54 | } catch (ClassNotFoundException t) {
55 | // Usually a misconfiguration
56 | throw new PushletException("Cannot find class for " + aClassNameProp + "=" + clazz, t);
57 | }
58 | }
59 |
60 | /**
61 | * Initialize event sources from properties file.
62 | */
63 | public static void load(String aDirPath) {
64 | // Load Event sources using properties file.
65 | try {
66 | // Try loading through classpath first (e.g. in WEB-INF/classes or from .jar)
67 | Log.info("Config: loading " + PROPERTIES_FILE + " from classpath");
68 | properties = Sys.loadPropertiesResource(PROPERTIES_FILE);
69 | } catch (Throwable t) {
70 | // Try from provided dir (e.g. WEB_INF/pushlet.properties)
71 | String filePath = aDirPath + File.separator + PROPERTIES_FILE;
72 | Log.info("Config: cannot load " + PROPERTIES_FILE + " from classpath, will try from " + filePath);
73 |
74 | try {
75 | properties = Sys.loadPropertiesFile(filePath);
76 | } catch (Throwable t2) {
77 | Log.fatal("Config: cannot load properties file from " + filePath, t);
78 |
79 | // Give up
80 | return;
81 | }
82 | }
83 |
84 | Log.info("Config: loaded values=" + properties);
85 | }
86 |
87 | public static String getProperty(String aName, String aDefault) {
88 | return properties.getProperty(aName, aDefault);
89 | }
90 |
91 | public static String getProperty(String aName) {
92 | String value = properties.getProperty(aName);
93 | if (value == null) {
94 | throw new IllegalArgumentException("Unknown property: " + aName);
95 | }
96 | return value;
97 | }
98 |
99 | public static boolean getBoolProperty(String aName) {
100 | String value = getProperty(aName);
101 | try {
102 | return value.equals("true");
103 | } catch (Throwable t) {
104 | throw new IllegalArgumentException("Illegal property value: " + aName + " val=" + value);
105 | }
106 | }
107 |
108 | public static int getIntProperty(String aName) {
109 | String value = getProperty(aName);
110 | try {
111 | return Integer.parseInt(value);
112 | } catch (Throwable t) {
113 | throw new IllegalArgumentException("Illegal property value: " + aName + " val=" + value);
114 | }
115 | }
116 |
117 | public static long getLongProperty(String aName) {
118 | String value = getProperty(aName);
119 | try {
120 | return Long.parseLong(value);
121 | } catch (Throwable t) {
122 | throw new IllegalArgumentException("Illegal property value: " + aName + " val=" + value);
123 | }
124 | }
125 |
126 | public static boolean hasProperty(String aName) {
127 | return properties.containsKey(aName);
128 | }
129 |
130 |
131 | }
132 |
133 | /*
134 | * $Log: Config.java,v $
135 | * Revision 1.5 2007/11/23 21:10:17 justb
136 | * add hooks for custom logging (you can override DefaultLogger in pushlet.properties)
137 | *
138 | * Revision 1.4 2007/11/23 14:33:07 justb
139 | * core classes now configurable through factory
140 | *
141 | * Revision 1.3 2007/11/10 13:44:02 justb
142 | * pushlet.properties and sources.properties can now also be put under WEB-INF
143 | *
144 | * Revision 1.2 2006/05/06 00:10:11 justb
145 | * various chgs but not too serious...
146 | *
147 | * Revision 1.1 2005/02/18 12:36:47 justb
148 | * changes for renaming and configurability
149 | *
150 |
151 | *
152 | */
153 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/ConfigDefs.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 |
7 | /**
8 | * Definition of config property strings.
9 | *
10 | * @author Just van den Broecke - Just Objects ©
11 | * @version $Id: ConfigDefs.java,v 1.9 2007/12/07 12:57:40 justb Exp $
12 | */
13 | public interface ConfigDefs {
14 | /**
15 | * Class factory definitions, used to insert your custom classes.
16 | */
17 | public static final String CONTROLLER_CLASS = "controller.class";
18 | public static final String DISPATCHER_CLASS = "dispatcher.class";
19 | public static final String LOGGER_CLASS = "logger.class";
20 | public static final String SESSION_MANAGER_CLASS = "sessionmanager.class";
21 | public static final String SESSION_CLASS = "session.class";
22 | public static final String SUBSCRIBER_CLASS = "subscriber.class";
23 | public static final String SUBSCRIPTION_CLASS = "subscription.class";
24 |
25 | /**
26 | * Session management.
27 | */
28 | public static final String SESSION_ID_SIZE = "session.id.size";
29 | public static final String SESSION_ID_GENERATION = "session.id.generation";
30 | public static final String SESSION_ID_GENERATION_UUID = "uuid";
31 | public static final String SESSION_ID_GENERATION_RANDOMSTRING = "randomstring";
32 | public static final String SESSION_TIMEOUT_MINS = "session.timeout.mins";
33 |
34 | public static final String SOURCES_ACTIVATE = "sources.activate";
35 |
36 | /**
37 | * Logging
38 | */
39 | public static final String LOG_LEVEL = "log.level";
40 | public static final int LOG_LEVEL_FATAL = 1;
41 | public static final int LOG_LEVEL_ERROR = 2;
42 | public static final int LOG_LEVEL_WARN = 3;
43 | public static final int LOG_LEVEL_INFO = 4;
44 | public static final int LOG_LEVEL_DEBUG = 5;
45 | public static final int LOG_LEVEL_TRACE = 6;
46 |
47 | /**
48 | * Queues
49 | */
50 | public static final String QUEUE_SIZE = "queue.size";
51 | public static final String QUEUE_READ_TIMEOUT_MILLIS = "queue.read.timeout.millis";
52 | public static final String QUEUE_WRITE_TIMEOUT_MILLIS = "queue.write.timeout.millis";
53 |
54 | /**
55 | * Listening modes.
56 | */
57 | public static final String LISTEN_FORCE_PULL_ALL = "listen.force.pull.all";
58 | public static final String LISTEN_FORCE_PULL_AGENTS = "listen.force.pull.agents";
59 |
60 |
61 | public static final String PULL_REFRESH_TIMEOUT_MILLIS = "pull.refresh.timeout.millis";
62 | public static final String PULL_REFRESH_WAIT_MIN_MILLIS = "pull.refresh.wait.min.millis";
63 | public static final String PULL_REFRESH_WAIT_MAX_MILLIS = "pull.refresh.wait.max.millis";
64 |
65 |
66 | public static final String POLL_REFRESH_TIMEOUT_MILLIS = "poll.refresh.timeout.millis";
67 | public static final String POLL_REFRESH_WAIT_MIN_MILLIS = "poll.refresh.wait.min.millis";
68 | public static final String POLL_REFRESH_WAIT_MAX_MILLIS = "poll.refresh.wait.max.millis";
69 |
70 | }
71 |
72 | /*
73 | * $Log: ConfigDefs.java,v $
74 | * Revision 1.9 2007/12/07 12:57:40 justb
75 | * added log4j and make it the default logging method
76 | *
77 | * Revision 1.8 2007/11/23 21:10:17 justb
78 | * add hooks for custom logging (you can override DefaultLogger in pushlet.properties)
79 | *
80 | * Revision 1.7 2007/11/23 14:33:07 justb
81 | * core classes now configurable through factory
82 | *
83 | * Revision 1.6 2007/11/10 14:48:35 justb
84 | * make session key generation configurable (can use uuid)
85 | *
86 | * Revision 1.5 2005/02/28 09:14:55 justb
87 | * sessmgr/dispatcher factory/singleton support
88 | *
89 | * Revision 1.4 2005/02/21 16:59:00 justb
90 | * SessionManager and session lease introduced
91 | *
92 | * Revision 1.3 2005/02/21 11:50:46 justb
93 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
94 | *
95 | * Revision 1.2 2005/02/21 11:16:44 justb
96 | * add log level config prop
97 | *
98 | * Revision 1.1 2005/02/18 12:36:47 justb
99 | * changes for renaming and configurability
100 | *
101 | *
102 | *
103 | */
104 |
105 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/Dispatcher.java:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/src/nl/justobjects/pushlet/core/Dispatcher.java
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/Event.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.Sys;
7 |
8 | import java.io.Serializable;
9 | import java.util.HashMap;
10 | import java.util.Iterator;
11 | import java.util.Map;
12 |
13 | /**
14 | * Represents the event data.
15 | *
16 | * @author Just van den Broecke - Just Objects ©
17 | * @version $Id: Event.java,v 1.13 2007/11/23 14:33:07 justb Exp $
18 | */
19 | public class Event implements Protocol, Serializable {
20 |
21 | protected Map attributes = new HashMap(3);
22 |
23 | public Event(String anEventType) {
24 | this(anEventType, null);
25 | }
26 |
27 | public Event(String anEventType, Map theAttributes) {
28 |
29 | if (theAttributes != null) {
30 | setAttrs(theAttributes);
31 | }
32 |
33 | // Set required field event type
34 | setField(P_EVENT, anEventType);
35 |
36 | // Set time in seconds since 1970
37 | setField(P_TIME, System.currentTimeMillis() / 1000);
38 | }
39 |
40 | public Event(Map theAttributes) {
41 | if (!theAttributes.containsKey(P_EVENT)) {
42 | throw new IllegalArgumentException(P_EVENT + " not found in attributes");
43 | }
44 | setAttrs(theAttributes);
45 | }
46 |
47 | public static Event createDataEvent(String aSubject) {
48 | return createDataEvent(aSubject, null);
49 | }
50 |
51 | public static Event createDataEvent(String aSubject, Map theAttributes) {
52 | Event dataEvent = new Event(E_DATA, theAttributes);
53 | dataEvent.setField(P_SUBJECT, aSubject);
54 | return dataEvent;
55 | }
56 |
57 | public String getEventType() {
58 | return getField(P_EVENT);
59 | }
60 |
61 | public String getSubject() {
62 | return getField(P_SUBJECT);
63 | }
64 |
65 | public void setField(String name, String value) {
66 | attributes.put(name, value);
67 | }
68 |
69 | public void setField(String name, int value) {
70 | attributes.put(name, value + "");
71 | }
72 |
73 | public void setField(String name, long value) {
74 | attributes.put(name, value + "");
75 | }
76 |
77 | public String getField(String name) {
78 | return (String) attributes.get(name);
79 | }
80 |
81 | /**
82 | * Return field; if null return default.
83 | */
84 | public String getField(String name, String aDefault) {
85 | String result = getField(name);
86 | return result == null ? aDefault : result;
87 | }
88 |
89 | public Iterator getFieldNames() {
90 | return attributes.keySet().iterator();
91 | }
92 |
93 | public String toString() {
94 | return attributes.toString();
95 | }
96 |
97 | /**
98 | * Convert to HTTP query string.
99 | */
100 | public String toQueryString() {
101 | String queryString = "";
102 | String amp = "";
103 | for (Iterator iter = getFieldNames(); iter.hasNext();) {
104 | String nextAttrName = (String) iter.next();
105 | String nextAttrValue = getField(nextAttrName);
106 | queryString = queryString + amp + nextAttrName + "=" + nextAttrValue;
107 | // After first add "&".
108 | amp = "&";
109 | }
110 |
111 | return queryString;
112 | }
113 |
114 | public String toXML(boolean strict) {
115 | String xmlString = "";
123 | return xmlString;
124 | }
125 |
126 | public String toXML() {
127 | return toXML(false);
128 | }
129 |
130 | public Object clone() {
131 | // Clone the Event by using copy constructor
132 | return new Event(attributes);
133 | }
134 |
135 | /**
136 | * Copy given attributes into event attributes
137 | */
138 | private void setAttrs(Map theAttributes) {
139 | attributes.putAll(theAttributes);
140 | }
141 | }
142 |
143 | /*
144 | * $Log: Event.java,v $
145 | * Revision 1.13 2007/11/23 14:33:07 justb
146 | * core classes now configurable through factory
147 | *
148 | * Revision 1.12 2006/05/15 11:52:53 justb
149 | * updates mainly for AJAX client
150 | *
151 | * Revision 1.11 2006/05/06 00:06:28 justb
152 | * first rough version AJAX client
153 | *
154 | * Revision 1.10 2005/02/21 11:50:46 justb
155 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
156 | *
157 | * Revision 1.9 2005/02/20 13:05:32 justb
158 | * removed the Postlet (integrated in Pushlet protocol)
159 | *
160 | * Revision 1.8 2005/02/15 13:29:24 justb
161 | * add toQueryString()
162 | *
163 | * Revision 1.7 2005/01/18 16:47:10 justb
164 | * protocol changes for v2 and publishing from pushlet client
165 | *
166 | * Revision 1.6 2005/01/13 14:47:15 justb
167 | * control evt: send response on same (control) connection
168 | *
169 | * Revision 1.5 2004/09/03 22:35:37 justb
170 | * Almost complete rewrite, just checking in now
171 | *
172 | * Revision 1.4 2004/08/15 16:00:15 justb
173 | * enhancements to pull mode
174 | *
175 | * Revision 1.3 2003/08/15 08:37:40 justb
176 | * fix/add Copyright+LGPL file headers and footers
177 | *
178 | * Revision 1.2 2003/05/18 16:15:08 justb
179 | * support for XML encoded Events
180 | *
181 | * Revision 1.1.1.1 2002/09/24 21:02:30 justb
182 | * import to sourceforge
183 | *
184 | */
185 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/EventParser.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import java.io.*;
7 | import java.util.HashMap;
8 |
9 | /**
10 | * Parses XML into Event objects.
11 | *
12 | * @author Just van den Broecke - Just Objects ©
13 | * @version $Id: EventParser.java,v 1.3 2007/11/23 14:33:07 justb Exp $
14 | */
15 | public class EventParser {
16 |
17 |
18 | private EventParser() {
19 | }
20 |
21 | /**
22 | * Parse Event from a File.
23 | */
24 | public static Event parse(File aFile) throws IOException {
25 | BufferedReader br = new BufferedReader(new FileReader(aFile));
26 | return parse(br);
27 | }
28 |
29 | /**
30 | * Parse Event from input Reader.
31 | */
32 | public static Event parse(Reader aReader) throws IOException {
33 | StringBuffer preparsedString = new StringBuffer(24);
34 |
35 | // First find the opening tag ('<')
36 | char nextChar;
37 | while ((nextChar = (char) aReader.read()) != '<') ;
38 |
39 | // Append '<'
40 | preparsedString.append(nextChar);
41 |
42 | // Then find end-tag ('>'), appending all chars to preparsed string.
43 | do {
44 | nextChar = (char) aReader.read();
45 | preparsedString.append(nextChar);
46 | } while (nextChar != '>');
47 |
48 | return parse(preparsedString.toString());
49 | }
50 |
51 | /**
52 | * Parse Event from a String.
53 | */
54 | public static Event parse(String aString) throws IOException {
55 | aString = aString.trim();
56 |
57 | if (!aString.startsWith("<") || !aString.endsWith("/>")) {
58 | throw new IOException("No start or end tag found while parsing event [" + aString + "]");
59 | }
60 |
61 | // Create the attributes object.
62 | HashMap properties = new HashMap(3);
63 |
64 | // Remove the start and end (< ... />) from the string
65 | aString = aString.substring(1, aString.length() - 2).trim();
66 |
67 | int index = 0;
68 |
69 | // Parse the tag
70 | while (!Character.isWhitespace(aString.charAt(index))
71 | && (index < aString.length())) {
72 | index++;
73 | }
74 |
75 | // We don't use the tag: remove from string
76 | aString = aString.substring(index).trim();
77 | index = 0;
78 |
79 | String attrName;
80 | String attrValue;
81 |
82 | while (index < aString.length()) {
83 |
84 | // Parse attribute name
85 | while ((aString.charAt(index) != '=')
86 | && (index < aString.length())) {
87 | index++;
88 | }
89 |
90 | // Create attr name string
91 | attrName = aString.substring(0, index).trim();
92 |
93 | // remove the attributeName and the '=' from the string
94 | aString = aString.substring(index + 1).trim();
95 | index = 1; // read past the first wrapping "\""
96 |
97 | // Parse attribute value
98 | while ((aString.charAt(index) != '\"')
99 | && (index < aString.length())) {
100 |
101 | // bypass the special characters '\' and '"' inside the
102 | // attributevalue itself which are deliniated with a preceding
103 | // '\'
104 | if (aString.charAt(index) == '\\') {
105 | aString = aString.substring(0, index)
106 | + aString.substring(index + 1); // remove the '\'
107 | }
108 |
109 | index++;
110 | }
111 |
112 | // create the attribute value; exclude the wrapping quote-characters
113 | attrValue = aString.substring(1, index);
114 |
115 | // Set the attribute N/V
116 | properties.put(attrName, attrValue);
117 |
118 | aString = aString.substring(index + 1).trim();
119 | index = 0;
120 | }
121 |
122 | return new Event(properties);
123 | }
124 |
125 | /**
126 | * Test method: use files to test.
127 | */
128 | public static void main(String[] args) {
129 | try {
130 | Event event = parse(new File(args[0]));
131 | System.out.println("OK parsed Event file " + args[0]);
132 | System.out.println(event.toXML());
133 |
134 | event = parse(event.toXML());
135 | System.out.println("OK parsed Event string");
136 | System.out.println(event.toXML());
137 | } catch (Throwable t) {
138 | System.out.println("Error parsing event file: " + args[0]);
139 | t.printStackTrace();
140 | }
141 | }
142 | }
143 |
144 | /*
145 | * $Log: EventParser.java,v $
146 | * Revision 1.3 2007/11/23 14:33:07 justb
147 | * core classes now configurable through factory
148 | *
149 | * Revision 1.2 2006/05/06 00:10:11 justb
150 | * various chgs but not too serious...
151 | *
152 | * Revision 1.1 2005/02/18 10:07:23 justb
153 | * many renamings of classes (make names compact)
154 | *
155 | * Revision 1.3 2004/09/03 22:35:37 justb
156 | * Almost complete rewrite, just checking in now
157 | *
158 | * Revision 1.2 2003/08/15 08:37:40 justb
159 | * fix/add Copyright+LGPL file headers and footers
160 | *
161 | * Revision 1.1 2003/05/18 16:12:27 justb
162 | * adding support for XML encoded Events
163 | *
164 | */
165 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/EventPullSource.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.Log;
7 |
8 | /**
9 | * Abstract Event source from which Events are pulled.
10 | *
11 | * @version $Id: EventPullSource.java,v 1.15 2007/11/23 14:33:07 justb Exp $
12 | * @author Just van den Broecke - Just Objects ©
13 | **/
14 |
15 | /**
16 | * ABC for specifc EventPullSources.
17 | */
18 | abstract public class EventPullSource implements EventSource, Runnable {
19 | private volatile boolean alive = false;
20 | private volatile boolean active = false;
21 | private static int threadNum = 0;
22 | private Thread thread;
23 |
24 | public EventPullSource() {
25 | }
26 |
27 | abstract protected long getSleepTime();
28 |
29 | abstract protected Event pullEvent();
30 |
31 | public void start() {
32 | thread = new Thread(this, "EventPullSource-" + (++threadNum));
33 | thread.setDaemon(true);
34 | thread.start();
35 | }
36 |
37 | public boolean isAlive() {
38 | return alive;
39 | }
40 |
41 | /**
42 | * Stop the event generator thread.
43 | */
44 | public void stop() {
45 | alive = false;
46 |
47 | if (thread != null) {
48 | thread.interrupt();
49 | thread = null;
50 | }
51 |
52 | }
53 |
54 | /**
55 | * Activate the event generator thread.
56 | */
57 | synchronized public void activate() {
58 | if (active) {
59 | return;
60 | }
61 | active = true;
62 | if (!alive) {
63 | start();
64 | return;
65 | }
66 | Log.debug(getClass().getName() + ": notifying...");
67 | notifyAll();
68 | }
69 |
70 | /**
71 | * Deactivate the event generator thread.
72 | */
73 | public void passivate() {
74 | if (!active) {
75 | return;
76 | }
77 | active = false;
78 | }
79 |
80 | /**
81 | * Main loop: sleep, generate event and publish.
82 | */
83 | public void run() {
84 | Log.debug(getClass().getName() + ": starting...");
85 | alive = true;
86 | while (alive) {
87 | try {
88 |
89 | Thread.sleep(getSleepTime());
90 |
91 | // Stopped during sleep: end loop.
92 | if (!alive) {
93 | break;
94 | }
95 |
96 | // If passivated wait until we get
97 | // get notify()-ied. If there are no subscribers
98 | // it wasts CPU to remain producing events...
99 | synchronized (this) {
100 | while (!active) {
101 | Log.debug(getClass().getName() + ": waiting...");
102 | wait();
103 | }
104 | }
105 |
106 | } catch (InterruptedException e) {
107 | break;
108 | }
109 |
110 | try {
111 | // Derived class should produce an event.
112 | Event event = pullEvent();
113 |
114 | // Let the publisher push it to subscribers.
115 | Dispatcher.getInstance().multicast(event);
116 | } catch (Throwable t) {
117 | Log.warn("EventPullSource exception while multicasting ", t);
118 | t.printStackTrace();
119 | }
120 | }
121 | Log.debug(getClass().getName() + ": stopped");
122 | }
123 | }
124 |
125 | /*
126 | * $Log: EventPullSource.java,v $
127 | * Revision 1.15 2007/11/23 14:33:07 justb
128 | * core classes now configurable through factory
129 | *
130 | * Revision 1.14 2005/02/28 09:14:55 justb
131 | * sessmgr/dispatcher factory/singleton support
132 | *
133 | * Revision 1.13 2005/02/21 16:59:08 justb
134 | * SessionManager and session lease introduced
135 | *
136 | * Revision 1.12 2005/02/21 11:50:46 justb
137 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
138 | *
139 | * Revision 1.11 2005/02/18 10:07:23 justb
140 | * many renamings of classes (make names compact)
141 | *
142 | * Revision 1.10 2005/02/18 09:54:15 justb
143 | * refactor: rename Publisher Dispatcher and single Subscriber class
144 | *
145 | * Revision 1.9 2004/09/20 22:01:38 justb
146 | * more changes for new protocol
147 | *
148 | * Revision 1.8 2004/09/03 22:35:37 justb
149 | * Almost complete rewrite, just checking in now
150 | *
151 | * Revision 1.7 2004/08/15 16:00:15 justb
152 | * enhancements to pull mode
153 | *
154 | * Revision 1.6 2004/08/13 23:36:05 justb
155 | * rewrite of Pullet into Pushlet "pull" mode
156 | *
157 | * Revision 1.5 2004/03/10 14:01:55 justb
158 | * formatting and *Subscriber refactoring
159 | *
160 | * Revision 1.4 2003/08/15 08:37:40 justb
161 | * fix/add Copyright+LGPL file headers and footers
162 | *
163 | * Revision 1.3 2003/08/12 09:57:05 justb
164 | * replaced all print statements to Log.*() calls
165 | *
166 | * Revision 1.2 2003/05/18 16:15:08 justb
167 | * support for XML encoded Events
168 | *
169 | * Revision 1.1.1.1 2002/09/24 21:02:31 justb
170 | * import to sourceforge
171 | *
172 | * Revision 1.1.1.1 2002/09/20 22:48:17 justb
173 | * import to SF
174 | *
175 | * Revision 1.1.1.1 2002/09/20 14:19:03 justb
176 | * first import into SF
177 | *
178 | * Revision 1.3 2002/04/15 20:42:41 just
179 | * reformatting and renaming GuardedQueue to EventQueue
180 | *
181 | * Revision 1.2 2000/08/21 20:48:29 just
182 | * added CVS log and id tags plus copyrights
183 | *
184 | *
185 | */
186 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/EventQueue.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | /**
7 | * FIFO queue with guarded suspension.
8 | * Purpose
9 | *
10 | * Implementation
11 | * FIFO queue class implemented with circular array. The enQueue() and
12 | * deQueue() methods use guarded suspension according to a readers/writers
13 | * pattern, implemented with java.lang.Object.wait()/notify().
14 | *
15 | * Examples
16 | *
17 | *
18 | *
19 | * @author Just van den Broecke - Just Objects ©
20 | * @version $Id: EventQueue.java,v 1.3 2007/11/23 14:33:07 justb Exp $
21 | */
22 | public class EventQueue {
23 | /**
24 | * Defines maximum queue size
25 | */
26 | private int capacity = 8;
27 | private Event[] queue = null;
28 | private int front, rear;
29 |
30 | /**
31 | * Construct queue with default (8) capacity.
32 | */
33 | public EventQueue() {
34 | this(8);
35 | }
36 |
37 | /**
38 | * Construct queue with specified capacity.
39 | */
40 | public EventQueue(int capacity) {
41 | this.capacity = capacity;
42 | queue = new Event[capacity];
43 | front = rear = 0;
44 | }
45 |
46 | /**
47 | * Put item in queue; waits() indefinitely if queue is full.
48 | */
49 | public synchronized boolean enQueue(Event item) throws InterruptedException {
50 | return enQueue(item, -1);
51 | }
52 |
53 | /**
54 | * Put item in queue; if full wait maxtime.
55 | */
56 | public synchronized boolean enQueue(Event item, long maxWaitTime) throws InterruptedException {
57 |
58 | // Wait (optional maxtime) as long as the queue is full
59 | while (isFull()) {
60 | if (maxWaitTime > 0) {
61 | // Wait at most maximum time
62 | wait(maxWaitTime);
63 |
64 | // Timed out or woken; if still full we
65 | // had bad luck and return failure.
66 | if (isFull()) {
67 | return false;
68 | }
69 | } else {
70 | wait();
71 | }
72 | }
73 |
74 | // Put item in queue
75 | queue[rear] = item;
76 | rear = next(rear);
77 |
78 | // Wake up waiters; NOTE: first waiter will eat item
79 | notifyAll();
80 | return true;
81 | }
82 |
83 | /**
84 | * Get head; if empty wait until something in queue.
85 | */
86 | public synchronized Event deQueue() throws InterruptedException {
87 | return deQueue(-1);
88 | }
89 |
90 | /**
91 | * Get head; if empty wait for specified time at max.
92 | */
93 | public synchronized Event deQueue(long maxWaitTime) throws InterruptedException {
94 | while (isEmpty()) {
95 | if (maxWaitTime >= 0) {
96 | wait(maxWaitTime);
97 |
98 | // Timed out or woken; if still empty we
99 | // had bad luck and return failure.
100 | if (isEmpty()) {
101 | return null;
102 | }
103 | } else {
104 | // Wait indefinitely for something in queue.
105 | wait();
106 | }
107 | }
108 |
109 | // Dequeue item
110 | Event result = fetchNext();
111 |
112 | // Notify possible wait()-ing enQueue()-ers
113 | notifyAll();
114 |
115 | // Return dequeued item
116 | return result;
117 | }
118 |
119 | /**
120 | * Get all queued Events.
121 | */
122 | public synchronized Event[] deQueueAll(long maxWaitTime) throws InterruptedException {
123 | while (isEmpty()) {
124 | if (maxWaitTime >= 0) {
125 | wait(maxWaitTime);
126 |
127 | // Timed out or woken; if still empty we
128 | // had bad luck and return failure.
129 | if (isEmpty()) {
130 | return null;
131 | }
132 | } else {
133 | // Wait indefinitely for something in queue.
134 | wait();
135 | }
136 | }
137 |
138 | // Dequeue all items item
139 | Event[] events = new Event[getSize()];
140 | for (int i = 0; i < events.length; i++) {
141 | events[i] = fetchNext();
142 | }
143 |
144 | // Notify possible wait()-ing enQueue()-ers
145 | notifyAll();
146 |
147 | // Return dequeued item
148 | return events;
149 | }
150 |
151 | public synchronized int getSize() {
152 | return (rear >= front) ? (rear - front) : (capacity - front + rear);
153 | }
154 |
155 | /**
156 | * Is the queue empty ?
157 | */
158 | public synchronized boolean isEmpty() {
159 | return front == rear;
160 | }
161 |
162 | /**
163 | * Is the queue full ?
164 | */
165 | public synchronized boolean isFull() {
166 | return (next(rear) == front);
167 | }
168 |
169 | /**
170 | * Circular counter.
171 | */
172 | private int next(int index) {
173 | return (index + 1 < capacity ? index + 1 : 0);
174 | }
175 |
176 | /**
177 | * Circular counter.
178 | */
179 | private Event fetchNext() {
180 | Event temp = queue[front];
181 | queue[front] = null;
182 | front = next(front);
183 | return temp;
184 | }
185 |
186 | public static void p(String s) {
187 | System.out.println(s);
188 | }
189 |
190 | public static void main(String[] args) {
191 | EventQueue q = new EventQueue(8);
192 | Event event = new Event("t");
193 | try {
194 | q.enQueue(event);
195 | p("(1) size = " + q.getSize());
196 | q.enQueue(event);
197 | p("(2) size = " + q.getSize());
198 | q.deQueue();
199 | p("(1) size = " + q.getSize());
200 | q.deQueue();
201 | p("(0) size = " + q.getSize());
202 |
203 | q.enQueue(event);
204 | q.enQueue(event);
205 | q.enQueue(event);
206 | p("(3) size = " + q.getSize());
207 | q.deQueue();
208 | p("(2) size = " + q.getSize());
209 | q.enQueue(event);
210 | q.enQueue(event);
211 | q.enQueue(event);
212 | p("(5) size = " + q.getSize());
213 | q.enQueue(event);
214 | q.enQueue(event);
215 | p("(7) size = " + q.getSize());
216 | q.deQueue();
217 | q.deQueue();
218 | q.deQueue();
219 | p("(4) size = " + q.getSize());
220 | q.deQueue();
221 | q.deQueue();
222 | q.deQueue();
223 | ;
224 | q.deQueue();
225 | p("(0) size = " + q.getSize());
226 |
227 | q.enQueue(event);
228 | q.enQueue(event);
229 | q.enQueue(event);
230 | q.enQueue(event);
231 | q.enQueue(event);
232 | p("(5) size = " + q.getSize());
233 |
234 | q.deQueue();
235 | q.deQueue();
236 | q.deQueue();
237 | ;
238 | q.deQueue();
239 | p("(1) size = " + q.getSize());
240 | } catch (InterruptedException ie) {
241 | }
242 | }
243 | }
244 |
245 | /*
246 | * $Log: EventQueue.java,v $
247 | * Revision 1.3 2007/11/23 14:33:07 justb
248 | * core classes now configurable through factory
249 | *
250 | * Revision 1.2 2005/02/21 11:50:46 justb
251 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
252 | *
253 | * Revision 1.1 2005/02/18 10:07:23 justb
254 | * many renamings of classes (make names compact)
255 | *
256 | * Revision 1.6 2005/02/16 12:16:16 justb
257 | * added support for "poll" mode
258 | *
259 | * Revision 1.5 2005/01/13 14:47:15 justb
260 | * control evt: send response on same (control) connection
261 | *
262 | * Revision 1.4 2004/09/03 22:35:37 justb
263 | * Almost complete rewrite, just checking in now
264 | *
265 | * Revision 1.3 2003/08/15 08:37:40 justb
266 | * fix/add Copyright+LGPL file headers and footers
267 | *
268 | *
269 | */
270 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/EventSource.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | /**
7 | * Abstract Event source from which Events are pulled.
8 | *
9 | * @version $Id: EventSource.java,v 1.7 2007/11/23 14:33:07 justb Exp $
10 | * @author Just van den Broecke - Just Objects ©
11 | **/
12 |
13 | /**
14 | * Interface for specifc Event(Pull/Push)Sources.
15 | */
16 | public interface EventSource {
17 | /**
18 | * Activate the event source.
19 | */
20 | public void activate();
21 |
22 | /**
23 | * Deactivate the event source.
24 | */
25 | public void passivate();
26 |
27 | /**
28 | * Halt the event source.
29 | */
30 | public void stop();
31 | }
32 |
33 | /*
34 | * $Log: EventSource.java,v $
35 | * Revision 1.7 2007/11/23 14:33:07 justb
36 | * core classes now configurable through factory
37 | *
38 | * Revision 1.6 2005/02/21 11:50:46 justb
39 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
40 | *
41 | * Revision 1.5 2005/02/18 10:07:23 justb
42 | * many renamings of classes (make names compact)
43 | *
44 | * Revision 1.4 2004/09/03 22:35:37 justb
45 | * Almost complete rewrite, just checking in now
46 | *
47 | * Revision 1.3 2003/08/15 08:37:40 justb
48 | * fix/add Copyright+LGPL file headers and footers
49 | *
50 | * Revision 1.2 2003/05/18 16:15:08 justb
51 | * support for XML encoded Events
52 | *
53 | * Revision 1.1.1.1 2002/09/24 21:02:31 justb
54 | * import to sourceforge
55 | *
56 | * Revision 1.1.1.1 2002/09/20 22:48:17 justb
57 | * import to SF
58 | *
59 | * Revision 1.1.1.1 2002/09/20 14:19:03 justb
60 | * first import into SF
61 | *
62 | * Revision 1.3 2002/04/15 20:42:41 just
63 | * reformatting and renaming GuardedQueue to EventQueue
64 | *
65 | * Revision 1.2 2000/08/21 20:48:29 just
66 | * added CVS log and id tags plus copyrights
67 | *
68 | *
69 | */
70 |
71 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/EventSourceManager.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.Log;
7 | import nl.justobjects.pushlet.util.Sys;
8 |
9 | import java.util.Enumeration;
10 | import java.util.Properties;
11 | import java.util.Vector;
12 | import java.io.File;
13 |
14 | /**
15 | * Maintains lifecycle of event sources.
16 | *
17 | * @author Just van den Broecke - Just Objects ©
18 | * @version $Id: EventSourceManager.java,v 1.14 2007/11/10 13:44:02 justb Exp $
19 | */
20 | public class EventSourceManager {
21 | private static Vector eventSources = new Vector(0);
22 | private static final String PROPERTIES_FILE = "sources.properties";
23 |
24 | /**
25 | * Initialize event sources from properties file.
26 | */
27 | public static void start(String aDirPath) {
28 | // Load Event sources using properties file.
29 | Log.info("EventSourceManager: start");
30 |
31 | Properties properties = null;
32 |
33 | try {
34 | properties = Sys.loadPropertiesResource(PROPERTIES_FILE);
35 | } catch (Throwable t) {
36 | // Try from provided dir (e.g. WEB_INF/pushlet.properties)
37 | String filePath = aDirPath + File.separator + PROPERTIES_FILE;
38 | Log.info("EventSourceManager: cannot load " + PROPERTIES_FILE + " from classpath, will try from " + filePath);
39 |
40 | try {
41 | properties = Sys.loadPropertiesFile(filePath);
42 | } catch (Throwable t2) {
43 | Log.fatal("EventSourceManager: cannot load properties file from " + filePath, t);
44 |
45 | // Give up
46 | Log.warn("EventSourceManager: not starting local event sources (maybe that is what you want)");
47 | return;
48 | }
49 | }
50 |
51 | // Create event source collection
52 | eventSources = new Vector(properties.size());
53 |
54 | // Add the configured sources
55 | for (Enumeration e = properties.keys(); e.hasMoreElements();) {
56 | String nextKey = (String) e.nextElement();
57 | String nextClass = properties.getProperty(nextKey);
58 | EventSource nextEventSource = null;
59 | try {
60 | nextEventSource = (EventSource) Class.forName(nextClass).newInstance();
61 | Log.info("created EventSource: key=" + nextKey + " class=" + nextClass);
62 | eventSources.addElement(nextEventSource);
63 | } catch (Exception ex) {
64 | Log.warn("Cannot create EventSource: class=" + nextClass, ex);
65 | }
66 | }
67 |
68 | activate();
69 | }
70 |
71 | /**
72 | * Activate all event sources.
73 | */
74 | public static void activate() {
75 | Log.info("Activating " + eventSources.size() + " EventSources");
76 | for (int i = 0; i < eventSources.size(); i++) {
77 | ((EventSource) eventSources.elementAt(i)).activate();
78 | }
79 | Log.info("EventSources activated");
80 | }
81 |
82 | /**
83 | * Deactivate all event sources.
84 | */
85 | public static void passivate() {
86 | Log.info("Passivating " + eventSources.size() + " EventSources");
87 | for (int i = 0; i < eventSources.size(); i++) {
88 | ((EventSource) eventSources.elementAt(i)).passivate();
89 | }
90 | Log.info("EventSources passivated");
91 | }
92 |
93 | /**
94 | * Halt event sources.
95 | */
96 | public static void stop() {
97 | Log.info("Stopping " + eventSources.size() + " EventSources...");
98 | for (int i = 0; i < eventSources.size(); i++) {
99 | ((EventSource) eventSources.elementAt(i)).stop();
100 | }
101 | Log.info("EventSources stopped");
102 | }
103 |
104 | }
105 |
106 | /*
107 | * $Log: EventSourceManager.java,v $
108 | * Revision 1.14 2007/11/10 13:44:02 justb
109 | * pushlet.properties and sources.properties can now also be put under WEB-INF
110 | *
111 | * Revision 1.13 2005/02/21 11:50:46 justb
112 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
113 | *
114 | * Revision 1.12 2005/02/18 12:36:47 justb
115 | * changes for renaming and configurability
116 | *
117 | * Revision 1.11 2005/02/18 10:07:23 justb
118 | * many renamings of classes (make names compact)
119 | *
120 | * Revision 1.10 2005/02/15 13:29:49 justb
121 | * use Sys.loadPropertiesResource()
122 | *
123 | * Revision 1.9 2004/09/20 22:01:38 justb
124 | * more changes for new protocol
125 | *
126 | * Revision 1.8 2004/09/03 22:35:37 justb
127 | * Almost complete rewrite, just checking in now
128 | *
129 | * Revision 1.7 2004/08/15 16:00:15 justb
130 | * enhancements to pull mode
131 | *
132 | * Revision 1.6 2004/08/13 23:36:05 justb
133 | * rewrite of Pullet into Pushlet "pull" mode
134 | *
135 | * Revision 1.5 2004/08/12 13:18:54 justb
136 | * cosmetic changes
137 | *
138 | * Revision 1.4 2003/08/15 08:37:40 justb
139 | * fix/add Copyright+LGPL file headers and footers
140 | *
141 | * Revision 1.3 2003/08/12 09:41:35 justb
142 | * replace static initalizer with explicit init()
143 | *
144 | * Revision 1.2 2003/05/18 16:15:08 justb
145 | * support for XML encoded Events
146 | *
147 | * Revision 1.1.1.1 2002/09/24 21:02:31 justb
148 | * import to sourceforge
149 | *
150 | * Revision 1.1.1.1 2002/09/20 22:48:17 justb
151 | * import to SF
152 | *
153 | * Revision 1.1.1.1 2002/09/20 14:19:03 justb
154 | * first import into SF
155 | *
156 | * Revision 1.5 2002/04/15 20:42:41 just
157 | * reformatting and renaming GuardedQueue to EventQueue
158 | *
159 | * Revision 1.4 2000/10/30 14:15:47 just
160 | * no message
161 | *
162 | * Revision 1.3 2000/08/31 08:26:54 just
163 | * Changed classloader that loads eventsources.properties to use EventSourceManager's classloader
164 | *
165 | * Revision 1.2 2000/08/21 20:48:29 just
166 | * added CVS log and id tags plus copyrights
167 | *
168 | *
169 | */
170 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/Protocol.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2004 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 |
7 | /**
8 | * Constants for Pushlet protocols.
9 | *
10 | * @author Just van den Broecke - Just Objects ©
11 | * @version $Id: Protocol.java,v 1.15 2007/11/23 14:33:07 justb Exp $
12 | */
13 | public interface Protocol {
14 | /**
15 | * Default URI .
16 | */
17 | public static final String DEFAULT_SERVLET_URI = "/pushlet/pushlet.srv";
18 |
19 | //
20 | // Common protocol header/parameter names
21 | //
22 |
23 | /**
24 | * Event type (join, leave, data, subscribe etc) .
25 | */
26 | public static final String P_EVENT = "p_event";
27 |
28 | /**
29 | * Time in seconds since 1970
30 | */
31 | public static final String P_TIME = "p_time";
32 |
33 | /**
34 | * Event sequence number, numbers per-client.
35 | */
36 | public static final String P_SEQ = "p_seq";
37 |
38 | /**
39 | * Subject (topic) of data event.
40 | */
41 | public static final String P_SUBJECT = "p_subject";
42 |
43 | /**
44 | * Originator of Event.
45 | */
46 | public static final String P_FROM = "p_from";
47 |
48 | /**
49 | * Addressee of Event, subject or client p_id.
50 | */
51 | public static final String P_TO = "p_to";
52 |
53 | /**
54 | * Identifier for client instance within server.
55 | */
56 | public static final String P_ID = "p_id";
57 |
58 | /**
59 | * Subscription id, identifies single subscription.
60 | */
61 | public static final String P_SUBSCRIPTION_ID = "p_sid";
62 |
63 | /**
64 | * Format to receive events
65 | */
66 | public static final String P_FORMAT = "p_format";
67 |
68 | /**
69 | * Protocol mode.
70 | */
71 | public static final String P_MODE = "p_mode";
72 |
73 | /**
74 | * Reason for errors.
75 | */
76 | public static final String P_REASON = "p_reason";
77 |
78 | /**
79 | * URL attribute.
80 | */
81 | public static final String P_URL = "p_url";
82 |
83 | /**
84 | * Wait attribute.
85 | */
86 | public static final String P_WAIT = "p_wait";
87 |
88 | /**
89 | * Subscription label, may be used to return user-specific
90 | * token with a data event, e.g. the name of a function for a callback.
91 | */
92 | public static final String P_SUBSCRIPTION_LABEL = "p_label";
93 |
94 | //
95 | // Event values with direction for P_EVENT (C=client, S=server)
96 | //
97 |
98 | /**
99 | * C-->S Request to join server.
100 | */
101 | public static final String E_JOIN = "join";
102 |
103 | /**
104 | * S-->C Acknowledgement of join.
105 | */
106 | public static final String E_JOIN_ACK = "join-ack";
107 |
108 | /**
109 | * C-->S Request to join server.
110 | */
111 | public static final String E_JOIN_LISTEN = "join-listen";
112 |
113 | /**
114 | * S-->C Acknowledgement of join.
115 | */
116 | public static final String E_JOIN_LISTEN_ACK = "join-listen-ack";
117 |
118 | /**
119 | * C-->S Client starts listening.
120 | */
121 | public static final String E_LISTEN = "listen";
122 |
123 | /**
124 | * S-->C Ack of listen.
125 | */
126 | public static final String E_LISTEN_ACK = "listen-ack";
127 |
128 | /**
129 | * C-->S Client leaves server.
130 | */
131 | public static final String E_LEAVE = "leave";
132 |
133 | /**
134 | * S-->C Ack of leave.
135 | */
136 | public static final String E_LEAVE_ACK = "leave-ack";
137 |
138 | /**
139 | * C-->S Publish to subject.
140 | */
141 | public static final String E_PUBLISH = "publish";
142 |
143 | /**
144 | * S-->C Publish to subject acknowledge.
145 | */
146 | public static final String E_PUBLISH_ACK = "publish-ack";
147 |
148 | /**
149 | * C-->S Subscribe to subject request.
150 | */
151 | public static final String E_SUBSCRIBE = "subscribe";
152 |
153 | /**
154 | * S-->C Subscribe to subject acknowledge.
155 | */
156 | public static final String E_SUBSCRIBE_ACK = "subscribe-ack";
157 |
158 | /**
159 | * C-->S Unsubscribe from subject request.
160 | */
161 | public static final String E_UNSUBSCRIBE = "unsubscribe";
162 |
163 | /**
164 | * S--C Unsubscribe from subject acknowledge.
165 | */
166 | public static final String E_UNSUBSCRIBE_ACK = "unsubscribe-ack";
167 |
168 | /**
169 | * S-->C Client error response, transitional error.
170 | */
171 | public static final String E_NACK = "nack";
172 |
173 | /**
174 | * S-->C Client should abort, permanent error.
175 | */
176 | public static final String E_ABORT = "abort";
177 |
178 | /**
179 | * S-->C Data.
180 | */
181 | public static final String E_DATA = "data";
182 |
183 | /**
184 | * S-->C or C-->S Heartbeat.
185 | */
186 | public static final String E_HEARTBEAT = "hb";
187 |
188 | /**
189 | * S-->C S-->C or C-->S Heartbeat confirmed.
190 | */
191 | public static final String E_HEARTBEAT_ACK = "hb-ack";
192 |
193 | /**
194 | * S-->C or C-->S client refresh of data channel.
195 | */
196 | public static final String E_REFRESH = "refresh";
197 |
198 | /**
199 | * S-->C client should refresh data channel.
200 | */
201 | public static final String E_REFRESH_ACK = "refresh-ack";
202 |
203 | //
204 | // Values for P_FORMAT parameter
205 | //
206 |
207 | /**
208 | * JavaScript callback.
209 | */
210 | public static String FORMAT_JAVASCRIPT = "js";
211 |
212 | /**
213 | * Java serialized object.
214 | */
215 | public static String FORMAT_SERIALIZED_JAVA_OBJECT = "ser";
216 |
217 | /**
218 | * Stream of XML documents.
219 | */
220 | public static String FORMAT_XML = "xml";
221 |
222 | /**
223 | * Single XML document containing zero or more events.
224 | */
225 | public static String FORMAT_XML_STRICT = "xml-strict";
226 |
227 | //
228 | // Values for P_MODE parameter
229 | //
230 | public static final String MODE_STREAM = "stream";
231 | public static final String MODE_PULL = "pull";
232 | public static final String MODE_POLL = "poll";
233 |
234 | //
235 | // Values for special/reserved subjects
236 | // TODO: use these to publish events when clients do these actions
237 | // TODO: Dispatcher may intercept these subjects to send cached events
238 | //
239 | public static final String SUBJECT_META = "/meta";
240 | public static final String SUBJECT_META_SUBS = SUBJECT_META + "/subs";
241 | public static final String SUBJECT_META_JOINS = SUBJECT_META + "/joins";
242 |
243 |
244 | }
245 |
246 | /*
247 | * $Log: Protocol.java,v $
248 | * Revision 1.15 2007/11/23 14:33:07 justb
249 | * core classes now configurable through factory
250 | *
251 | * Revision 1.14 2006/10/19 12:33:40 justb
252 | * add atomic join-listen support (one request)
253 | *
254 | * Revision 1.13 2005/05/06 19:44:00 justb
255 | * added xml-strict format
256 | *
257 | * Revision 1.12 2005/02/28 13:05:59 justb
258 | * introduced join-listen protocol service
259 | *
260 | * Revision 1.11 2005/02/28 12:45:59 justb
261 | * introduced Command class
262 | *
263 | * Revision 1.10 2005/02/16 12:16:17 justb
264 | * added support for "poll" mode
265 | *
266 | * Revision 1.9 2005/01/24 22:46:02 justb
267 | * getting safari to work
268 | *
269 | * Revision 1.8 2005/01/24 13:42:00 justb
270 | * new protocol changes (p_listen)
271 | *
272 | * Revision 1.7 2005/01/18 16:47:10 justb
273 | * protocol changes for v2 and publishing from pushlet client
274 | *
275 | * Revision 1.6 2005/01/13 14:47:15 justb
276 | * control evt: send response on same (control) connection
277 | *
278 | * Revision 1.5 2004/10/24 13:52:52 justb
279 | * small fixes in client lib
280 | *
281 | * Revision 1.4 2004/10/24 12:58:18 justb
282 | * revised client and test classes for new protocol
283 | *
284 | * Revision 1.3 2004/09/20 22:01:38 justb
285 | * more changes for new protocol
286 | *
287 | * Revision 1.2 2004/09/03 22:35:37 justb
288 | * Almost complete rewrite, just checking in now
289 | *
290 | * Revision 1.1 2004/09/03 21:02:20 justb
291 | * make more formalized protocol
292 | *
293 | *
294 | */
295 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/SerializedAdapter.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import javax.servlet.http.HttpServletResponse;
7 | import java.io.IOException;
8 | import java.io.ObjectOutputStream;
9 |
10 | /**
11 | * Implementation of ClientAdapter that sends Events as serialized objects.
12 | *
13 | * NOTE: You are discouraged to use this adapter, since it is Java-only
14 | * and may have JVM-specific problems. Far better choice is to use XML
15 | * and the XMLAdapter.
16 | *
17 | * @author Just van den Broecke - Just Objects ©
18 | * @version $Id: SerializedAdapter.java,v 1.4 2007/11/23 14:33:07 justb Exp $
19 | */
20 | class SerializedAdapter implements ClientAdapter {
21 | private ObjectOutputStream out = null;
22 | public static final String CONTENT_TYPE = "application/x-java-serialized-object";
23 | private HttpServletResponse servletRsp;
24 |
25 | /**
26 | * Initialize.
27 | */
28 | public SerializedAdapter(HttpServletResponse aServletResponse) {
29 | servletRsp = aServletResponse;
30 | }
31 |
32 | public void start() throws IOException {
33 |
34 | servletRsp.setContentType(CONTENT_TYPE);
35 |
36 | // Use a serialized object output stream
37 | out = new ObjectOutputStream(servletRsp.getOutputStream());
38 |
39 | // Don't need this further
40 | servletRsp = null;
41 | }
42 |
43 | /**
44 | * Push Event to client.
45 | */
46 | public void push(Event anEvent) throws IOException {
47 | out.writeObject(anEvent);
48 |
49 | out.flush();
50 | }
51 |
52 |
53 | public void stop() throws IOException {
54 | }
55 | }
56 |
57 | /*
58 | * $Log: SerializedAdapter.java,v $
59 | * Revision 1.4 2007/11/23 14:33:07 justb
60 | * core classes now configurable through factory
61 | *
62 | * Revision 1.3 2005/02/28 12:45:59 justb
63 | * introduced Command class
64 | *
65 | * Revision 1.2 2005/02/21 11:50:46 justb
66 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
67 | *
68 | * Revision 1.1 2005/02/18 10:07:23 justb
69 | * many renamings of classes (make names compact)
70 | *
71 | * Revision 1.4 2004/09/03 22:35:37 justb
72 | * Almost complete rewrite, just checking in now
73 | *
74 | * Revision 1.3 2003/08/15 08:37:40 justb
75 | * fix/add Copyright+LGPL file headers and footers
76 | *
77 | * Revision 1.2 2003/05/18 16:13:48 justb
78 | * fixed blocking for java.net.URL with HTTP/1.1 (JVMs > 1.1)
79 | *
80 | * Revision 1.1.1.1 2002/09/24 21:02:31 justb
81 | * import to sourceforge
82 | *
83 | * Revision 1.1.1.1 2002/09/20 22:48:18 justb
84 | * import to SF
85 | *
86 | * Revision 1.1.1.1 2002/09/20 14:19:03 justb
87 | * first import into SF
88 | *
89 | * Revision 1.5 2002/04/15 20:42:41 just
90 | * reformatting and renaming GuardedQueue to EventQueue
91 | *
92 | * Revision 1.4 2000/12/27 22:39:35 just
93 | * no message
94 | *
95 | * Revision 1.3 2000/08/21 20:48:29 just
96 | * added CVS log and id tags plus copyrights
97 | *
98 | *
99 | */
100 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/Session.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.Log;
7 | import nl.justobjects.pushlet.util.PushletException;
8 |
9 | /**
10 | * Represents client pushlet session state.
11 | *
12 | * @author Just van den Broecke - Just Objects ©
13 | * @version $Id: Session.java,v 1.8 2007/11/23 14:33:07 justb Exp $
14 | */
15 | public class Session implements Protocol, ConfigDefs {
16 | private Controller controller;
17 | private Subscriber subscriber;
18 |
19 | private String userAgent;
20 | private long LEASE_TIME_MILLIS = Config.getLongProperty(SESSION_TIMEOUT_MINS) * 60 * 1000;
21 | private volatile long timeToLive = LEASE_TIME_MILLIS;
22 |
23 | public static String[] FORCED_PULL_AGENTS = Config.getProperty(LISTEN_FORCE_PULL_AGENTS).split(",");
24 |
25 | private String address = "unknown";
26 | private String format = FORMAT_XML;
27 |
28 | private String id;
29 |
30 | /**
31 | * Protected constructor as we create through factory method.
32 | */
33 | protected Session() {
34 | }
35 |
36 | /**
37 | * Create instance through factory method.
38 | *
39 | * @param anId a session id
40 | * @return a Session object (or derived)
41 | * @throws PushletException exception, usually misconfiguration
42 | */
43 | public static Session create(String anId) throws PushletException {
44 | Session session;
45 | try {
46 | session = (Session) Config.getClass(SESSION_CLASS, "nl.justobjects.pushlet.core.Session").newInstance();
47 | } catch (Throwable t) {
48 | throw new PushletException("Cannot instantiate Session from config", t);
49 | }
50 |
51 | // Init session
52 | session.id = anId;
53 | session.controller = Controller.create(session);
54 | session.subscriber = Subscriber.create(session);
55 | return session;
56 | }
57 |
58 | /**
59 | * Return (remote) Subscriber client's IP address.
60 | */
61 | public String getAddress() {
62 | return address;
63 | }
64 |
65 | /**
66 | * Return command controller.
67 | */
68 | public Controller getController() {
69 | return controller;
70 | }
71 |
72 | /**
73 | * Return Event format to send to client.
74 | */
75 | public String getFormat() {
76 | return format;
77 | }
78 |
79 | /**
80 | * Return (remote) Subscriber client's unique id.
81 | */
82 | public String getId() {
83 | return id;
84 | }
85 |
86 | /**
87 | * Return subscriber.
88 | */
89 | public Subscriber getSubscriber() {
90 | return subscriber;
91 | }
92 |
93 | /**
94 | * Return remote HTTP User-Agent.
95 | */
96 | public String getUserAgent() {
97 | return userAgent;
98 | }
99 |
100 | /**
101 | * Set address.
102 | */
103 | protected void setAddress(String anAddress) {
104 | address = anAddress;
105 | }
106 |
107 | /**
108 | * Set event format to encode.
109 | */
110 | protected void setFormat(String aFormat) {
111 | format = aFormat;
112 | }
113 |
114 | /**
115 | * Set client HTTP UserAgent.
116 | */
117 | public void setUserAgent(String aUserAgent) {
118 | userAgent = aUserAgent;
119 | }
120 |
121 | /**
122 | * Decrease time to live.
123 | */
124 | public void age(long aDeltaMillis) {
125 | timeToLive -= aDeltaMillis;
126 | }
127 |
128 | /**
129 | * Has session timed out?
130 | */
131 | public boolean isExpired() {
132 | return timeToLive <= 0;
133 | }
134 |
135 | /**
136 | * Keep alive by resetting TTL.
137 | */
138 | public void kick() {
139 | timeToLive = LEASE_TIME_MILLIS;
140 | }
141 |
142 | public void start() {
143 | SessionManager.getInstance().addSession(this);
144 | }
145 |
146 | public void stop() {
147 | subscriber.stop();
148 | SessionManager.getInstance().removeSession(this);
149 | }
150 |
151 | /**
152 | * Info.
153 | */
154 | public void info(String s) {
155 | Log.info("S-" + this + ": " + s);
156 | }
157 |
158 | /**
159 | * Exceptional print util.
160 | */
161 | public void warn(String s) {
162 | Log.warn("S-" + this + ": " + s);
163 | }
164 |
165 | /**
166 | * Exceptional print util.
167 | */
168 | public void debug(String s) {
169 | Log.debug("S-" + this + ": " + s);
170 | }
171 |
172 | public String toString() {
173 | return getAddress() + "[" + getId() + "]";
174 | }
175 | }
176 |
177 | /*
178 | * $Log: Session.java,v $
179 | * Revision 1.8 2007/11/23 14:33:07 justb
180 | * core classes now configurable through factory
181 | *
182 | * Revision 1.7 2005/02/28 15:58:05 justb
183 | * added SimpleListener example
184 | *
185 | * Revision 1.6 2005/02/28 12:45:59 justb
186 | * introduced Command class
187 | *
188 | * Revision 1.5 2005/02/28 09:14:55 justb
189 | * sessmgr/dispatcher factory/singleton support
190 | *
191 | * Revision 1.4 2005/02/25 15:13:01 justb
192 | * session id generation more robust
193 | *
194 | * Revision 1.3 2005/02/21 16:59:08 justb
195 | * SessionManager and session lease introduced
196 | *
197 | * Revision 1.2 2005/02/21 12:32:28 justb
198 | * fixed publish event in Controller
199 | *
200 | * Revision 1.1 2005/02/21 11:50:46 justb
201 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
202 | *
203 |
204 | *
205 | */
206 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/SessionManager.java:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/src/nl/justobjects/pushlet/core/SessionManager.java
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/Subscriber.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.PushletException;
7 | import nl.justobjects.pushlet.util.Rand;
8 | import nl.justobjects.pushlet.util.Sys;
9 |
10 | import java.util.Collections;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 | import java.net.URLEncoder;
14 |
15 | /**
16 | * Handles data channel between dispatcher and client.
17 | *
18 | * @author Just van den Broecke - Just Objects ©
19 | * @version $Id: Subscriber.java,v 1.26 2007/11/23 14:33:07 justb Exp $
20 | */
21 | public class Subscriber implements Protocol, ConfigDefs {
22 | private Session session;
23 |
24 | /**
25 | * Blocking queue.
26 | */
27 | private EventQueue eventQueue = new EventQueue(Config.getIntProperty(QUEUE_SIZE));
28 |
29 | /**
30 | * URL to be used in refresh requests in pull/poll modes.
31 | */
32 | private long queueReadTimeoutMillis = Config.getLongProperty(QUEUE_READ_TIMEOUT_MILLIS);
33 | private long queueWriteTimeoutMillis = Config.getLongProperty(QUEUE_WRITE_TIMEOUT_MILLIS);
34 | private long refreshTimeoutMillis = Config.getLongProperty(PULL_REFRESH_TIMEOUT_MILLIS);
35 | volatile long lastAlive = Sys.now();
36 |
37 | /**
38 | * Map of active subscriptions, keyed by their subscription id.
39 | */
40 | private Map subscriptions = Collections.synchronizedMap(new HashMap(3));
41 |
42 | /**
43 | * Are we able to accept/send events ?.
44 | */
45 | private volatile boolean active;
46 |
47 | /**
48 | * Transfer mode (stream, pull, poll).
49 | */
50 | private String mode;
51 |
52 |
53 | /**
54 | * Protected constructor as we create through factory method.
55 | */
56 | protected Subscriber() {
57 | }
58 |
59 | /**
60 | * Create instance through factory method.
61 | *
62 | * @param aSession the parent Session
63 | * @return a Subscriber object (or derived)
64 | * @throws PushletException exception, usually misconfiguration
65 | */
66 | public static Subscriber create(Session aSession) throws PushletException {
67 | Subscriber subscriber;
68 | try {
69 | subscriber = (Subscriber) Config.getClass(SUBSCRIBER_CLASS, "nl.justobjects.pushlet.core.Subscriber").newInstance();
70 | } catch (Throwable t) {
71 | throw new PushletException("Cannot instantiate Subscriber from config", t);
72 | }
73 |
74 | subscriber.session = aSession;
75 | return subscriber;
76 | }
77 |
78 | public void start() {
79 | active = true;
80 | }
81 |
82 | public void stop() {
83 | removeSubscriptions();
84 | active = false;
85 | }
86 |
87 | public void bailout() {
88 | session.stop();
89 | }
90 |
91 | /**
92 | * Are we still active to handle events.
93 | */
94 | public boolean isActive() {
95 | return active;
96 | }
97 |
98 | /**
99 | * Return client session.
100 | */
101 | public Session getSession() {
102 | return session;
103 | }
104 |
105 | /**
106 | * Get (session) id.
107 | */
108 | public String getId() {
109 | return session.getId();
110 | }
111 |
112 | /**
113 | * Return subscriptions.
114 | */
115 | public Subscription[] getSubscriptions() {
116 | // todo: Optimize
117 | return (Subscription[]) subscriptions.values().toArray(new Subscription[0]);
118 | }
119 |
120 | /**
121 | * Add a subscription.
122 | */
123 | public Subscription addSubscription(String aSubject, String aLabel) throws PushletException {
124 | Subscription subscription = Subscription.create(aSubject, aLabel);
125 | subscriptions.put(subscription.getId(), subscription);
126 | info("Subscription added subject=" + aSubject + " sid=" + subscription.getId() + " label=" + aLabel);
127 | return subscription;
128 | }
129 |
130 | /**
131 | * Remove a subscription.
132 | */
133 | public Subscription removeSubscription(String aSubscriptionId) {
134 | Subscription subscription = (Subscription) subscriptions.remove(aSubscriptionId);
135 | if (subscription == null) {
136 | warn("No subscription found sid=" + aSubscriptionId);
137 | return null;
138 | }
139 | info("Subscription removed subject=" + subscription.getSubject() + " sid=" + subscription.getId() + " label=" + subscription.getLabel());
140 | return subscription;
141 | }
142 |
143 | /**
144 | * Remove all subscriptions.
145 | */
146 | public void removeSubscriptions() {
147 | subscriptions.clear();
148 | }
149 |
150 | public String getMode() {
151 | return mode;
152 | }
153 |
154 | public void setMode(String aMode) {
155 | mode = aMode;
156 | }
157 |
158 | public long getRefreshTimeMillis() {
159 | String minWaitProperty = PULL_REFRESH_WAIT_MIN_MILLIS;
160 | String maxWaitProperty = PULL_REFRESH_WAIT_MAX_MILLIS;
161 | if (mode.equals((MODE_POLL))) {
162 | minWaitProperty = POLL_REFRESH_WAIT_MIN_MILLIS;
163 | maxWaitProperty = POLL_REFRESH_WAIT_MAX_MILLIS;
164 |
165 | }
166 | return Rand.randomLong(Config.getLongProperty(minWaitProperty),
167 | Config.getLongProperty(maxWaitProperty));
168 | }
169 |
170 | /**
171 | * Get events from queue and push to client.
172 | */
173 | public void fetchEvents(Command aCommand) throws PushletException {
174 |
175 | String refreshURL = aCommand.httpReq.getRequestURI() + "?" + P_ID + "=" + session.getId() + "&" + P_EVENT + "=" + E_REFRESH;
176 |
177 | // This is the only thing required to support "poll" mode
178 | if (mode.equals(MODE_POLL)) {
179 | queueReadTimeoutMillis = 0;
180 | refreshTimeoutMillis = Config.getLongProperty(POLL_REFRESH_TIMEOUT_MILLIS);
181 | }
182 |
183 | // Required for fast bailout (tomcat)
184 | aCommand.httpRsp.setBufferSize(128);
185 |
186 | // Try to prevent caching in any form.
187 | aCommand.sendResponseHeaders();
188 |
189 | // Let clientAdapter determine how to send event
190 | ClientAdapter clientAdapter = aCommand.getClientAdapter();
191 | Event responseEvent = aCommand.getResponseEvent();
192 | try {
193 | clientAdapter.start();
194 |
195 | // Send first event (usually hb-ack or listen-ack)
196 | clientAdapter.push(responseEvent);
197 |
198 | // In pull/poll mode and when response is listen-ack or join-listen-ack,
199 | // return and force refresh immediately
200 | // such that the client recieves response immediately over this channel.
201 | // This is usually when loading the browser app for the first time
202 | if ((mode.equals(MODE_POLL) || mode.equals(MODE_PULL))
203 | && responseEvent.getEventType().endsWith(Protocol.E_LISTEN_ACK)) {
204 | sendRefresh(clientAdapter, refreshURL);
205 |
206 | // We should come back later with refresh event...
207 | return;
208 | }
209 | } catch (Throwable t) {
210 | bailout();
211 | return;
212 | }
213 |
214 |
215 | Event[] events = null;
216 |
217 | // Main loop: as long as connected, get events and push to client
218 | long eventSeqNr = 1;
219 | while (isActive()) {
220 | // Indicate we are still alive
221 | lastAlive = Sys.now();
222 |
223 | // Update session time to live
224 | session.kick();
225 |
226 | // Get next events; blocks until timeout or entire contents
227 | // of event queue is returned. Note that "poll" mode
228 | // will return immediately when queue is empty.
229 | try {
230 | // Put heartbeat in queue when starting to listen in stream mode
231 | // This speeds up the return of *_LISTEN_ACK
232 | if (mode.equals(MODE_STREAM) && eventSeqNr == 1) {
233 | eventQueue.enQueue(new Event(E_HEARTBEAT));
234 | }
235 |
236 | events = eventQueue.deQueueAll(queueReadTimeoutMillis);
237 | } catch (InterruptedException ie) {
238 | warn("interrupted");
239 | bailout();
240 | }
241 |
242 | // Send heartbeat when no events received
243 | if (events == null) {
244 | events = new Event[1];
245 | events[0] = new Event(E_HEARTBEAT);
246 | }
247 |
248 | // ASSERT: one or more events available
249 |
250 | // Send events to client using adapter
251 | // debug("received event count=" + events.length);
252 | for (int i = 0; i < events.length; i++) {
253 | // Check for abort event
254 | if (events[i].getEventType().equals(E_ABORT)) {
255 | warn("Aborting Subscriber");
256 | bailout();
257 | }
258 |
259 | // Push next Event to client
260 | try {
261 | // Set sequence number
262 | events[i].setField(P_SEQ, eventSeqNr++);
263 |
264 | // Push to client through client adapter
265 | clientAdapter.push(events[i]);
266 | } catch (Throwable t) {
267 | bailout();
268 | return;
269 | }
270 | }
271 |
272 | // Force client refresh request in pull or poll modes
273 | if (mode.equals(MODE_PULL) || mode.equals(MODE_POLL)) {
274 | sendRefresh(clientAdapter, refreshURL);
275 |
276 | // Always leave loop in pull/poll mode
277 | break;
278 | }
279 | }
280 | }
281 |
282 | /**
283 | * Determine if we should receive event.
284 | */
285 | public Subscription match(Event event) {
286 | Subscription[] subscriptions = getSubscriptions();
287 | for (int i = 0; i < subscriptions.length; i++) {
288 | if (subscriptions[i].match(event)) {
289 | return subscriptions[i];
290 | }
291 | }
292 | return null;
293 | }
294 |
295 | /**
296 | * Event from Dispatcher: enqueue it.
297 | */
298 | public void onEvent(Event theEvent) {
299 | if (!isActive()) {
300 | return;
301 | }
302 |
303 | // p("send: queue event: "+theEvent.getSubject());
304 |
305 | // Check if we had any active continuation for at
306 | // least 'timeOut' millisecs. If the client has left this
307 | // instance there would be no way of knowing otherwise.
308 | long now = Sys.now();
309 | if (now - lastAlive > refreshTimeoutMillis) {
310 | warn("not alive for at least: " + refreshTimeoutMillis + "ms, leaving...");
311 | bailout();
312 | return;
313 | }
314 |
315 | // Put event in queue; leave if queue full
316 | try {
317 | if (!eventQueue.enQueue(theEvent, queueWriteTimeoutMillis)) {
318 | warn("queue full, bailing out...");
319 | bailout();
320 | }
321 |
322 | // ASSERTION : Event in queue.
323 | // see fetchEvents() where Events are dequeued and pushed to the client.
324 | } catch (InterruptedException ie) {
325 | bailout();
326 | }
327 |
328 | }
329 |
330 | /**
331 | * Send refresh command to pull/poll clients.
332 | */
333 | protected void sendRefresh(ClientAdapter aClientAdapter, String aRefreshURL) {
334 | Event refreshEvent = new Event(E_REFRESH);
335 |
336 | // Set wait time and url for refresh
337 | refreshEvent.setField(P_WAIT, "" + getRefreshTimeMillis());
338 | refreshEvent.setField(P_URL, aRefreshURL);
339 |
340 | try {
341 | // Push to client through client adapter
342 | aClientAdapter.push(refreshEvent);
343 |
344 | // Stop this round until refresh event
345 | aClientAdapter.stop();
346 | } catch (Throwable t) {
347 | // Leave on any exception
348 | bailout();
349 | }
350 | }
351 |
352 | /**
353 | * Info.
354 | */
355 | protected void info(String s) {
356 | session.info("[Subscriber] " + s);
357 | }
358 |
359 | /**
360 | * Exceptional print util.
361 | */
362 | protected void warn(String s) {
363 | session.warn("[Subscriber] " + s);
364 | }
365 |
366 | /**
367 | * Exceptional print util.
368 | */
369 | protected void debug(String s) {
370 | session.debug("[Subscriber] " + s);
371 | }
372 |
373 |
374 | public String toString() {
375 | return session.toString();
376 | }
377 | }
378 |
379 | /*
380 | * $Log: Subscriber.java,v $
381 | * Revision 1.26 2007/11/23 14:33:07 justb
382 | * core classes now configurable through factory
383 | *
384 | * Revision 1.25 2007/11/10 15:53:15 justb
385 | * put heartbeat in queue when start fetching events in stream-mode
386 | *
387 | * Revision 1.24 2006/10/19 12:33:40 justb
388 | * add atomic join-listen support (one request)
389 | *
390 | * Revision 1.22 2006/05/06 00:06:28 justb
391 | * first rough version AJAX client
392 | *
393 | * Revision 1.21 2005/02/28 12:45:59 justb
394 | * introduced Command class
395 | *
396 | * Revision 1.20 2005/02/21 16:59:09 justb
397 | * SessionManager and session lease introduced
398 | *
399 | * Revision 1.19 2005/02/21 12:32:28 justb
400 | * fixed publish event in Controller
401 | *
402 | * Revision 1.18 2005/02/21 11:50:46 justb
403 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
404 | *
405 | * Revision 1.17 2005/02/20 13:05:32 justb
406 | * removed the Postlet (integrated in Pushlet protocol)
407 | *
408 | * Revision 1.16 2005/02/18 12:36:47 justb
409 | * changes for renaming and configurability
410 | *
411 | * Revision 1.15 2005/02/18 10:07:23 justb
412 | * many renamings of classes (make names compact)
413 | *
414 | * Revision 1.14 2005/02/18 09:54:15 justb
415 | * refactor: rename Publisher Dispatcher and single Subscriber class
416 | *
417 | * Revision 1.13 2005/02/16 14:39:34 justb
418 | * fixed leave handling and added "poll" mode
419 | *
420 | * Revision 1.12 2005/01/24 13:42:00 justb
421 | * new protocol changes (p_listen)
422 | *
423 | * Revision 1.11 2005/01/13 14:47:15 justb
424 | * control evt: send response on same (control) connection
425 | *
426 | * Revision 1.10 2004/10/24 20:50:35 justb
427 | * refine subscription with label and sending sid and label on events
428 | *
429 | * Revision 1.9 2004/10/24 12:58:18 justb
430 | * revised client and test classes for new protocol
431 | *
432 | * Revision 1.8 2004/09/26 21:39:43 justb
433 | * allow multiple subscriptions and out-of-band requests
434 | *
435 | * Revision 1.7 2004/09/20 22:01:38 justb
436 | * more changes for new protocol
437 | *
438 | * Revision 1.6 2004/09/03 22:35:37 justb
439 | * Almost complete rewrite, just checking in now
440 | *
441 | * Revision 1.5 2004/08/13 23:36:05 justb
442 | * rewrite of Pullet into Pushlet "pull" mode
443 | *
444 | * Revision 1.4 2004/03/10 14:01:55 justb
445 | * formatting and *Subscriber refactoring
446 | *
447 | * Revision 1.3 2003/08/15 08:37:40 justb
448 | * fix/add Copyright+LGPL file headers and footers
449 | *
450 | * Revision 1.2 2003/05/18 16:15:08 justb
451 | * support for XML encoded Events
452 | *
453 | * Revision 1.1.1.1 2002/09/24 21:02:32 justb
454 | * import to sourceforge
455 | *
456 | * Revision 1.1.1.1 2002/09/20 22:48:18 justb
457 | * import to SF
458 | *
459 | * Revision 1.1.1.1 2002/09/20 14:19:04 justb
460 | * first import into SF
461 | *
462 | * Revision 1.3 2002/04/15 20:42:41 just
463 | * reformatting and renaming GuardedQueue to EventQueue
464 | *
465 | * Revision 1.2 2000/08/21 20:48:29 just
466 | * added CVS log and id tags plus copyrights
467 | *
468 | *
469 | */
470 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/Subscription.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.Rand;
7 | import nl.justobjects.pushlet.util.PushletException;
8 |
9 |
10 | /**
11 | * Represents single subject subscription
12 | *
13 | * @author Just van den Broecke - Just Objects ©
14 | * @version $Id: Subscription.java,v 1.5 2007/11/23 14:33:07 justb Exp $
15 | */
16 | public class Subscription implements ConfigDefs {
17 | public static final int ID_SIZE = 5;
18 | public static final String SUBJECT_SEPARATOR = ",";
19 | private String id = Rand.randomName(ID_SIZE);
20 | private String subject;
21 | private String[] subjects;
22 |
23 | /**
24 | * Optional label, a user supplied token.
25 | */
26 | private String label;
27 |
28 |
29 | /**
30 | * Protected constructor as we create through factory method.
31 | */
32 | protected Subscription() {
33 | }
34 |
35 | /**
36 | * Create instance through factory method.
37 | *
38 | * @param aSubject the subject (topic).
39 | * @return a Subscription object (or derived)
40 | * @throws nl.justobjects.pushlet.util.PushletException
41 | * exception, usually misconfiguration
42 | */
43 | public static Subscription create(String aSubject) throws PushletException {
44 | return create(aSubject, null);
45 | }
46 |
47 | /**
48 | * Create instance through factory method.
49 | *
50 | * @param aSubject the subject (topic).
51 | * @param aLabel the subject label (optional).
52 | * @return a Subscription object (or derived)
53 | * @throws nl.justobjects.pushlet.util.PushletException
54 | * exception, usually misconfiguration
55 | */
56 | public static Subscription create(String aSubject, String aLabel) throws PushletException {
57 | if (aSubject == null || aSubject.length() == 0) {
58 | throw new IllegalArgumentException("Null or emtpy subject");
59 | }
60 |
61 | Subscription subscription;
62 | try {
63 | subscription = (Subscription) Config.getClass(SUBSCRIPTION_CLASS, "nl.justobjects.pushlet.core.Subscription").newInstance();
64 | } catch (Throwable t) {
65 | throw new PushletException("Cannot instantiate Subscriber from config", t);
66 | }
67 |
68 | // Init
69 | subscription.subject = aSubject;
70 |
71 | // We may subscribe to multiple subjects by separating
72 | // them with SUBJECT_SEPARATOR, e.g. "/stocks/aex,/system/memory,..").
73 | subscription.subjects = aSubject.split(SUBJECT_SEPARATOR);
74 |
75 | subscription.label = aLabel;
76 | return subscription;
77 | }
78 |
79 |
80 | public String getId() {
81 | return id;
82 | }
83 |
84 | public String getLabel() {
85 | return label;
86 | }
87 |
88 | public String getSubject() {
89 | return subject;
90 | }
91 |
92 | /**
93 | * Determine if Event matches subscription.
94 | */
95 | public boolean match(Event event) {
96 | String eventSubject = event.getSubject();
97 |
98 | // Silly case but check anyway
99 | if (eventSubject == null || eventSubject.length() == 0) {
100 | return false;
101 | }
102 |
103 | // Test if one of the subjects matches
104 | for (int i = 0; i < subjects.length; i++) {
105 | if (eventSubject.startsWith(subjects[i])) {
106 | return true;
107 | }
108 | }
109 |
110 | // No match
111 | return false;
112 | }
113 | }
114 |
115 | /*
116 | * $Log: Subscription.java,v $
117 | * Revision 1.5 2007/11/23 14:33:07 justb
118 | * core classes now configurable through factory
119 | *
120 | * Revision 1.4 2006/05/06 00:10:11 justb
121 | * various chgs but not too serious...
122 | *
123 | * Revision 1.3 2005/02/16 15:23:10 justb
124 | * multiple subject (, separated) support
125 | *
126 | * Revision 1.2 2005/01/18 16:47:10 justb
127 | * protocol changes for v2 and publishing from pushlet client
128 | *
129 | * Revision 1.1 2004/09/26 21:39:43 justb
130 | * allow multiple subscriptions and out-of-band requests
131 | *
132 | *
133 | */
134 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/core/XMLAdapter.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.core;
5 |
6 | import nl.justobjects.pushlet.util.Log;
7 |
8 | import javax.servlet.ServletOutputStream;
9 | import javax.servlet.http.HttpServletResponse;
10 |
11 | import java.io.IOException;
12 | import java.io.PrintWriter;
13 |
14 | /**
15 | * ClientAdapter that sends Events as XML.
16 | *
17 | * @author Just van den Broecke - Just Objects ©
18 | * @version $Id: XMLAdapter.java,v 1.7 2007/11/09 13:15:35 justb Exp $
19 | */
20 | class XMLAdapter implements ClientAdapter {
21 | /**
22 | * Header for strict XML
23 | */
24 | // public static final String XML_HEAD = "\n";
25 | private String contentType = "text/plain;charset=UTF-8";
26 | // private ServletOutputStream out = null;
27 | private PrintWriter pw = null;
28 | private HttpServletResponse servletRsp;
29 | private boolean strictXML;
30 |
31 | /**
32 | * Initialize.
33 | */
34 | public XMLAdapter(HttpServletResponse aServletResponse) {
35 | this(aServletResponse, false);
36 | }
37 |
38 | /**
39 | * Initialize.
40 | */
41 | public XMLAdapter(HttpServletResponse aServletResponse, boolean useStrictXML) {
42 | servletRsp = aServletResponse;
43 |
44 | // Strict XML implies returning a complete XML document
45 | strictXML = useStrictXML;
46 | if (strictXML) {
47 | contentType = "text/xml;charset=UTF-8";
48 | }
49 | }
50 |
51 | public void start() throws IOException {
52 |
53 | // If content type is plain text
54 | // then this is not a complete XML document, but rather
55 | // a stream of XML documents where each document is
56 | // an Event. In strict XML mode a complete document is returned.
57 | servletRsp.setContentType(contentType);
58 |
59 | // out = servletRsp.getOutputStream();
60 | pw = servletRsp.getWriter();
61 |
62 | // Don't need this further
63 | servletRsp = null;
64 |
65 | // Start XML document if strict XML mode
66 | if (strictXML) {
67 | // out.print("");
68 | pw.print("");
69 | }
70 | }
71 |
72 | /**
73 | * Force client to refresh the request.
74 | */
75 | public void push(Event anEvent) throws IOException {
76 | debug("event=" + anEvent);
77 |
78 | // Send the event as XML to the client and flush.
79 | pw.print(anEvent.toXML(strictXML));
80 | pw.flush();
81 | // out.print(anEvent.toXML(strictXML));
82 | // out.flush();
83 | }
84 |
85 | /**
86 | * No action.
87 | */
88 | public void stop() throws IOException {
89 | // Close XML document if strict XML mode
90 | if (strictXML) {
91 | pw.print("");
92 | pw.flush();
93 | // out.print("");
94 | // out.flush();
95 | }
96 | }
97 |
98 | private void debug(String s) {
99 | Log.debug("[XMLAdapter]" + s);
100 | }
101 | }
102 |
103 | /*
104 | * $Log: XMLAdapter.java,v $
105 | * Revision 1.7 2007/11/09 13:15:35 justb
106 | * add charset=UTF-8 in returned HTTP content types
107 | *
108 | * Revision 1.6 2006/05/15 11:52:53 justb
109 | * updates mainly for AJAX client
110 | *
111 | * Revision 1.5 2006/05/06 00:06:28 justb
112 | * first rough version AJAX client
113 | *
114 | * Revision 1.4 2005/05/06 19:44:00 justb
115 | * added xml-strict format
116 | *
117 | * Revision 1.3 2005/02/28 12:45:59 justb
118 | * introduced Command class
119 | *
120 | * Revision 1.2 2005/02/21 11:50:47 justb
121 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
122 | *
123 | * Revision 1.1 2005/02/18 10:07:23 justb
124 | * many renamings of classes (make names compact)
125 | *
126 | * Revision 1.7 2004/09/03 22:35:37 justb
127 | * Almost complete rewrite, just checking in now
128 | *
129 | * Revision 1.6 2004/03/10 14:01:55 justb
130 | * formatting and *Subscriber refactoring
131 | *
132 | * Revision 1.5 2003/08/15 08:37:40 justb
133 | * fix/add Copyright+LGPL file headers and footers
134 | *
135 | * Revision 1.4 2003/08/13 14:00:00 justb
136 | * some testing for applets; no real change
137 | *
138 | * Revision 1.3 2003/08/12 09:57:06 justb
139 | * replaced all print statements to Log.*() calls
140 | *
141 | * Revision 1.2 2003/05/19 21:56:29 justb
142 | * various fixes for applet clients
143 | *
144 | * Revision 1.1 2003/05/18 16:12:28 justb
145 | * adding support for XML encoded Events
146 | */
147 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/servlet/Pushlet.java:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ushelp/Pushlet-UserUnicast/b423f8e1cde4ae761d16cf55d732a16d5d540d1a/src/nl/justobjects/pushlet/servlet/Pushlet.java
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/test/PushletApplet.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.test;
5 |
6 | import nl.justobjects.pushlet.client.PushletClient;
7 | import nl.justobjects.pushlet.client.PushletClientListener;
8 | import nl.justobjects.pushlet.core.Event;
9 | import nl.justobjects.pushlet.core.Protocol;
10 | import nl.justobjects.pushlet.util.PushletException;
11 |
12 | import java.applet.Applet;
13 | import java.awt.*;
14 |
15 | /**
16 | * Tester for applet clients; displays incoming events in text area.
17 | *
18 | * @version $Id: PushletApplet.java,v 1.16 2005/02/18 09:54:15 justb Exp $
19 | * @author Just van den Broecke - Just Objects ©
20 | **/
21 | public class PushletApplet extends Applet implements PushletClientListener, Protocol {
22 | private TextArea textArea;
23 | private String host = "localhost";
24 | private int port = 8080;
25 | private String subject;
26 | private PushletClient pushletClient;
27 | private String VERSION = "15.feb.05 #5";
28 | private String PUSH_MODE = Protocol.MODE_PULL;
29 |
30 | /** One-time setup. */
31 | public void init() {
32 | // Subject to subscribe to
33 | subject = getParameter(P_SUBJECT);
34 |
35 | host = getDocumentBase().getHost();
36 | port = getDocumentBase().getPort();
37 |
38 | // Hmm sometimes this value is -1...(Mozilla with Java 1.3.0 on Win)
39 | if (port == -1) {
40 | port = 80;
41 | }
42 |
43 | setLayout(new GridLayout(1, 1));
44 | textArea = new TextArea(15, 40);
45 | textArea.setForeground(Color.yellow);
46 | textArea.setBackground(Color.gray);
47 | textArea.setEditable(false);
48 | add(textArea);
49 | p("PushletApplet - " + VERSION);
50 | }
51 |
52 | public void start() {
53 | dbg("start()");
54 | bailout();
55 |
56 | try {
57 | pushletClient = new PushletClient(host, port);
58 | p("Created PushletClient");
59 |
60 | pushletClient.join();
61 | p("Joined server");
62 |
63 | pushletClient.listen(this, PUSH_MODE);
64 | p("Listening in mode=" + PUSH_MODE);
65 |
66 | pushletClient.subscribe(subject);
67 | p("Subscribed to=" + subject);
68 | } catch (PushletException pe) {
69 | p("Error exception=" + pe);
70 | bailout();
71 | }
72 | }
73 |
74 | public void stop() {
75 | dbg("stop()");
76 | bailout();
77 | }
78 |
79 | /** Abort event from server. */
80 | public void onAbort(Event theEvent) {
81 | p(theEvent.toXML());
82 | bailout();
83 | }
84 |
85 | /** Data event from server. */
86 | public void onData(Event theEvent) {
87 | p(theEvent.toXML());
88 | }
89 |
90 | /** Heartbeat event from server. */
91 | public void onHeartbeat(Event theEvent) {
92 | p(theEvent.toXML());
93 | }
94 |
95 | /** Error occurred. */
96 | public void onError(String message) {
97 | p(message);
98 | bailout();
99 | }
100 |
101 | private void bailout() {
102 | if (pushletClient != null) {
103 | p("Stopping PushletClient");
104 | try {
105 | pushletClient.leave();
106 | } catch (PushletException ignore) {
107 | p("Error during leave pe=" + ignore);
108 |
109 | }
110 | pushletClient = null;
111 | }
112 | }
113 |
114 | /** Generic print. */
115 | private void p(String s) {
116 | dbg("event: " + s);
117 | synchronized (textArea) {
118 | textArea.append(s + "\n");
119 | }
120 | }
121 |
122 | /** Generic print. */
123 | private void dbg(String s) {
124 | System.out.println("[PushletApplet] " + s);
125 | }
126 | }
127 |
128 | /*
129 | * $Log: PushletApplet.java,v $
130 | * Revision 1.16 2005/02/18 09:54:15 justb
131 | * refactor: rename Publisher Dispatcher and single Subscriber class
132 | *
133 | * Revision 1.15 2005/02/15 15:46:36 justb
134 | * client API improves
135 | *
136 | * Revision 1.14 2005/02/15 13:28:33 justb
137 | * use new PushletClient lib
138 | *
139 | * Revision 1.13 2004/10/25 21:22:26 justb
140 | * *** empty log message ***
141 | *
142 | * Revision 1.12 2004/10/24 13:52:52 justb
143 | * small fixes in client lib
144 | *
145 | * Revision 1.11 2004/10/24 12:58:19 justb
146 | * revised client and test classes for new protocol
147 | *
148 | * Revision 1.10 2004/09/03 22:35:37 justb
149 | * Almost complete rewrite, just checking in now
150 | *
151 | * Revision 1.9 2004/08/12 13:18:54 justb
152 | * cosmetic changes
153 | *
154 | * Revision 1.8 2003/08/17 21:06:37 justb
155 | * version info change
156 | *
157 | * Revision 1.7 2003/08/15 08:37:41 justb
158 | * fix/add Copyright+LGPL file headers and footers
159 | *
160 | * Revision 1.6 2003/08/14 21:43:10 justb
161 | * improved Java client lifecycle; notably stopping listener thread
162 | *
163 | * Revision 1.5 2003/08/13 14:00:00 justb
164 | * some testing for applets; no real change
165 | *
166 | * Revision 1.4 2003/05/19 22:53:33 justb
167 | * more fixes for applets
168 | *
169 | * Revision 1.3 2003/05/19 21:56:29 justb
170 | * various fixes for applet clients
171 | *
172 | * Revision 1.2 2003/05/18 16:15:08 justb
173 | * support for XML encoded Events
174 | *
175 | * Revision 1.1.1.1 2002/09/24 21:02:33 justb
176 | * import to sourceforge
177 | *
178 | */
179 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/test/PushletPingApplication.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.test;
5 |
6 | import nl.justobjects.pushlet.client.PushletClient;
7 | import nl.justobjects.pushlet.client.PushletClientListener;
8 | import nl.justobjects.pushlet.core.Event;
9 | import nl.justobjects.pushlet.core.Protocol;
10 | import nl.justobjects.pushlet.util.PushletException;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | /**
16 | * Tester to demonstrate Pushlet use in Java applications.
17 | *
18 | * This program does two things:
19 | * (1) it subscribes to the subject "test/ping"
20 | * (2) it publishes an Event with subject "/test/ping" every few seconds.
21 | *
22 | * @version $Id: PushletPingApplication.java,v 1.15 2005/02/21 16:59:17 justb Exp $
23 | * @author Just van den Broecke - Just Objects ©
24 | **/
25 | public class PushletPingApplication extends Thread implements PushletClientListener, Protocol {
26 | private PushletClient pushletClient;
27 | private String host;
28 | private int port;
29 | private static final String SUBJECT = "/test/ping";
30 | private static final long PUBLISH_INTERVAL_MILLIS = 3000;
31 |
32 | public PushletPingApplication(String aHost, int aPort) {
33 | host = aHost;
34 | port = aPort;
35 | }
36 |
37 | public void run() {
38 | // Create and start a Pushlet client; we receive callbacks
39 | // through onHeartbeat() and onData().
40 | try {
41 | pushletClient = new PushletClient(host, port);
42 | pushletClient.setDebug(true);
43 | pushletClient.join();
44 | pushletClient.listen(this, Protocol.MODE_STREAM);
45 |
46 | // Test subscribe/unsubscribe
47 | String subscriptionId = pushletClient.subscribe(SUBJECT);
48 | pushletClient.unsubscribe(subscriptionId);
49 |
50 | // The real subscribe
51 | pushletClient.subscribe(SUBJECT);
52 | p("pushletClient started");
53 | } catch (PushletException pe) {
54 | p("Error in setting up pushlet session pe=" + pe);
55 | return;
56 | }
57 |
58 | // Publish an event to the server every N seconds.
59 | Map eventData = new HashMap(2);
60 | int seqNr = 1;
61 | while (true) {
62 | try {
63 | // Create event data
64 | eventData.put("seqNr", "" + seqNr++);
65 | eventData.put("time", "" + System.currentTimeMillis());
66 |
67 | // POST event to pushlet server
68 | pushletClient.publish(SUBJECT, eventData);
69 |
70 | p("published ping # " + (seqNr - 1) + " - sleeping...");
71 | Thread.sleep(PUBLISH_INTERVAL_MILLIS);
72 | } catch (Exception e) {
73 | p("Postlet exception: " + e);
74 | System.exit(-1);
75 | }
76 | }
77 | }
78 |
79 | /** Error occurred. */
80 | public void onError(String message) {
81 | p(message);
82 | }
83 |
84 | /** Abort event from server. */
85 | public void onAbort(Event theEvent) {
86 | p("onAbort received: " + theEvent);
87 | }
88 |
89 | /** Data event from server. */
90 | public void onData(Event theEvent) {
91 | // Calculate round trip delay
92 | long then = Long.parseLong(theEvent.getField("time"));
93 | long delay = System.currentTimeMillis() - then;
94 | p("onData: ping #" + theEvent.getField("seqNr") + " in " + delay + " ms");
95 | }
96 |
97 | /** Heartbeat event from server. */
98 | public void onHeartbeat(Event theEvent) {
99 | p("onHeartbeat received: " + theEvent);
100 | }
101 |
102 | /** Generic print. */
103 | public void p(String s) {
104 | System.out.println("[PushletPing] " + s);
105 | }
106 |
107 | /** Main program. */
108 | public static void main(String args[]) {
109 | for (int i = 0; i < 1; i++) {
110 | if (args.length == 0) {
111 | new PushletPingApplication("localhost", 8080).start();
112 | } else {
113 | // Supply a host and port
114 | new PushletPingApplication(args[0], Integer.parseInt(args[1])).start();
115 | }
116 | }
117 |
118 | }
119 | }
120 |
121 |
122 | /*
123 | * $Log: PushletPingApplication.java,v $
124 | * Revision 1.15 2005/02/21 16:59:17 justb
125 | * SessionManager and session lease introduced
126 | *
127 | * Revision 1.14 2005/02/21 11:50:48 justb
128 | * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
129 | *
130 | * Revision 1.13 2005/02/20 13:05:33 justb
131 | * removed the Postlet (integrated in Pushlet protocol)
132 | *
133 | * Revision 1.12 2005/02/18 09:54:15 justb
134 | * refactor: rename Publisher Dispatcher and single Subscriber class
135 | *
136 | * Revision 1.11 2005/02/15 15:46:37 justb
137 | * client API improves
138 | *
139 | * Revision 1.10 2005/02/15 13:28:08 justb
140 | * use new protocol lib and publish with PushletClient
141 | *
142 | * Revision 1.9 2004/10/24 13:52:52 justb
143 | * small fixes in client lib
144 | *
145 | * Revision 1.8 2004/10/24 12:58:19 justb
146 | * revised client and test classes for new protocol
147 | *
148 | * Revision 1.7 2004/09/03 22:35:37 justb
149 | * Almost complete rewrite, just checking in now
150 | *
151 | * Revision 1.6 2004/08/12 13:18:54 justb
152 | * cosmetic changes
153 | *
154 | * Revision 1.5 2004/03/10 14:53:10 justb
155 | * new
156 | *
157 | * Revision 1.4 2003/08/17 20:30:20 justb
158 | * cosmetic changes
159 | *
160 | * Revision 1.3 2003/08/15 08:37:41 justb
161 | * fix/add Copyright+LGPL file headers and footers
162 | *
163 | * Revision 1.2 2003/05/18 16:15:08 justb
164 | * support for XML encoded Events
165 | *
166 | * Revision 1.1.1.1 2002/09/24 21:02:33 justb
167 | * import to sourceforge
168 | *
169 | * Revision 1.1.1.1 2002/09/20 22:48:19 justb
170 | * import to SF
171 | *
172 | * Revision 1.1.1.1 2002/09/20 14:19:02 justb
173 | * first import into SF
174 | *
175 | * Revision 1.3 2000/08/31 12:49:50 just
176 | * added CVS comment tags for log and copyright
177 | *
178 | *
179 | */
180 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/test/SimpleListener.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.test;
5 |
6 | import nl.justobjects.pushlet.client.PushletClient;
7 | import nl.justobjects.pushlet.client.PushletClientListener;
8 | import nl.justobjects.pushlet.core.Event;
9 | import nl.justobjects.pushlet.core.Protocol;
10 | import nl.justobjects.pushlet.util.PushletException;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | /**
16 | * Demonstrates join-listen service.
17 | *
18 | * The pushlet protocol supports the "join-listen" service
19 | * which allows stateless (e.g. RESTful) clients to join/subscribe/listen
20 | * using a single HTTP request.
21 | *
22 | * @version $Id: SimpleListener.java,v 1.3 2005/03/14 14:07:23 justb Exp $
23 | * @author Just van den Broecke - Just Objects ©
24 | **/
25 | public class SimpleListener implements PushletClientListener, Protocol {
26 | private static String SUBJECT = "/temperature";
27 | private static final String MODE = MODE_STREAM;
28 |
29 | public SimpleListener(String aHost, int aPort) {
30 | // Create and start a Pushlet client; we receive callbacks
31 | // through onHeartbeat() and onData().
32 | try {
33 | PushletClient pushletClient = new PushletClient(aHost, aPort);
34 | pushletClient.setDebug(false);
35 | pushletClient.join();
36 | pushletClient.listen(this, MODE, SUBJECT);
37 | p("pushletClient started");
38 | } catch (PushletException pe) {
39 | p("Error in setting up pushlet session pe=" + pe);
40 | }
41 | }
42 |
43 | /** Error occurred. */
44 | public void onError(String message) {
45 | p(message);
46 | }
47 |
48 | /** Abort event from server. */
49 | public void onAbort(Event theEvent) {
50 | p("onAbort received: " + theEvent);
51 | }
52 |
53 | /** Data event from server. */
54 | public void onData(Event theEvent) {
55 | // Calculate round trip delay
56 | System.out.println(theEvent.toXML());
57 | }
58 |
59 | /** Heartbeat event from server. */
60 | public void onHeartbeat(Event theEvent) {
61 | p("onHeartbeat received: " + theEvent);
62 | }
63 |
64 | /** Generic print. */
65 | public void p(String s) {
66 | System.out.println("[SimpleListener] " + s);
67 | }
68 |
69 | /** Main program. */
70 | public static void main(String args[]) {
71 | if (args.length == 0) {
72 | new SimpleListener("localhost", 8080);
73 | }
74 | else if (args.length == 1) {
75 | SUBJECT = args[0];
76 | new SimpleListener("localhost", 8080);
77 | } else {
78 | SUBJECT = args[0];
79 | // args[1] and [2] should be host and port
80 | new SimpleListener(args[1], Integer.parseInt(args[2]));
81 | }
82 | }
83 | }
84 |
85 |
86 | /*
87 | * $Log: SimpleListener.java,v $
88 | * Revision 1.3 2005/03/14 14:07:23 justb
89 | * addded subject arg
90 | *
91 | * Revision 1.2 2005/02/28 21:21:32 justb
92 | * no chg
93 | *
94 | * Revision 1.1 2005/02/28 15:58:05 justb
95 | * added SimpleListener example
96 | *
97 | *
98 | */
99 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/test/StressTester.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.test;
5 |
6 | import nl.justobjects.pushlet.client.PushletClient;
7 | import nl.justobjects.pushlet.client.PushletClientListener;
8 | import nl.justobjects.pushlet.core.Event;
9 | import nl.justobjects.pushlet.core.Protocol;
10 | import nl.justobjects.pushlet.util.PushletException;
11 | import nl.justobjects.pushlet.util.Rand;
12 |
13 | import java.util.HashMap;
14 | import java.util.Map;
15 |
16 | /**
17 | * Tester to demonstrate Pushlet use in Java applications.
18 | *
19 | * This program does two things:
20 | * (1) it subscribes to the subject "test/ping"
21 | * (2) it publishes an Event with subject "/test/ping" every few seconds.
22 | *
23 | * @author Just van den Broecke - Just Objects ©
24 | * @version $Id: StressTester.java,v 1.2 2007/11/09 13:16:57 justb Exp $
25 | */
26 | public class StressTester implements Protocol {
27 | static private String host = "localhost";
28 | static private int port = 8080;
29 | static private int TESTER_COUNT = 10;
30 | private static final String SUBJECT = "/test/ping";
31 | private static final long MIN_PUBLISH_INTERVAL_MILLIS = 200;
32 | private static final long MAX_PUBLISH_INTERVAL_MILLIS = 1000;
33 | private static final long MIN_SUBSCRIBER_INTERVAL_MILLIS = 500;
34 | private static final long MAX_SUBSCRIBER_INTERVAL_MILLIS = 1000;
35 |
36 | public StressTester() {
37 | }
38 |
39 | public void run() {
40 | new EventPublisher().start();
41 | new EventSubscriber().start();
42 | }
43 |
44 | /**
45 | * Generic print.
46 | */
47 | public void err(String s) {
48 | System.out.println("[StressTester] ERROR" + s);
49 | }
50 |
51 | /**
52 | * Generic print.
53 | */
54 | public void p(String s) {
55 | System.out.println("[StressTester] " + s);
56 | }
57 |
58 | private class EventSubscriber extends Thread implements PushletClientListener {
59 | private PushletClient pushletClient;
60 |
61 | public void run() {
62 | while (true) {
63 | // Create and start a Pushlet client; we receive callbacks
64 | // through onHeartbeat() and onData().
65 | try {
66 | pushletClient = new PushletClient(host, port);
67 | // pushletClient.setDebug(true);
68 | pushletClient.join();
69 | pushletClient.listen(this, Protocol.MODE_STREAM);
70 | //p("listening");
71 | // Test subscribe/unsubscribe
72 | String subscriptionId = pushletClient.subscribe(SUBJECT);
73 | pushletClient.unsubscribe(subscriptionId);
74 |
75 | // The real subscribe
76 | subscriptionId = pushletClient.subscribe(SUBJECT);
77 | //p("sleeping");
78 | sleepRandom();
79 | //p("leaving");
80 | pushletClient.unsubscribe(subscriptionId);
81 | pushletClient.leave();
82 |
83 | } catch (Throwable t) {
84 | err("Error in EventSubscriber t=" + t);
85 | return;
86 | }
87 | }
88 | }
89 |
90 | /**
91 | * Error occurred.
92 | */
93 | public void onError(String message) {
94 | // p(message);
95 | }
96 |
97 | /**
98 | * Abort event from server.
99 | */
100 | public void onAbort(Event theEvent) {
101 | //p("onAbort received: " + theEvent);
102 | }
103 |
104 | /**
105 | * Data event from server.
106 | */
107 | public void onData(Event theEvent) {
108 | // Calculate round trip delay
109 | long then = Long.parseLong(theEvent.getField("time"));
110 | long delay = System.currentTimeMillis() - then;
111 | //p("onData: ping #" + theEvent.getField("seqNr") + " in " + delay + " ms");
112 | }
113 |
114 | /**
115 | * Heartbeat event from server.
116 | */
117 | public void onHeartbeat(Event theEvent) {
118 | //p("onHeartbeat received: " + theEvent);
119 | }
120 |
121 | private void sleepRandom() throws InterruptedException {
122 | Thread.sleep(Rand.randomLong(MIN_SUBSCRIBER_INTERVAL_MILLIS, MAX_SUBSCRIBER_INTERVAL_MILLIS));
123 | }
124 | }
125 |
126 | private class EventPublisher extends Thread {
127 | private PushletClient pushletClient;
128 |
129 | public void run() {
130 | // Create and start a Pushlet client; we receive callbacks
131 | // through onHeartbeat() and onData().
132 | try {
133 | pushletClient = new PushletClient(host, port);
134 | pushletClient.join();
135 |
136 | // p("pushletClient started");
137 | } catch (PushletException pe) {
138 | err("Error in EventPublisher pe=" + pe);
139 | return;
140 | }
141 |
142 | // Publish an event to the server every N seconds.
143 | Map eventData = new HashMap(2);
144 | int seqNr = 1;
145 | while (true) {
146 | try {
147 | // Create event data
148 | eventData.put("seqNr", "" + seqNr++);
149 | eventData.put("time", "" + System.currentTimeMillis());
150 |
151 | // POST event to pushlet server
152 | pushletClient.publish(SUBJECT, eventData);
153 |
154 | Thread.sleep(Rand.randomLong(MIN_PUBLISH_INTERVAL_MILLIS, MAX_PUBLISH_INTERVAL_MILLIS));
155 | } catch (Exception e) {
156 | p("EventPublisher exception: " + e);
157 | return;
158 | }
159 | }
160 | }
161 |
162 | }
163 |
164 | /**
165 | * Main program.
166 | */
167 | public static void main(String args[]) {
168 | if (args.length > 0) {
169 | TESTER_COUNT = Integer.parseInt(args[0]);
170 | }
171 | if (args.length == 3) {
172 | host = args[1];
173 | port = Integer.parseInt(args[2]);
174 | }
175 |
176 | for (int i = 0; i < TESTER_COUNT; i++) {
177 | new StressTester().run();
178 | }
179 |
180 | }
181 | }
182 |
183 | /*
184 | * $Log: StressTester.java,v $
185 | * Revision 1.2 2007/11/09 13:16:57 justb
186 | * use Rand from util package (and and Rand.java to pushlet client jar
187 | *
188 | * Revision 1.1 2005/02/28 17:16:58 justb
189 | * simple stress tester
190 | *
191 | *
192 | */
193 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/test/TestEventPullSources.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.test;
5 |
6 | import nl.justobjects.pushlet.core.Event;
7 | import nl.justobjects.pushlet.core.EventPullSource;
8 | import nl.justobjects.pushlet.core.SessionManager;
9 | import nl.justobjects.pushlet.util.Rand;
10 |
11 |
12 | /**
13 | * Event sources for testing.
14 | *
15 | * @author Just van den Broecke - Just Objects ©
16 | * @version $Id: TestEventPullSources.java,v 1.10 2007/11/09 13:16:57 justb Exp $
17 | */
18 | public class TestEventPullSources {
19 |
20 | /**
21 | * Produces a fake temparature event.
22 | */
23 | static public class TemperatureEventPullSource extends EventPullSource {
24 | String[] cities = {"amsterdam", null, "rotterdam", null,
25 | "leeuwarden", null, "twente", null, "limburg", null};
26 |
27 | public long getSleepTime() {
28 | return Rand.randomLong(3000, 5000);
29 | }
30 |
31 | public Event pullEvent() {
32 | int cityNumber = Rand.randomInt(0, (cities.length) / 2 - 1);
33 | int nextCityIndex = 2 * cityNumber;
34 |
35 | Event event = Event.createDataEvent("/temperature");
36 |
37 | event.setField("number", "" + cityNumber);
38 | event.setField("city", cities[nextCityIndex]);
39 | if (cities[nextCityIndex + 1] == null) {
40 | cities[nextCityIndex + 1] = "" + Rand.randomInt(5, 10);
41 | }
42 | int currentCityValue = new Integer(cities[nextCityIndex + 1]).intValue();
43 | int newCityValue = currentCityValue + Rand.randomInt(-2, 2);
44 |
45 | event.setField("value", "" + newCityValue);
46 | return event;
47 | }
48 | }
49 |
50 | /**
51 | * Produces a ping event.
52 | */
53 | static public class PingEventPullSource extends EventPullSource {
54 | public long getSleepTime() {
55 | return 3000;
56 | }
57 |
58 | public Event pullEvent() {
59 |
60 | return Event.createDataEvent("/pushlet/ping");
61 | }
62 | }
63 |
64 | /**
65 | * Produces an event related to the JVM status.
66 | */
67 | static public class SystemStatusEventPullSource extends EventPullSource {
68 | Runtime runtime = Runtime.getRuntime();
69 |
70 | public long getSleepTime() {
71 | return 4000;
72 | }
73 |
74 | public Event pullEvent() {
75 | Event event = Event.createDataEvent("/system/jvm");
76 | event.setField("totalMemory", "" + runtime.totalMemory());
77 | event.setField("freeMemory", "" + runtime.freeMemory());
78 | event.setField("maxMemory", "" + runtime.maxMemory());
79 | int activeCount = Thread.activeCount();
80 | event.setField("threads", "" + activeCount);
81 |
82 | return event;
83 | }
84 | }
85 |
86 | /**
87 | * Produces an event related to the Dispatcher.getInstance(). status.
88 | */
89 | static public class PushletStatusEventPullSource extends EventPullSource {
90 |
91 | public long getSleepTime() {
92 | return 5000;
93 | }
94 |
95 | public Event pullEvent() {
96 | Event event = Event.createDataEvent("/system/pushlet");
97 | // p(Dispatcher.getStatus());
98 | event.setField("publisher", "" + SessionManager.getInstance().getStatus());
99 | return event;
100 | }
101 | }
102 |
103 |
104 | /**
105 | * Produces events simulating stocks from the AEX.
106 | */
107 | static public class AEXStocksEventPullSource extends EventPullSource {
108 |
109 | String[] stocks = {"abn amro", "26",
110 | "aegon", "38",
111 | "ahold", "34",
112 | "akzo nobel", "51",
113 | "asm lith h", "26",
114 | "corus plc", "2",
115 | "dsm", "40",
116 | "elsevier", "14",
117 | "fortis (nl)", "32",
118 | "getronics", "6",
119 | "gucci", "94",
120 | "hagemeyer", "25",
121 | "heineken", "61",
122 | "ing c", "78",
123 | "klm", "66",
124 | "kon olie", "66",
125 | "kpn", "13",
126 | "numico c", "44",
127 | "philips, kon", "38",
128 | "tnt", "26",
129 | "unilever c", "62",
130 | "vendex kbb", "16",
131 | "vnu", "49",
132 | "wolt-kluw c", "25"};
133 |
134 | public long getSleepTime() {
135 | return Rand.randomLong(2000, 4000);
136 | }
137 |
138 | public Event pullEvent() {
139 | Event event = Event.createDataEvent("/stocks/aex");
140 | int stockNumber = Rand.randomInt(0, (stocks.length) / 2 - 1);
141 | int nextStockIndex = 2 * stockNumber;
142 |
143 | event.setField("number", "" + stockNumber);
144 | event.setField("name", stocks[nextStockIndex]);
145 | if (stocks[nextStockIndex + 1] == null) {
146 | stocks[nextStockIndex + 1] = "" + Rand.randomInt(50, 150);
147 | }
148 | int currentStockValue = new Integer(stocks[nextStockIndex + 1]).intValue();
149 | int newStockValue = currentStockValue + Rand.randomInt(-2, 2);
150 |
151 | event.setField("rate", "" + newStockValue + "." + Rand.randomInt(0, 99));
152 | return event;
153 | }
154 |
155 | }
156 |
157 | /**
158 | * Produces an URL event for automatic webpresentation.
159 | */
160 | static public class WebPresentationEventPullSource extends EventPullSource {
161 | String slideRootDir = "http://www.justobjects.org/cowcatcher/browse/j2ee/slides/";
162 | String[] slideURLs = {
163 | "ejb/j2ee/ejbover/slide.0.0.html",
164 | "ejb/j2ee/ejbover/slide.0.1.html",
165 | "ejb/j2ee/ejbover/slide.0.2.html",
166 | "ejb/j2ee/ejbover/slide.0.3.html",
167 | "ejb/j2ee/ejbover/slide.0.4.html"
168 | };
169 |
170 | int nextSlideNumber = 0;
171 |
172 | public long getSleepTime() {
173 | return 5000;
174 | }
175 |
176 | public Event pullEvent() {
177 | Event event = Event.createDataEvent("/webpres/auto");
178 | event.setField("url", slideRootDir + slideURLs[nextSlideNumber++]);
179 | if (nextSlideNumber == slideURLs.length) {
180 | nextSlideNumber = 0;
181 | }
182 | // Log.debug("Sending next slide url=" + event.getField("url"));
183 | return event;
184 | }
185 | }
186 |
187 | /**
188 | * Produces an event related to the Dispatcher.getInstance(). status.
189 | */
190 | static public class TestEventPullSource extends EventPullSource {
191 | private int number = 0;
192 |
193 | public long getSleepTime() {
194 | return 2000;
195 | }
196 |
197 | public Event pullEvent() {
198 | Event event = Event.createDataEvent("/system/test");
199 | // p(Dispatcher.getInstance()..getStatus());
200 | event.setField("nr", "" + (number++));
201 | event.setField("time", "" + System.currentTimeMillis());
202 | return event;
203 | }
204 |
205 | }
206 |
207 | /**
208 | * Util: stderr print method.
209 | */
210 | public static void e(String s) {
211 | System.out.println(s);
212 | }
213 |
214 | /**
215 | * Util: stdout print method.
216 | */
217 | public static void p(String s) {
218 | // System.out.println(s);
219 | }
220 | }
221 |
222 | /*
223 | * $Log: TestEventPullSources.java,v $
224 | * Revision 1.10 2007/11/09 13:16:57 justb
225 | * use Rand from util package (and and Rand.java to pushlet client jar
226 | *
227 | * Revision 1.9 2005/02/28 09:14:56 justb
228 | * sessmgr/dispatcher factory/singleton support
229 | *
230 | * Revision 1.8 2005/02/21 16:59:17 justb
231 | * SessionManager and session lease introduced
232 | *
233 | * Revision 1.7 2005/02/18 10:07:23 justb
234 | * many renamings of classes (make names compact)
235 | *
236 | * Revision 1.6 2005/02/18 09:54:15 justb
237 | * refactor: rename Publisher Dispatcher.getInstance(). and single Subscriber class
238 | *
239 | * Revision 1.5 2004/09/03 22:35:37 justb
240 | * Almost complete rewrite, just checking in now
241 | *
242 | * Revision 1.4 2003/12/03 21:16:58 justb
243 | * *** empty log message ***
244 | *
245 | * Revision 1.3 2003/08/15 08:37:41 justb
246 | * fix/add Copyright+LGPL file headers and footers
247 | *
248 | * Revision 1.2 2003/05/18 16:15:08 justb
249 | * support for XML encoded Events
250 | *
251 | * Revision 1.1.1.1 2002/09/24 21:02:33 justb
252 | * import to sourceforge
253 | *
254 | * Revision 1.1.1.1 2002/09/20 22:48:19 justb
255 | * import to SF
256 | *
257 | * Revision 1.1.1.1 2002/09/20 14:19:01 justb
258 | * first import into SF
259 | *
260 | * Revision 1.6 2002/07/29 10:17:22 just
261 | * no message
262 | *
263 | * Revision 1.5 2001/02/18 23:45:13 just
264 | * fixes for AEX
265 | *
266 | * Revision 1.4 2000/10/30 14:16:09 just
267 | * no message
268 | *
269 | * Revision 1.3 2000/08/31 12:49:50 just
270 | * added CVS comment tags for log and copyright
271 | *
272 | *
273 | */
274 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/test/TestEventPushSources.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.test;
5 |
6 | import nl.justobjects.pushlet.core.Dispatcher;
7 | import nl.justobjects.pushlet.core.Event;
8 | import nl.justobjects.pushlet.core.EventSource;
9 | import nl.justobjects.pushlet.util.Rand;
10 |
11 | import java.io.BufferedReader;
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 | import java.io.InputStreamReader;
15 | import java.net.URL;
16 | import java.util.StringTokenizer;
17 | import java.util.Vector;
18 |
19 | /**
20 | * Event sources that push events (for testing).
21 | *
22 | * @author Just van den Broecke - Just Objects ©
23 | * @version $Id: TestEventPushSources.java,v 1.10 2007/11/09 13:16:57 justb Exp $
24 | */
25 | public class TestEventPushSources {
26 |
27 | static public class AEXStocksEventPushSourceABN {
28 | String pageURL = "http://ri2.rois.com/E36msPtnZC0e15CVb4KT97JAGfGSfCcrvv6*FcyZIoNyh/CTIB/RI2APISNAP?RIC=0%23.AEX&FORMAT=XML";
29 | // This could be further expanded: getting the Reuters AEX stocks
30 | // as XML from ABN with this URL, but we may get into legal problems...
31 | }
32 |
33 | /**
34 | * Produces events from REAL stocks from the AEX.
35 | */
36 | static public class AEXStocksEventPushSource implements EventSource, Runnable {
37 | /**
38 | * Here we get our stocks from.
39 | */
40 | String pageURL = "http://www.debeurs.nl/koersen/aex.asp";
41 | Thread thread = null;
42 | volatile boolean active = false;
43 |
44 | // Since Baan has been thrown out...
45 | public final static int NR_OF_STOCKS = 24;
46 |
47 | public final static String EMPTY = "wait...";
48 | private int restarts = 1;
49 |
50 | class Stock {
51 | public String name = EMPTY;
52 | public String rate = EMPTY;
53 | volatile public boolean modified = false;
54 | }
55 |
56 | Vector stocksCache = new Vector(NR_OF_STOCKS);
57 |
58 | public AEXStocksEventPushSource() {
59 | for (int i = 0; i < NR_OF_STOCKS; i++) {
60 | stocksCache.addElement(new Stock());
61 | }
62 | // updateCache();
63 | }
64 |
65 | /**
66 | * Activate the event source.
67 | */
68 | synchronized public void activate() {
69 | e("activating...");
70 | // Stop a possibly running thread
71 | stopThread();
72 |
73 | // Start new thread and
74 | thread = new Thread(this, "AEXStocksPublisher-" + (restarts++));
75 | active = true;
76 | thread.start();
77 | e("activated");
78 | }
79 |
80 | /**
81 | * Deactivate the event source.
82 | */
83 | synchronized public void passivate() {
84 | e("passivating...");
85 | active = false;
86 | stopThread();
87 |
88 | // Mark the cache modified so we'll send the contents
89 | // on the next activation.
90 | for (int i = 0; i < NR_OF_STOCKS; i++) {
91 | ((Stock) stocksCache.elementAt(i)).modified = true;
92 | }
93 |
94 | e("passivated");
95 | }
96 |
97 |
98 | /**
99 | * Deactivate the event source.
100 | */
101 | synchronized public void stop() {
102 | }
103 |
104 | public void run() {
105 | // Publish cache content (if any) first.
106 | publishStocks();
107 |
108 | int count = 5; // enforce update first
109 | while (active) {
110 |
111 | // Only do work if active
112 | // Update cache every 10 secs.
113 | if (count == 5) {
114 | updateCache();
115 | count = 0;
116 | }
117 | count++;
118 |
119 | // Do updates for changed stock rates
120 | sendUpdates();
121 |
122 | // If we were interrupted just return.
123 | if (thread == null || thread.isInterrupted()) {
124 | break;
125 | }
126 |
127 | // Sleep 2 secs before sending next updates
128 | try {
129 | thread.sleep(2000);
130 | } catch (InterruptedException ie) {
131 | break;
132 | }
133 | }
134 |
135 | // Loop terminated: reset vars
136 | thread = null;
137 | active = false;
138 | }
139 |
140 | private String getStocksLine() {
141 | BufferedReader br = null;
142 | InputStream is = null;
143 | String nextLine = "";
144 |
145 | // Read line from server
146 | try {
147 | is = new URL(pageURL).openStream();
148 | br = new BufferedReader(new InputStreamReader(is));
149 | boolean foundLine = false;
150 | while (!foundLine) {
151 | nextLine = br.readLine();
152 | if (nextLine == null) {
153 | return "";
154 | }
155 | foundLine = (nextLine.indexOf("details.asp?iid=14053&parent=aex") != -1);
156 | }
157 | } catch (Exception e) {
158 | e("could not open or read URL pageURL=" + pageURL + " ex=" + e);
159 | return "";
160 | } finally {
161 | try {
162 | if (is != null) is.close();
163 | } catch (IOException ignore) {
164 | }
165 | }
166 | return nextLine;
167 | }
168 |
169 | private void publishStocks() {
170 | // Publish only modified stocks from the cache
171 | for (int i = 0; i < NR_OF_STOCKS; i++) {
172 | Stock nextStock = (Stock) stocksCache.elementAt(i);
173 |
174 | // Publish modified stocks
175 | if (nextStock.modified) {
176 | publishStock(i, nextStock.name, nextStock.rate);
177 | nextStock.modified = false;
178 | try {
179 | Thread.sleep(400);
180 | } catch (InterruptedException ie) {
181 | return;
182 | }
183 | }
184 | }
185 | }
186 |
187 | private void publishStock(int index, String name, String rate) {
188 | Event event = Event.createDataEvent("/stocks/aex");
189 | event.setField("number", index + "");
190 | event.setField("name", name);
191 | event.setField("rate", rate);
192 | p("publish: nr=" + index + " name=" + name + " rate=" + rate);
193 | Dispatcher.getInstance().multicast(event);
194 | }
195 |
196 | private void sendUpdates() {
197 | p("sending updates");
198 | // In any case send a random stock value by
199 | // making it modified, just to see something moving...
200 | int randomIndex = Rand.randomInt(0, NR_OF_STOCKS - 1);
201 | Stock randomStock = (Stock) stocksCache.elementAt(randomIndex);
202 | randomStock.modified = true;
203 |
204 | publishStocks();
205 | }
206 |
207 | private void stopThread() {
208 | if (thread != null) {
209 | thread.interrupt();
210 | thread = null;
211 | }
212 | }
213 |
214 | private void updateCache() {
215 | p("updating Cache");
216 |
217 | // Get the line with all stocks from HTML page
218 | String stocksLine = getStocksLine();
219 | if ("".equals(stocksLine)) {
220 | e("updateCache: stocksLine == null");
221 | return;
222 | }
223 |
224 | // Parse the stocksline and put in cache.
225 | // Beware: this is the messy part!!
226 | // We assume that stock/names and rates are located at
227 | // regular positions in the line.
228 | String delim = "<>";
229 | StringTokenizer st = new StringTokenizer(stocksLine, delim);
230 | String nextToken = "";
231 | int count = 0;
232 | String nextStock = "";
233 | String nextQuote = "";
234 | String currentQuote = null;
235 | int index = -1;
236 | while (st.hasMoreTokens()) {
237 | nextToken = st.nextToken();
238 | count++;
239 | // The with the stock name
240 | if ((count - 5) % 57 == 0) {
241 | p("c=" + count + " s=" + nextToken);
242 | nextStock = nextToken;
243 | }
244 |
245 | // The | with the stock rate
246 | if ((count - 10) % 57 == 0) {
247 | nextQuote = nextToken;
248 | index++;
249 | p("c=" + count + " val=" + nextQuote);
250 | Stock currentStock = (Stock) stocksCache.elementAt(index);
251 |
252 | // Only update new or modified stocks
253 | if (EMPTY.equals(currentStock.rate) || !currentStock.rate.equals(nextQuote)) {
254 | p("modified: " + nextStock);
255 | currentStock.name = nextStock;
256 | currentStock.rate = nextQuote;
257 | currentStock.modified = true;
258 | }
259 | }
260 | }
261 | }
262 | }
263 |
264 |
265 | /**
266 | * Util: stderr print method.
267 | */
268 | public static void e(String s) {
269 | System.out.println("AEXStocksEventPushSource: " + s);
270 | }
271 |
272 | /**
273 | * Util: stdout print method.
274 | */
275 | public static void p(String s) {
276 | // System.out.println(s);
277 | }
278 |
279 |
280 | public static void main(String[] args) {
281 | // new TestEventPushSources$AEXStocksEventPushSource();
282 | }
283 | }
284 |
285 | /*
286 | * $Log: TestEventPushSources.java,v $
287 | * Revision 1.10 2007/11/09 13:16:57 justb
288 | * use Rand from util package (and and Rand.java to pushlet client jar
289 | *
290 | * Revision 1.9 2005/02/28 09:14:56 justb
291 | * sessmgr/dispatcher factory/singleton support
292 | *
293 | * Revision 1.8 2005/02/21 16:59:17 justb
294 | * SessionManager and session lease introduced
295 | *
296 | * Revision 1.7 2005/02/18 10:07:23 justb
297 | * many renamings of classes (make names compact)
298 | *
299 | * Revision 1.6 2005/02/18 09:54:15 justb
300 | * refactor: rename Publisher Dispatcher and single Subscriber class
301 | *
302 | * Revision 1.5 2004/09/03 22:35:37 justb
303 | * Almost complete rewrite, just checking in now
304 | *
305 | * Revision 1.4 2004/03/10 15:45:55 justb
306 | * many cosmetic changes
307 | *
308 | * Revision 1.3 2003/08/15 08:37:41 justb
309 | * fix/add Copyright+LGPL file headers and footers
310 | *
311 | * Revision 1.2 2003/05/18 16:15:08 justb
312 | * support for XML encoded Events
313 | *
314 | * Revision 1.1.1.1 2002/09/24 21:02:33 justb
315 | * import to sourceforge
316 | *
317 | * Revision 1.1.1.1 2002/09/20 22:48:20 justb
318 | * import to SF
319 | *
320 | * Revision 1.1.1.1 2002/09/20 14:19:02 justb
321 | * first import into SF
322 | *
323 | * Revision 1.6 2001/02/18 23:45:13 just
324 | * fixes for AEX
325 | *
326 | * Revision 1.5 2000/10/30 14:16:09 just
327 | * no message
328 | *
329 | * Revision 1.4 2000/09/24 21:02:43 just
330 | * chnages due to changed webpage in debeurs.nl
331 | *
332 | * Revision 1.3 2000/08/31 12:49:50 just
333 | * added CVS comment tags for log and copyright
334 | *
335 | *
336 | */
337 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/util/DefaultLogger.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.util;
5 |
6 | /**
7 | * Default logger.
8 | *
9 | * Logs to stdout. Override this class by setting "logger.class" in pushlet.properties to your own logger
10 | * to integrate your own logging library.
11 | *
12 | * @author Just van den Broecke
13 | * @version $Id: DefaultLogger.java,v 1.2 2007/12/07 12:57:40 justb Exp $
14 | */
15 | public class DefaultLogger implements PushletLogger {
16 |
17 |
18 | /**
19 | * Level intialized with default.
20 | */
21 | private int level = LOG_LEVEL_INFO;
22 |
23 | public DefaultLogger() {
24 | }
25 |
26 | public void init() {
27 |
28 | }
29 |
30 | /**
31 | * Log message for trace level.
32 | *
33 | * @param aMessage the message to be logged
34 | */
35 | public void trace(String aMessage) {
36 | if (level < LOG_LEVEL_TRACE) {
37 | return;
38 | }
39 | print("TRACE", aMessage);
40 | }
41 |
42 | /**
43 | * Log message for debug level.
44 | *
45 | * @param aMessage the message to be logged
46 | */
47 | public void debug(String aMessage) {
48 | if (level < LOG_LEVEL_DEBUG) {
49 | return;
50 | }
51 | print("DEBUG", aMessage);
52 | }
53 |
54 | /**
55 | * Log message for info level.
56 | *
57 | * @param aMessage the message to be logged
58 | */
59 | public void info(String aMessage) {
60 | if (level < LOG_LEVEL_INFO) {
61 | return;
62 | }
63 | print("INFO", aMessage);
64 | }
65 |
66 | /**
67 | * Log message for warning level.
68 | *
69 | * @param aMessage the message to be logged
70 | */
71 | public void warn(String aMessage) {
72 | if (level < LOG_LEVEL_WARN) {
73 | return;
74 | }
75 | print("WARN", aMessage);
76 | }
77 |
78 | /**
79 | * Log message for warning level with exception.
80 | *
81 | * @param aMessage the message to be logged
82 | * @param aThrowable the exception
83 | */
84 | public void warn(String aMessage, Throwable aThrowable) {
85 | warn(aMessage + " exception=" + aThrowable);
86 | }
87 |
88 | /**
89 | * Log message for error level.
90 | *
91 | * @param aMessage the message to be logged
92 | */
93 | public void error(String aMessage) {
94 | if (level < LOG_LEVEL_ERROR) {
95 | return;
96 | }
97 | print("FATAL", aMessage);
98 | }
99 |
100 | /**
101 | * Log message (error level with exception).
102 | *
103 | * @param aMessage the message to be logged
104 | * @param aThrowable the exception
105 | */
106 | public void error(String aMessage, Throwable aThrowable) {
107 | error(aMessage + " exception=" + aThrowable);
108 | }
109 |
110 | /**
111 | * Log message for fatal level.
112 | *
113 | * @param aMessage the message to be logged
114 | */
115 | public void fatal(String aMessage) {
116 | if (level < LOG_LEVEL_FATAL) {
117 | return;
118 | }
119 | print("FATAL", aMessage);
120 | }
121 |
122 | /**
123 | * Log message (fatal level with exception).
124 | *
125 | * @param aMessage the message to be logged
126 | * @param aThrowable the exception
127 | */
128 | public void fatal(String aMessage, Throwable aThrowable) {
129 | fatal(aMessage + " exception=" + aThrowable);
130 | }
131 |
132 | /**
133 | * Set log level
134 | *
135 | * @param aLevel the message to be logged
136 | */
137 | public void setLevel(int aLevel) {
138 | level = aLevel;
139 | }
140 |
141 | /**
142 | * Print message.
143 | *
144 | * @param aTag the log type
145 | * @param aMessage the message to be logged
146 | */
147 | private void print(String aTag, String aMessage) {
148 | // SImple std out e.g. to catalina.out in Tomcat
149 | System.out.println("Pushlet[" + aTag + "] " + aMessage);
150 | }
151 |
152 | }
153 |
154 | /*
155 | * $Log: DefaultLogger.java,v $
156 | * Revision 1.2 2007/12/07 12:57:40 justb
157 | * added log4j and make it the default logging method
158 | *
159 | * Revision 1.1 2007/11/23 21:10:17 justb
160 | * add hooks for custom logging (you can override DefaultLogger in pushlet.properties)
161 | *
162 | *
163 | *
164 | */
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/util/Log.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.util;
5 |
6 | import nl.justobjects.pushlet.core.Config;
7 | import nl.justobjects.pushlet.core.ConfigDefs;
8 |
9 | /**
10 | * Logging wrapper.
11 | *
12 | * Provides a hook to direct logging to your own logging library. Override the DefaultLogger class by setting
13 | * "logger.class" in pushlet.properties to your own logger
14 | * to integrate your own logging library.
15 | *
16 | * @author Just van den Broecke
17 | * @version $Id: Log.java,v 1.5 2007/12/07 12:57:40 justb Exp $
18 | */
19 | public class Log implements ConfigDefs {
20 | /**
21 | * Init with default to have at least some logging.
22 | */
23 | private static PushletLogger logger = new DefaultLogger();
24 |
25 | /**
26 | * General purpose initialization.
27 | */
28 | static public void init() {
29 | try {
30 | logger = (PushletLogger) Config.getClass(LOGGER_CLASS, "nl.justobjects.pushlet.util.DefaultLogger").newInstance();
31 | } catch (Throwable t) {
32 | // Hmmm cannot log this since we don't have a log...
33 | System.out.println("Cannot instantiate Logger from config ex=" + t);
34 | return;
35 | }
36 |
37 | logger.init();
38 |
39 | // Set log level
40 | logger.setLevel(Config.getIntProperty(Config.LOG_LEVEL));
41 |
42 | logger.info("Logging intialized logger class=" + logger.getClass());
43 | }
44 |
45 | /**
46 | * Log message for trace level.
47 | *
48 | * @param aMessage the message to be logged
49 | */
50 | static public void trace(String aMessage) {
51 | logger.debug(aMessage);
52 | }
53 |
54 | /**
55 | * Log message for debug level.
56 | *
57 | * @param aMessage the message to be logged
58 | */
59 | static public void debug(String aMessage) {
60 | logger.debug(aMessage);
61 | }
62 |
63 | /**
64 | * Log message for info level.
65 | *
66 | * @param aMessage the message to be logged
67 | */
68 | static public void info(String aMessage) {
69 | logger.info(aMessage);
70 | }
71 |
72 | /**
73 | * Log message for warning level.
74 | *
75 | * @param aMessage the message to be logged
76 | */
77 | static public void warn(String aMessage) {
78 | logger.warn(aMessage);
79 | }
80 |
81 | /**
82 | * Log message for warning level with exception.
83 | *
84 | * @param aMessage the message to be logged
85 | * @param aThrowable the exception
86 | */
87 | static public void warn(String aMessage, Throwable aThrowable) {
88 | logger.warn(aMessage, aThrowable);
89 | }
90 |
91 | /**
92 | * Log message for error level.
93 | *
94 | * @param aMessage the message to be logged
95 | */
96 | static public void error(String aMessage) {
97 | logger.error(aMessage);
98 | }
99 |
100 | /**
101 | * Log message (error level with exception).
102 | *
103 | * @param aMessage the message to be logged
104 | * @param aThrowable the exception
105 | */
106 | static public void error(String aMessage, Throwable aThrowable) {
107 | logger.error(aMessage, aThrowable);
108 | }
109 |
110 | /**
111 | * Log message for fatal level.
112 | *
113 | * @param aMessage the message to be logged
114 | */
115 | static public void fatal(String aMessage) {
116 | logger.fatal(aMessage);
117 | }
118 |
119 | /**
120 | * Log message (fatal level with exception).
121 | *
122 | * @param aMessage the message to be logged
123 | * @param aThrowable the exception
124 | */
125 | static public void fatal(String aMessage, Throwable aThrowable) {
126 | logger.fatal(aMessage, aThrowable);
127 | }
128 |
129 | /**
130 | * Set log level
131 | *
132 | * @param aLevel the message to be logged
133 | */
134 | static public void setLevel(int aLevel) {
135 | logger.setLevel(aLevel);
136 | }
137 | }
138 |
139 | /*
140 | * $Log: Log.java,v $
141 | * Revision 1.5 2007/12/07 12:57:40 justb
142 | * added log4j and make it the default logging method
143 | *
144 | * Revision 1.4 2007/11/23 21:29:43 justb
145 | * add hooks for custom logging (you can override DefaultLogger in pushlet.properties)
146 | *
147 | * Revision 1.3 2007/11/23 21:10:17 justb
148 | * add hooks for custom logging (you can override DefaultLogger in pushlet.properties)
149 | *
150 | * Revision 1.2 2005/02/21 11:15:59 justb
151 | * support log levels
152 | *
153 | * Revision 1.1 2005/02/18 10:07:23 justb
154 | * many renamings of classes (make names compact)
155 | *
156 | * Revision 1.7 2004/09/03 22:35:37 justb
157 | * Almost complete rewrite, just checking in now
158 | *
159 | * Revision 1.6 2004/08/12 13:16:08 justb
160 | * make debug flag false
161 | *
162 | * Revision 1.5 2004/03/10 14:01:55 justb
163 | * formatting and *Subscriber refactoring
164 | *
165 | * Revision 1.4 2003/08/15 09:54:46 justb
166 | * fix javadoc warnings
167 | *
168 | * Revision 1.3 2003/08/15 08:37:40 justb
169 | * fix/add Copyright+LGPL file headers and footers
170 | *
171 | * Revision 1.2 2003/08/12 09:42:47 justb
172 | * enhancements
173 | *
174 | * Revision 1.1 2003/08/12 08:46:00 justb
175 | * cvs comment tags added
176 | *
177 | *
178 | */
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/util/Log4jLogger.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.util;
5 |
6 | import org.apache.log4j.Level;
7 | import org.apache.log4j.LogManager;
8 | import org.apache.log4j.Logger;
9 |
10 | /**
11 | * Logger to use Log4j for logging.
12 | *
13 | * Logs using Log4j.
14 | * This class will require a log4j library in the classpath of the Pushlet.
15 | *
16 | * @author Uli Romahn
17 | * @version $Id: Log4jLogger.java,v 1.1 2007/12/07 12:57:40 justb Exp $
18 | */
19 | public class Log4jLogger implements PushletLogger {
20 |
21 | /**
22 | * Level intialized with default.
23 | */
24 | private Logger logger = LogManager.getLogger("pushlet");
25 |
26 |
27 | /* (non-Javadoc)
28 | * @see nl.justobjects.pushlet.util.PushletLogger#init()
29 | */
30 | public void init() {
31 | setLevel(LOG_LEVEL_INFO);
32 | }
33 |
34 | /* (non-Javadoc)
35 | * @see nl.justobjects.pushlet.util.PushletLogger#debug(java.lang.String)
36 | */
37 | public void debug(String aMessage) {
38 | if (!logger.isDebugEnabled()) {
39 | return;
40 | }
41 | logger.debug(aMessage);
42 | }
43 |
44 | /* (non-Javadoc)
45 | * @see nl.justobjects.pushlet.util.PushletLogger#error(java.lang.String)
46 | */
47 | public void error(String aMessage) {
48 | logger.error(aMessage);
49 | }
50 |
51 | /* (non-Javadoc)
52 | * @see nl.justobjects.pushlet.util.PushletLogger#error(java.lang.String, java.lang.Throwable)
53 | */
54 | public void error(String aMessage, Throwable aThrowable) {
55 | logger.error(aMessage, aThrowable);
56 | }
57 |
58 | /* (non-Javadoc)
59 | * @see nl.justobjects.pushlet.util.PushletLogger#fatal(java.lang.String)
60 | */
61 | public void fatal(String aMessage) {
62 | logger.fatal(aMessage);
63 | }
64 |
65 | /* (non-Javadoc)
66 | * @see nl.justobjects.pushlet.util.PushletLogger#fatal(java.lang.String, java.lang.Throwable)
67 | */
68 | public void fatal(String aMessage, Throwable aThrowable) {
69 | logger.fatal(aMessage, aThrowable);
70 | }
71 |
72 | /* (non-Javadoc)
73 | * @see nl.justobjects.pushlet.util.PushletLogger#info(java.lang.String)
74 | */
75 | public void info(String aMessage) {
76 | if (!logger.isInfoEnabled()) {
77 | return;
78 | }
79 | logger.info(aMessage);
80 | }
81 |
82 | /* (non-Javadoc)
83 | * @see nl.justobjects.pushlet.util.PushletLogger#trace(java.lang.String)
84 | */
85 | public void trace(String aMessage) {
86 | logger.trace(aMessage);
87 | }
88 |
89 | /* (non-Javadoc)
90 | * @see nl.justobjects.pushlet.util.PushletLogger#warn(java.lang.String)
91 | */
92 | public void warn(String aMessage) {
93 | logger.warn(aMessage);
94 | }
95 |
96 | /* (non-Javadoc)
97 | * @see nl.justobjects.pushlet.util.PushletLogger#warn(java.lang.String, java.lang.Throwable)
98 | */
99 | public void warn(String aMessage, Throwable aThrowable) {
100 | logger.warn(aMessage, aThrowable);
101 | }
102 |
103 | /* (non-Javadoc)
104 | * @see nl.justobjects.pushlet.util.PushletLogger#setLevel(int)
105 | */
106 | public void setLevel(int aLevel) {
107 | if (aLevel < LOG_LEVEL_FATAL) {
108 | logger.setLevel(Level.OFF);
109 | } else {
110 | switch (aLevel) {
111 | case LOG_LEVEL_FATAL:
112 | logger.setLevel(Level.FATAL);
113 | break;
114 | case LOG_LEVEL_ERROR:
115 | logger.setLevel(Level.ERROR);
116 | break;
117 | case LOG_LEVEL_WARN:
118 | logger.setLevel(Level.WARN);
119 | break;
120 | case LOG_LEVEL_INFO:
121 | logger.setLevel(Level.INFO);
122 | break;
123 | case LOG_LEVEL_DEBUG:
124 | logger.setLevel(Level.DEBUG);
125 | break;
126 | case LOG_LEVEL_TRACE:
127 | logger.setLevel(Level.TRACE);
128 | break;
129 | default:
130 | logger.setLevel(Level.INFO);
131 | }
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/util/PushletException.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.util;
5 |
6 | /**
7 | * Generic exception wrapper.
8 | *
9 | * @author Just van den Broecke
10 | * @version $Id: PushletException.java,v 1.1 2005/02/15 15:14:34 justb Exp $
11 | */
12 | public class PushletException extends Exception {
13 |
14 | private PushletException() {
15 | }
16 |
17 | public PushletException(String aMessage, Throwable t) {
18 | super(aMessage + "\n embedded exception=" + t.toString());
19 | }
20 |
21 | public PushletException(String aMessage) {
22 | super(aMessage);
23 | }
24 |
25 | public PushletException(Throwable t) {
26 | this("PushletException: ", t);
27 | }
28 |
29 | public String toString() {
30 | return "PushletException: " + getMessage();
31 | }
32 | }
33 |
34 | /*
35 | * $Log: PushletException.java,v $
36 | * Revision 1.1 2005/02/15 15:14:34 justb
37 | * *** empty log message ***
38 | *
39 |
40 | *
41 | */
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/util/PushletLogger.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.util;
5 |
6 | import nl.justobjects.pushlet.core.ConfigDefs;
7 |
8 | /**
9 | * Logger interface to allow different logging providers.
10 | *
11 | *
12 | * @author Ulrich Romahn
13 | * @version $Id: PushletLogger.java,v 1.1 2007/12/07 12:57:40 justb Exp $
14 | */
15 | public interface PushletLogger extends ConfigDefs {
16 |
17 | /**
18 | * Method allowing to initialize our logger
19 | */
20 | public void init();
21 |
22 | /**
23 | * Log message for trace level.
24 | *
25 | * @param aMessage the message to be logged
26 | */
27 | public void trace(String aMessage);
28 |
29 | /**
30 | * Log message for debug level.
31 | *
32 | * @param aMessage the message to be logged
33 | */
34 | public void debug(String aMessage);
35 |
36 | /**
37 | * Log message for info level.
38 | *
39 | * @param aMessage the message to be logged
40 | */
41 | public void info(String aMessage);
42 |
43 | /**
44 | * Log message for warning level.
45 | *
46 | * @param aMessage the message to be logged
47 | */
48 | public void warn(String aMessage);
49 |
50 | /**
51 | * Log message for warning level with exception.
52 | *
53 | * @param aMessage the message to be logged
54 | * @param aThrowable the exception
55 | */
56 | public void warn(String aMessage, Throwable aThrowable);
57 |
58 | /**
59 | * Log message for error level.
60 | *
61 | * @param aMessage the message to be logged
62 | */
63 | public void error(String aMessage);
64 |
65 | /**
66 | * Log message (error level with exception).
67 | *
68 | * @param aMessage the message to be logged
69 | * @param aThrowable the exception
70 | */
71 | public void error(String aMessage, Throwable aThrowable);
72 |
73 | /**
74 | * Log message for fatal level.
75 | *
76 | * @param aMessage the message to be logged
77 | */
78 | public void fatal(String aMessage);
79 |
80 | /**
81 | * Log message (fatal level with exception).
82 | *
83 | * @param aMessage the message to be logged
84 | * @param aThrowable the exception
85 | */
86 | public void fatal(String aMessage, Throwable aThrowable);
87 |
88 | /**
89 | * Set log level
90 | *
91 | * @param aLevel a valid Level from ConfigDefs
92 | */
93 | public void setLevel(int aLevel);
94 | }
95 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/util/Rand.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 | package nl.justobjects.pushlet.util;
4 |
5 | import java.io.File;
6 | import java.io.FileOutputStream;
7 | import java.io.PrintWriter;
8 | import java.util.Random;
9 |
10 | /**
11 | * Randomizing routines.
12 | *
13 | * @author Just van den Broecke
14 | * @version $Id: Rand.java,v 1.4 2007/12/07 12:57:40 justb Exp $
15 | */
16 | public class Rand {
17 | private static char CHARS[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z'};
18 | private static char NON_VOWELS[] = {'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'z'};
19 | private static char VOWELS[] = {'a', 'e', 'i', 'o', 'u', 'y'};
20 | private static Random random = new Random();
21 |
22 | public static char randomChar() {
23 | return CHARS[randomInt(0, CHARS.length - 1)];
24 | }
25 |
26 | public static char randomVowel() {
27 | return VOWELS[randomInt(0, VOWELS.length - 1)];
28 | }
29 |
30 | public static char randomNonVowel() {
31 | return NON_VOWELS[randomInt(0, NON_VOWELS.length - 1)];
32 | }
33 |
34 | public static File randomTempDir() throws Exception {
35 | File file = new File(System.getProperty("java.io.tmpdir") + File.separator + "oasetest" + File.separator + randomString(12));
36 | file.mkdirs();
37 | file.deleteOnExit();
38 | return file;
39 | }
40 |
41 | public static File randomTempFile() throws Exception {
42 | File file = new File(System.getProperty("java.io.tmpdir") + File.separator + "oase-" + randomString(6));
43 | file.createNewFile();
44 | file.deleteOnExit();
45 | return file;
46 | }
47 |
48 | public static File randomBinaryFile(int aSize) throws Exception {
49 | File file = randomTempFile();
50 | FileOutputStream fos = new FileOutputStream(file);
51 | fos.write(randomBytes(aSize));
52 | fos.close();
53 | return file;
54 | }
55 |
56 | public static File randomTextFile(int aSize) throws Exception {
57 | File file = randomTempFile();
58 | PrintWriter pw = new PrintWriter(new FileOutputStream(file));
59 | pw.write(randomString(aSize));
60 | pw.close();
61 | return file;
62 | }
63 |
64 | public static byte[] randomBytes(int aSize) {
65 | return randomBlob(aSize);
66 | }
67 |
68 | public static byte[] randomBlob(int aSize) {
69 | byte[] retval = new byte[aSize];
70 | for (int i = 0; i < retval.length; i++) {
71 | retval[i] = randomByte();
72 | }
73 | return retval;
74 | }
75 |
76 | public static byte randomByte() {
77 | return (byte) random.nextInt();
78 | }
79 |
80 | public static double randomDouble() {
81 | return random.nextLong();
82 | }
83 |
84 |
85 | public static int randomInt() {
86 | return random.nextInt();
87 | }
88 |
89 | public static int randomInt(int min, int max) {
90 | return (int) ((Math.random() * (double) (max + 1 - min)) + min);
91 | }
92 |
93 | public static long randomLong() {
94 | return random.nextLong();
95 | }
96 |
97 | public static long randomLong(long min, long max) {
98 | return (long) ((Math.random() * (double) (max + 1L - min)) + min);
99 | }
100 |
101 | public static String randomName(int aLength) {
102 | StringBuffer sb = new StringBuffer(aLength);
103 | for (int i = 0; i < aLength; i++) {
104 | sb.append(i % 2 == 0 ? randomNonVowel() : randomVowel());
105 | }
106 | return sb.toString();
107 | }
108 |
109 | public static void randomSleep(long min, long max) {
110 | try {
111 | Thread.sleep(randomLong(min, max));
112 | } catch (InterruptedException ie) {
113 |
114 | }
115 | }
116 |
117 | public static String randomString(int aLength) {
118 | StringBuffer sb = new StringBuffer(aLength);
119 | for (int i = 0; i < aLength; i++) {
120 | sb.append(randomChar());
121 | }
122 | return sb.toString();
123 | }
124 |
125 | public static String randomString() {
126 | return "" + randomLong();
127 | }
128 |
129 |
130 | }
131 |
132 | /*
133 | * $Log: Rand.java,v $
134 | * Revision 1.4 2007/12/07 12:57:40 justb
135 | * added log4j and make it the default logging method
136 | *
137 | * Revision 1.3 2007/11/23 21:10:17 justb
138 | * add hooks for custom logging (you can override DefaultLogger in pushlet.properties)
139 | *
140 | * Revision 1.2 2004/09/03 22:35:38 justb
141 | * Almost complete rewrite, just checking in now
142 | *
143 | * Revision 1.1 2004/03/10 12:21:27 justb
144 | * *** empty log message ***
145 | *
146 | *
147 | */
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/util/Servlets.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.util;
5 |
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpServletResponse;
8 |
9 |
10 | /**
11 | * Servlet utilities.
12 | *
13 | * @author Just van den Broecke - Just Objects ©
14 | * @version $Id: Servlets.java,v 1.2 2007/11/23 21:10:17 justb Exp $
15 | */
16 | public class Servlets {
17 |
18 | /**
19 | * Get parameter; if not set or empty return null.
20 | */
21 | public static String getParameter(HttpServletRequest aRequest, String aName) {
22 | return getParameter(aRequest, aName, null);
23 | }
24 |
25 | /**
26 | * Get parameter; if not set or empty return specified default value.
27 | */
28 | public static String getParameter(HttpServletRequest aRequest, String aName, String aDefault) {
29 | String value = aRequest.getParameter(aName);
30 | if (value == null || value.length() == 0) {
31 | value = aDefault;
32 | }
33 | return value;
34 | }
35 |
36 | /**
37 | * Set HTTP headers to prevent caching.
38 | */
39 | public static void setNoCacheHeaders(HttpServletResponse aResponse) {
40 | // Set to expire far in the past.
41 | aResponse.setHeader("Expires", "Sat, 6 May 1995 12:00:00 GMT");
42 |
43 | // Set standard HTTP/1.1 no-cache headers.
44 | aResponse.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
45 |
46 | // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
47 | aResponse.addHeader("Cache-Control", "post-check=0, pre-check=0");
48 |
49 | // Set standard HTTP/1.0 no-cache header.
50 | aResponse.setHeader("Pragma", "no-cache");
51 |
52 | }
53 |
54 | }
55 |
56 | /*
57 | * $Log: Servlets.java,v $
58 | * Revision 1.2 2007/11/23 21:10:17 justb
59 | * add hooks for custom logging (you can override DefaultLogger in pushlet.properties)
60 | *
61 | * Revision 1.1 2004/09/20 22:01:40 justb
62 | * more changes for new protocol
63 | *
64 | *
65 | */
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/nl/justobjects/pushlet/util/Sys.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2000 Just Objects B.V.
2 | // Distributable under LGPL license. See terms of license at gnu.org.
3 |
4 | package nl.justobjects.pushlet.util;
5 |
6 |
7 | import java.io.FileInputStream;
8 | import java.io.IOException;
9 | import java.text.CharacterIterator;
10 | import java.text.StringCharacterIterator;
11 | import java.util.Properties;
12 |
13 | /**
14 | * Utilities that interact with the underlying OS/JVM.
15 | *
16 | * @author Just van den Broecke
17 | * @version $Id: Sys.java,v 1.4 2007/11/10 14:17:18 justb Exp $
18 | */
19 | public class Sys {
20 |
21 | public static final String USERUNICAST_ALL="all";
22 | public static final String USERUNICAST_FIRST="all";
23 | public static final String USERUNICAST_LAST="all";
24 |
25 | /**
26 | * Replace characters having special meaning inside HTML tags
27 | * with their escaped equivalents, using character entities such as '&'.
28 | *
29 | * The escaped characters are :
30 | *
31 | * - <
32 | *
- >
33 | *
- "
34 | *
- '
35 | *
- \
36 | *
- &
37 | *
38 | *
39 | * This method ensures that arbitrary text appearing inside a tag does not "confuse"
40 | * the tag. For example, HREF='Blah.do?Page=1&Sort=ASC'
41 | * does not comply with strict HTML because of the ampersand, and should be changed to
42 | * HREF='Blah.do?Page=1&Sort=ASC'. This is commonly seen in building
43 | * query strings. (In JSTL, the c:url tag performs this task automatically.)
44 | */
45 | static public String forHTMLTag(String aTagFragment) {
46 | final StringBuffer result = new StringBuffer();
47 |
48 | final StringCharacterIterator iterator = new StringCharacterIterator(aTagFragment);
49 | char character = iterator.current();
50 | while (character != CharacterIterator.DONE) {
51 | if (character == '<') {
52 | result.append("<");
53 | } else if (character == '>') {
54 | result.append(">");
55 | } else if (character == '\"') {
56 | result.append(""");
57 | } else if (character == '\'') {
58 | result.append("'");
59 | } else if (character == '\\') {
60 | result.append("\");
61 | } else if (character == '&') {
62 | result.append("&");
63 | } else {
64 | //the char is not a special one
65 | //add it to the result as is
66 | result.append(character);
67 | }
68 | character = iterator.next();
69 | }
70 | return result.toString();
71 | }
72 |
73 | /**
74 | * Load properties file from classpath.
75 | */
76 | static public Properties loadPropertiesResource(String aResourcePath) throws IOException {
77 | try {
78 | // Use the class loader that loaded our class.
79 | // This is required where for reasons like security
80 | // multiple class loaders exist, e.g. BEA WebLogic.
81 | // Thanks to Lutz Lennemann 29-aug-2000.
82 | ClassLoader classLoader = Sys.class.getClassLoader();
83 |
84 | Properties properties = new Properties();
85 |
86 | // Try loading it.
87 | properties.load(classLoader.getResourceAsStream(aResourcePath));
88 | return properties;
89 | } catch (Throwable t) {
90 | throw new IOException("failed loading Properties resource from " + aResourcePath);
91 | }
92 | }
93 |
94 | /**
95 | * Load properties file from file path.
96 | */
97 | static public Properties loadPropertiesFile(String aFilePath) throws IOException {
98 | try {
99 |
100 | Properties properties = new Properties();
101 |
102 | // Try loading it.
103 | properties.load(new FileInputStream(aFilePath));
104 | return properties;
105 | } catch (Throwable t) {
106 | throw new IOException("failed loading Properties file from " + aFilePath);
107 | }
108 | }
109 |
110 | /**
111 | * Shorthand for current time.
112 | */
113 | static public long now() {
114 | return System.currentTimeMillis();
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/src/修改说明.txt:
--------------------------------------------------------------------------------
1 |
2 | # JAVA 修改
3 |
4 | ### 1、中文无法传输问题修改
5 |
6 | 修改:`nl.justobjects.pushlet.core.XMLAdapter.java`
7 |
8 | 将
9 | ```JAVA
10 | private ServletOutputStream out = null;
11 | ```
12 | 替换为
13 | ```JAVA
14 | private PrintWriter pw = null;
15 | ```
16 | 并修改代码使用pw完成相应输出。
17 |
18 |
19 | 2、修改`nl.justobjects.pushlet.servlet.Pushlet.java`
20 |
21 | ```JAVA
22 | //添加如下代码
23 | // 点播推送用户和连接信息(用户名,所有连接集合_Set防止重复)
24 | public static final Map> UNICAST_USERS = new HashMap>();
25 |
26 | /**
27 | * Generic request handler (GET+POST).
28 | */
29 | protected void doRequest(Event anEvent, HttpServletRequest request,
30 | HttpServletResponse response) {
31 |
32 | //修改doRequest,添加如下代码
33 | String unicastType = Config.getProperty("unicast.type");
34 |
35 | // 获得pushlet的SessionID
36 | String sessionId = anEvent.getField(P_ID);
37 | if (sessionId != null) {
38 | // 获得当前的用户名(提前存入了Session)
39 | String userId = anEvent.getField("userId");
40 | if (userId != null) {
41 | // 用户多会话点播
42 | // 为用户会话初始化会话集合列表
43 | if (UNICAST_USERS.get(userId) == null) {
44 | UNICAST_USERS.put(userId, new ArrayList());
45 | }
46 | //登记会话,不存在会话,则添加
47 | if(!UNICAST_USERS.get(userId).contains(sessionId)){
48 | UNICAST_USERS.get(userId).add(sessionId);
49 | }
50 | }
51 | }
52 |
53 |
54 | //其他代码
55 | }
56 | ```
57 |
58 | 3、修改`nl.justobjects.pushlet.core.Dispatcher.java`
59 |
60 | 添加新方法:
61 | ```JAVA
62 | /**
63 | * 为指定用户点播
64 | *
65 | * @param event
66 | * @param userId
67 | */
68 | public synchronized void unicastUserId(Event event, String userId) {
69 | // 用户已连接
70 | if (Pushlet.UNICAST_USERS.get(userId) != null) {
71 | String unicastType = Config.getProperty("unicast.type");
72 | //点播类型默认为all,所有连接
73 | if(unicastType==null){
74 | unicastType="all";
75 | }
76 | unicastUserId(event,userId,unicastType);
77 | }
78 | }
79 |
80 | /**
81 | * 为指定用户点播
82 | *
83 | * @param event
84 | * @param userId
85 | * @param unicastType
86 | */
87 | public synchronized void unicastUserId(Event event, String userId,
88 | String unicastType) {
89 | // 用户已连接
90 | if (Pushlet.UNICAST_USERS.get(userId) != null) {
91 |
92 | List userSessions = Pushlet.UNICAST_USERS.get(userId);
93 | // 已退出的用户
94 | List removeSessions = new ArrayList();
95 |
96 | if (unicastType.equalsIgnoreCase("first")
97 | || unicastType.equalsIgnoreCase("last")) {
98 | if (userSessions.size() > 0) {
99 | String sessionId = userSessions.get(0); // first
100 | if (unicastType.equalsIgnoreCase("last")) {
101 | sessionId = userSessions.get(userSessions.size() - 1); // last
102 | }
103 | if (SessionManager.getInstance().getSession(sessionId) != null) {
104 | // 点播信息给用户的会话
105 | unicast(event, sessionId);
106 | } else {
107 | // 记录断开连接的用户
108 | removeSessions.add(sessionId);
109 | }
110 | }
111 |
112 | } else {
113 | // all
114 | for (int i = 0; i < userSessions.size(); i++) {
115 | String sessionId = userSessions.get(i);
116 | // 用户会话有效(sessionID存在)
117 | if (SessionManager.getInstance().getSession(sessionId) != null) {
118 | // 点播信息给用户的会话
119 | unicast(event, sessionId);
120 | } else {
121 | // 记录断开连接的用户
122 | removeSessions.add(sessionId);
123 | }
124 | }
125 | }
126 | // 用户离线,移除绑定的用户会话记录
127 | userSessions.removeAll(removeSessions);
128 | // 如果用户都已离开,则移除
129 | if (userSessions.size() == 0) {
130 | Pushlet.UNICAST_USERS.remove(userId);
131 | }
132 | }
133 | }
134 | ```
135 |
136 | 4、修改`nl.justobjects.pushlet.core.SessionManager.java`
137 |
138 | ```JAVA
139 | /**
140 | * Register session for removal.
141 | */
142 | public Session removeSession(Session aSession) {
143 | synchronized (mutex) {
144 | Session session = (Session) sessions.remove(aSession.getId());
145 | if (session != null) {
146 | info(session.getId() + " at " + session.getAddress() + " removed ");
147 | }
148 | //添加代码
149 | //已退出的用户
150 | List removeSessions = new ArrayList();
151 | for(Entry> e:Pushlet.UNICAST_USERS.entrySet()){
152 | if(e.getValue().contains(session.getId())){
153 | e.getValue().remove(session.getId()); //从用户广播注册列表移除当前会话
154 | }
155 | if(e.getValue().size()==0){
156 | removeSessions.add(e.getKey());
157 | }
158 | }
159 | //清除无效userId(已无会话)
160 | for(String userId:removeSessions){
161 | Pushlet.UNICAST_USERS.remove(userId);
162 | }
163 | sessionCacheDirty = true;
164 | return session;
165 | }
166 | }
167 | ```
168 |
169 | 4、修改`nl.justobjects.pushlet.util.Sys.java`
170 | 添加如下代码:
171 | ```JAV
172 | public static final String USERUNICAST_ALL="all";
173 | public static final String USERUNICAST_FIRST="all";
174 | public static final String USERUNICAST_LAST="all";
175 | ```
176 |
177 | # JAVASCRIPT 修改:
178 |
179 | 添加`userID`属性,在`_onEvent`函数的`refres`h判断中,注册服务器连接成功事件 `PL._doCallback(event, window.onConnected); `
180 |
181 | ```JS
182 | var PL = {
183 | //添加userID标识
184 | userId:'',
185 | //....
186 |
187 | _onEvent: function (event) {
188 | //...
189 | if (eventType == 'data') {
190 | //...
191 | } else if (eventType == 'refresh') {
192 | if (PL.state < PL.STATE_LISTENING) {
193 | PL._setStatus('not refreshing state=' + PL.STATE_LISTENING);
194 | }
195 | var timeout = event.get('p_wait');
196 | setTimeout(function () {
197 | PL._doRequest('refresh');
198 | //添加服务器连接成功事件
199 | PL._doCallback(event, window.onConnected);
200 | }, timeout);
201 | return;
202 | } else if (eventType == 'error') {
203 | //...
204 | }
205 | //...
206 | },
207 | //...
208 | }
209 | ```
--------------------------------------------------------------------------------
|