1 | CSP Plugins
|
---|
2 | -----------
|
---|
3 |
|
---|
4 | As of 0.8.6 the proxy has a plugin framework. This is another one-hour hack so don't read too much into it.
|
---|
5 |
|
---|
6 | NOTE: With 0.8.10 it is also possible to add connector implementations using these same methods, although connectors
|
---|
7 | wouldn't be loaded under the plugins section (see README.ConaxConnector.txt).
|
---|
8 |
|
---|
9 | Some ideas for plugins:
|
---|
10 | - CA emulation, have the proxy read/fetch/use the static keys and the clients wont have to.
|
---|
11 | - Your own simplified CS protocol.
|
---|
12 | - BISS.
|
---|
13 | - Log aggregation, have the proxy receive udp/syslog events from servers and clients to aid troubleshooting.
|
---|
14 | - Fault management, triggering nagios or zabbix alarms on critical errors.
|
---|
15 |
|
---|
16 | To create a new plugin, first read README.Compiling.txt and ensure you can successfully build the proxy itself.
|
---|
17 | The source code itself is the ultimate documentation and to find a place to start, you can use the following param when
|
---|
18 | starting java: -Dcom.bowman.cardserv.util.tracexmlcfg=true
|
---|
19 | That will keep track of where all proxy.xml accesses are made from in the code, and dump it to file when
|
---|
20 | you request it via the admin section of the status web.
|
---|
21 |
|
---|
22 | Then use the following procedure:
|
---|
23 | 1. Copy one of the existing plugin directory trees and rename it (e.g MyTestPlugin).
|
---|
24 | 2. Edit build.xml in the plugin dir and search/replace the old name to match yours.
|
---|
25 | 3. Place any extra dependencies your plugin will need in the lib dir. Remove any jars that are not needed.
|
---|
26 | NOTE: As of 0.9.0, the plugin class loader can fetch needed jars for you, to avoid having to distribute them with
|
---|
27 | the plugin. See below for details.
|
---|
28 | 4. Start editing the source, renaming the main plugin class and file to match your new name.
|
---|
29 | 5. Run ant in the plugin dir to compile and build the jar (it ends up in dist).
|
---|
30 |
|
---|
31 | Place the jar in proxy-home/plugins, then add the config elements for the plugin to proxy.xml, e.g:
|
---|
32 | <proxy-plugins>
|
---|
33 | ...
|
---|
34 | <plugin class="com.bowman.cardserv.MyTestPlugin" enabled="true" jar-file="mytestplugin.jar">
|
---|
35 | <plugin-config>
|
---|
36 | // plugin specific config here
|
---|
37 | </plugin-config>
|
---|
38 | </plugin>
|
---|
39 | ...
|
---|
40 | </proxy-plugins>
|
---|
41 |
|
---|
42 | The plugin lifecycle is as follows:
|
---|
43 | 1. The main plugin class (that implements ProxyPlugin) is instantiated.
|
---|
44 | 2. configUpdated() is called with the settings specified for this plugin in proxy.xml.
|
---|
45 | 3. Assuming configUpdated() didn't throw any exceptions, start() is called, with a reference to the proxy istself.
|
---|
46 | 4. Next time proxy.xml is touched or changed, stop() will be called allowing the plugin to cleanup before unload.
|
---|
47 |
|
---|
48 | For most plugins it should be possible to replace the jar file and update/touch proxy.xml to have the new version loaded
|
---|
49 | without restarting.
|
---|
50 | NOTE: As of 0.8.11, plugin jars are watched for changes, and automatically reloaded when replaced.
|
---|
51 |
|
---|
52 | The plugin api is fairly primitive, here's a quick guide for a single class example:
|
---|
53 |
|
---|
54 | package com.bowman.cardserv;
|
---|
55 | // You can use any package, but there could be some protected methods only accessible from this one in the main classes.
|
---|
56 |
|
---|
57 | import com.bowman.cardserv.interfaces.*;
|
---|
58 | import com.bowman.cardserv.util.*;
|
---|
59 | import com.bowman.cardserv.rmi.*;
|
---|
60 | import com.bowman.cardserv.web.*;
|
---|
61 | // Depending on what you intend to do, different parts of the proxy source needs to be imported.
|
---|
62 |
|
---|
63 | import java.io.*;
|
---|
64 | import java.util.*;
|
---|
65 |
|
---|
66 |
|
---|
67 | public class MyTestPlugin implements ProxyPlugin {
|
---|
68 | // The main class of the plugin must implement this interface: com.bowman.cardserv.interfaces.ProxyPlugin
|
---|
69 | // If you want the plugin to have a say in connector selection, also implement: com.bowman.cardserv.interfaces.CwsSelector
|
---|
70 | // If you want the plugin to filter replies from connectors (dcw's) implement: com.bowman.cardserv.interfaces.ReplyFilter
|
---|
71 |
|
---|
72 | // If your plugin makes use of 3rd party libraries, create an array with the direct urls to each dependency:
|
---|
73 | public static final String[] dependencyUrls = new String[] {
|
---|
74 | "http://www.host.com/path/jarfile.jar",
|
---|
75 | "http://www.host2.org/jarfile2.jar"
|
---|
76 | }
|
---|
77 |
|
---|
78 | // Methods outlined below...
|
---|
79 |
|
---|
80 | public void configUpdated(ProxyXmlConfig xml) throws ConfigException {
|
---|
81 | // Whenever proxy.xml is changed the plugin will be discarded and reloaded. Settings from proxy.xml available here.
|
---|
82 | // The plugin should verify that they make sense and throw a ConfigException if they dont.
|
---|
83 |
|
---|
84 | // If you have 3rd party dependencies, configure the plugin classloader to fetch them first:
|
---|
85 | PluginClassLoader pcl = (PluginClassLoader)getClass().getClassLoader();
|
---|
86 | pcl.resolveDependencies(dependencyUrls, logger);
|
---|
87 | // Now you can make use of any class from the listed jars.
|
---|
88 | }
|
---|
89 |
|
---|
90 | public void start(CardServProxy proxy) {
|
---|
91 | // Called after configUpdated(). Tells the plugin to initialize everything and start any background jobs etc.
|
---|
92 | // If it needs access to the proxy it should store the reference passed to this method.
|
---|
93 | // This reference can be used to get access to most of the functionality, see MessagingPlugin for one example.
|
---|
94 | // Any control or status commands should be registered here.
|
---|
95 | }
|
---|
96 |
|
---|
97 | public void stop() {
|
---|
98 | // Called before unload is attempted (every time proxy.xml changes, or when the plugin jar is replaced).
|
---|
99 | // The plugin should stop all threads and remove any references to itself that it might have registered elsewhere.
|
---|
100 | // Any control or status commands should be unregistered here.
|
---|
101 | }
|
---|
102 |
|
---|
103 | public String getName() {
|
---|
104 | return "MyTestPlugin"; // Displayname for the plugin
|
---|
105 | }
|
---|
106 |
|
---|
107 | public String getDescription() {
|
---|
108 | return "A test plugin."; // Description
|
---|
109 | }
|
---|
110 |
|
---|
111 | public Properties getProperties() { // arbitrary parameters shown in the proxy-plugins status command output (admin only, 0.9.0+)
|
---|
112 | Properties p = new Properties();
|
---|
113 | p.setProperty("relevant-param", "value");
|
---|
114 | return p;
|
---|
115 | }
|
---|
116 |
|
---|
117 | public CamdNetMessage doFilter(ProxySession proxySession, CamdNetMessage msg) {
|
---|
118 | // Called with every single message sent to the proxy from a client session, or from the proxy back to the sessions.
|
---|
119 | // The plugin can modify the message, return something else entirely, or block it (by doing msg.setFilteredBy("Reason text")).
|
---|
120 | // See the LoggingPlugin or EmmAnalyzerPlugin for examples.
|
---|
121 |
|
---|
122 | return msg; // = do nothing
|
---|
123 | }
|
---|
124 |
|
---|
125 | public byte[] getResource(String path, boolean admin) {
|
---|
126 | // This allows the plugin to export content to the httpd
|
---|
127 | // The following code ensure that anything placed in /web in the plugin jar file is available via the proxy web.
|
---|
128 | // Files are accessed using http://proxy.host.com/plugin/PluginName/path/filename.ext
|
---|
129 | // The admin flag indicates if the user logged into the web as an admin user.
|
---|
130 |
|
---|
131 | if(path.startsWith("/")) path = path.substring(1);
|
---|
132 | try {
|
---|
133 | DataInputStream dis = new DataInputStream(getClass().getResourceAsStream("/web/" + path));
|
---|
134 | byte[] buf = new byte[dis.available()];
|
---|
135 | dis.readFully(buf);
|
---|
136 | return buf;
|
---|
137 | } catch (IOException e) {
|
---|
138 | return null;
|
---|
139 | }
|
---|
140 |
|
---|
141 | // Note that user login will be required to access anything this way, with one exception:
|
---|
142 | // The plugin will be queried for a file called "load.js" whenever anyone accesses the default status web (cs-status).
|
---|
143 | // This file allows the plugin to hook itself into the javascript in cs-status.war. See GeoipPlugin for an example.
|
---|
144 | }
|
---|
145 |
|
---|
146 | public byte[] getResource(String path, byte[] inData, boolean admin) {
|
---|
147 | // Same as the above method, except for http POST instead of GET.
|
---|
148 | // Can be used to allow file uploads from the web to a plugin (or any custom data from the client side scripting).
|
---|
149 |
|
---|
150 | return null;
|
---|
151 | }
|
---|
152 |
|
---|
153 |
|
---|
154 | // If your plugin implements the CwsSelector interface, the proxy will call this method for every incoming ecm request.
|
---|
155 | // The call includes the session where the message originated, and the connectors (set of name Strings) that the proxy
|
---|
156 | // thinks are valid choices to handle this request. Your plugin can remove names from this list based on the contents
|
---|
157 | // of the CamdNetMessage data, or based on the properties of the user associated with the session.
|
---|
158 | /*
|
---|
159 | public Set doSelection(ProxySession session, CamdNetMessage msg, Set connectors) {
|
---|
160 | return connectors;
|
---|
161 | }
|
---|
162 | */
|
---|
163 |
|
---|
164 | // If your plugin implements the ReplyFilter interface, this method will get called for every connector reply (dcw).
|
---|
165 | // This happens before the proxy does anything with the reply, and allows the plugin to look for and remove bad dcw's.
|
---|
166 | // To change a suspicious dcw reply into a cannot decode, do msg.setCustomData(new byte[0]).
|
---|
167 | // To silently block a reply entirely (probably a bad idea) return null instead of the msg.
|
---|
168 | /*
|
---|
169 | public CamdNetMessage doReplyFilter(CwsConnector connector, CamdNetMessage msg) {
|
---|
170 | return msg; // = do nothing
|
---|
171 | }
|
---|
172 | */
|
---|
173 |
|
---|
174 | } |
---|