Mercurial > hg > monetdb-java
comparison src/main/java/org/monetdb/merovingian/Control.java @ 391:f523727db392
Moved Java classes from packages starting with nl.cwi.monetdb.* to package org.monetdb.*
This naming complies to the Java Package Naming convention as MonetDB's main website is www.monetdb.org.
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Thu, 12 Nov 2020 22:02:01 +0100 (2020-11-12) |
parents | src/main/java/nl/cwi/monetdb/merovingian/Control.java@54137aeb1f92 |
children | bf9f6b6ecf40 |
comparison
equal
deleted
inserted
replaced
390:6199e0be3c6e | 391:f523727db392 |
---|---|
1 /* | |
2 * This Source Code Form is subject to the terms of the Mozilla Public | |
3 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 * | |
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V. | |
7 */ | |
8 | |
9 package org.monetdb.merovingian; | |
10 | |
11 import org.monetdb.mcl.net.MapiSocket; | |
12 import org.monetdb.mcl.io.*; | |
13 import org.monetdb.mcl.MCLException; | |
14 import org.monetdb.mcl.parser.MCLParseException; | |
15 import java.io.BufferedReader; | |
16 import java.io.IOException; | |
17 import java.io.InputStreamReader; | |
18 import java.io.PrintStream; | |
19 import java.net.Socket; | |
20 import java.net.URI; | |
21 import java.net.URISyntaxException; | |
22 import java.util.ArrayList; | |
23 import java.util.Collections; | |
24 import java.util.List; | |
25 import java.util.Properties; | |
26 | |
27 /** | |
28 * A Control class to perform operations on a remote merovingian | |
29 * instance, using the TCP control protocol. | |
30 * | |
31 * This class implements the protocol specific bits to perform all | |
32 * possible actions against a merovingian server that has remote control | |
33 * facilities enabled. | |
34 * | |
35 * In the merovingian world, other merovingians in the vicinity are | |
36 * known to each merovingian, allowing to perform cluster wide actions. | |
37 * The implementation taken in this class is to require one known | |
38 * merovingian to get insight in the entire network. Note that | |
39 * connecting to a merovingian requires a passphrase that is likely to | |
40 * be different for each merovingian. | |
41 * | |
42 * @author Fabian Groffen | |
43 * @version 1.0 | |
44 */ | |
45 public class Control { | |
46 /** The host to connect to */ | |
47 private final String host; | |
48 /** The port to connect to */ | |
49 private final int port; | |
50 /** The passphrase to use when connecting */ | |
51 private final String passphrase; | |
52 /** The file we should write MapiSocket debuglog to */ | |
53 private String debug; | |
54 | |
55 | |
56 /** | |
57 * Constructs a new Control object. | |
58 * | |
59 * @param host - IP address or DNS | |
60 * @param port - port number | |
61 * @param passphrase - phrase used to pass authorisation | |
62 * @throws IllegalArgumentException if host, port or passphrase are | |
63 * null or <= 0 | |
64 */ | |
65 public Control(String host, int port, String passphrase) | |
66 throws IllegalArgumentException | |
67 { | |
68 this.host = host; | |
69 this.port = port; | |
70 this.passphrase = passphrase; | |
71 } | |
72 | |
73 /** | |
74 * Instructs to write a MCL protocol debug log to the given file. | |
75 * This affects any newly performed command, and can be changed | |
76 * inbetween commands. Passing null to this method disables the | |
77 * debug log. | |
78 * | |
79 * @param filename the filename to write debug information to, or null | |
80 */ | |
81 public void setDebug(String filename) { | |
82 this.debug = filename; | |
83 } | |
84 | |
85 private String controlHash(String pass, String salt) { | |
86 long ho; | |
87 long h = 0; | |
88 | |
89 /* use a very simple hash function designed for a single int val | |
90 * (hash buckets), we can make this more interesting if necessary in | |
91 * the future. | |
92 * http://www.cs.hmc.edu/~geoff/classes/hmc.cs070.200101/homework10/hashfuncs.html */ | |
93 | |
94 for (int i = 0; i < pass.length(); i++) { | |
95 ho = h & 0xF8000000L; | |
96 h <<= 5; | |
97 h &= 0xFFFFFFFFL; | |
98 h ^= ho >>> 27; | |
99 h ^= (int)(pass.charAt(i)); | |
100 } | |
101 | |
102 for (int i = 0; i < salt.length(); i++) { | |
103 ho = h & 0xF8000000L; | |
104 h <<= 5; | |
105 h &= 0xFFFFFFFFL; | |
106 h ^= ho >> 27; | |
107 h ^= (int)(salt.charAt(i)); | |
108 } | |
109 | |
110 return(Long.toString(h)); | |
111 } | |
112 | |
113 final static private String RESPONSE_OK = "OK"; | |
114 | |
115 private List<String> sendCommand( | |
116 String database, String command, boolean hasOutput) | |
117 throws MerovingianException, IOException | |
118 { | |
119 BufferedMCLReader min; | |
120 BufferedMCLWriter mout; | |
121 MapiSocket ms = new MapiSocket(); | |
122 ms.setDatabase("merovingian"); | |
123 ms.setLanguage("control"); | |
124 if (debug != null) | |
125 ms.debug(debug); | |
126 try { | |
127 ms.connect(host, port, "monetdb", passphrase); | |
128 min = ms.getReader(); | |
129 mout = ms.getWriter(); | |
130 } catch (MCLParseException e) { | |
131 throw new MerovingianException(e.getMessage()); | |
132 } catch (MCLException e) { | |
133 throw new MerovingianException(e.getMessage()); | |
134 } catch (AssertionError e) { // mcl panics | |
135 ms.close(); | |
136 | |
137 // Try old protocol instead | |
138 Socket s; | |
139 PrintStream out; | |
140 BufferedReader in; | |
141 s = new Socket(host, port); | |
142 out = new PrintStream(s.getOutputStream()); | |
143 in = new BufferedReader( | |
144 new InputStreamReader(s.getInputStream())); | |
145 try { | |
146 /* login ritual, step 1: get challenge from server */ | |
147 String response = in.readLine(); | |
148 if (response == null) | |
149 throw new MerovingianException("server closed the connection"); | |
150 | |
151 if (!response.startsWith("merovingian:1:") && | |
152 !response.startsWith("merovingian:2:")) | |
153 throw new MerovingianException("unsupported merovingian server"); | |
154 | |
155 String[] tokens = response.split(":"); | |
156 if (tokens.length < 3) | |
157 throw new MerovingianException("did not understand merovingian server"); | |
158 String version = tokens[1]; | |
159 String token = tokens[2]; | |
160 | |
161 response = controlHash(passphrase, token); | |
162 if (version.equals("1")) { | |
163 out.print(response + "\n"); | |
164 } else if (version.equals("2")) { | |
165 // we only support control mode for now | |
166 out.print(response + ":control\n"); | |
167 } | |
168 | |
169 response = in.readLine(); | |
170 if (response == null) { | |
171 throw new MerovingianException("server closed the connection"); | |
172 } | |
173 | |
174 if (!response.equals(RESPONSE_OK)) { | |
175 throw new MerovingianException(response); | |
176 } | |
177 | |
178 /* send command, form is simple: "<db> <cmd>\n" */ | |
179 out.print(database + " " + command + "\n"); | |
180 | |
181 /* Response has the first line either "OK\n" or an error | |
182 * message. In case of a command with output, the data will | |
183 * follow the first line */ | |
184 response = in.readLine(); | |
185 if (response == null) { | |
186 throw new MerovingianException("server closed the connection"); | |
187 } | |
188 if (!response.equals(RESPONSE_OK)) { | |
189 throw new MerovingianException(response); | |
190 } | |
191 | |
192 if (!hasOutput) | |
193 return null; | |
194 | |
195 ArrayList<String> l = new ArrayList<String>(); | |
196 while ((response = in.readLine()) != null) { | |
197 l.add(response); | |
198 } | |
199 return l; | |
200 } finally { | |
201 in.close(); | |
202 out.close(); | |
203 s.close(); | |
204 } | |
205 } | |
206 | |
207 mout.writeLine(database + " " + command + "\n"); | |
208 ArrayList<String> l = new ArrayList<String>(); | |
209 String tmpLine = min.readLine(); | |
210 int linetype = min.getLineType(); | |
211 if (linetype == BufferedMCLReader.ERROR) | |
212 throw new MerovingianException(tmpLine.substring(6)); | |
213 if (linetype != BufferedMCLReader.RESULT) | |
214 throw new MerovingianException("unexpected line: " + tmpLine); | |
215 if (!tmpLine.substring(1).equals(RESPONSE_OK)) | |
216 throw new MerovingianException(tmpLine.substring(1)); | |
217 tmpLine = min.readLine(); | |
218 linetype = min.getLineType(); | |
219 while (linetype != BufferedMCLReader.PROMPT) { | |
220 if (linetype != BufferedMCLReader.RESULT) | |
221 throw new MerovingianException("unexpected line: " + | |
222 tmpLine); | |
223 | |
224 l.add(tmpLine.substring(1)); | |
225 | |
226 tmpLine = min.readLine(); | |
227 linetype = min.getLineType(); | |
228 } | |
229 | |
230 ms.close(); | |
231 return l; | |
232 } | |
233 | |
234 public void start(String database) | |
235 throws MerovingianException, IOException | |
236 { | |
237 sendCommand(database, "start", false); | |
238 } | |
239 | |
240 public void stop(String database) | |
241 throws MerovingianException, IOException | |
242 { | |
243 sendCommand(database, "stop", false); | |
244 } | |
245 | |
246 public void kill(String database) | |
247 throws MerovingianException, IOException | |
248 { | |
249 sendCommand(database, "kill", false); | |
250 } | |
251 | |
252 public void create(String database) | |
253 throws MerovingianException, IOException | |
254 { | |
255 sendCommand(database, "create", false); | |
256 } | |
257 | |
258 public void destroy(String database) | |
259 throws MerovingianException, IOException | |
260 { | |
261 sendCommand(database, "destroy", false); | |
262 } | |
263 | |
264 public void lock(String database) | |
265 throws MerovingianException, IOException | |
266 { | |
267 sendCommand(database, "lock", false); | |
268 } | |
269 | |
270 public void release(String database) | |
271 throws MerovingianException, IOException | |
272 { | |
273 sendCommand(database, "release", false); | |
274 } | |
275 | |
276 public void rename(String database, String newname) | |
277 throws MerovingianException, IOException | |
278 { | |
279 if (newname == null) | |
280 newname = ""; /* force error from merovingian */ | |
281 | |
282 sendCommand(database, "name=" + newname, false); | |
283 } | |
284 | |
285 /** | |
286 * Sets property for database to value. If value is null, the | |
287 * property is unset, and its inherited value becomes active again. | |
288 * | |
289 * @param database the target database | |
290 * @param property the property to set value for | |
291 * @param value the value to set | |
292 * @throws MerovingianException if performing the command failed at | |
293 * the merovingian side | |
294 * @throws IOException if connecting to or communicating with | |
295 * merovingian failed | |
296 */ | |
297 public void setProperty(String database, String property, String value) | |
298 throws MerovingianException, IOException | |
299 { | |
300 /* inherit: set to empty string */ | |
301 if (value == null) | |
302 value = ""; | |
303 | |
304 sendCommand(database, property + "=" + value, false); | |
305 } | |
306 | |
307 public void inheritProperty(String database, String property) | |
308 throws MerovingianException, IOException | |
309 { | |
310 setProperty(database, property, null); | |
311 } | |
312 | |
313 public Properties getProperties(String database) | |
314 throws MerovingianException, IOException | |
315 { | |
316 Properties ret = new Properties(); | |
317 List<String> response = sendCommand(database, "get", true); | |
318 for (String responseLine : response) { | |
319 if (responseLine.startsWith("#")) | |
320 continue; | |
321 int pos = responseLine.indexOf("="); | |
322 if (pos > 0) { | |
323 ret.setProperty( | |
324 responseLine.substring(0, pos), | |
325 responseLine.substring(pos + 1, responseLine.length())); | |
326 } | |
327 } | |
328 return ret; | |
329 } | |
330 | |
331 public Properties getDefaultProperties() | |
332 throws MerovingianException, IOException | |
333 { | |
334 return(getProperties("#defaults")); | |
335 } | |
336 | |
337 public SabaothDB getStatus(String database) | |
338 throws MerovingianException, IOException | |
339 { | |
340 List<String> response = sendCommand(database, "status", true); | |
341 if (response.isEmpty()) | |
342 throw new MerovingianException("communication error"); | |
343 return new SabaothDB(response.get(0)); | |
344 } | |
345 | |
346 /** | |
347 * Test whether a specific database exists. | |
348 * | |
349 * @param database name of database | |
350 * @return true, iff database already exists. | |
351 * @throws MerovingianException if performing the command failed at | |
352 * the merovingian side | |
353 * @throws IOException if connecting to or communicating with | |
354 * merovingian failed | |
355 */ | |
356 public boolean exists(String database) | |
357 throws MerovingianException, IOException | |
358 { | |
359 List<SabaothDB> all = getAllStatuses(); | |
360 for (SabaothDB db : all) { | |
361 if (db.getName().equals(database)) { | |
362 return true; | |
363 } | |
364 } | |
365 return false; | |
366 } | |
367 | |
368 public List<SabaothDB> getAllStatuses() | |
369 throws MerovingianException, IOException | |
370 { | |
371 List<SabaothDB> l = new ArrayList<SabaothDB>(); | |
372 List<String> response = sendCommand("#all", "status", true); | |
373 try { | |
374 for (String responseLine : response) { | |
375 l.add(new SabaothDB(responseLine)); | |
376 } | |
377 } catch (IllegalArgumentException e) { | |
378 throw new MerovingianException(e.getMessage()); | |
379 } | |
380 return Collections.unmodifiableList(l); | |
381 } | |
382 | |
383 public List<URI> getAllNeighbours() | |
384 throws MerovingianException, IOException | |
385 { | |
386 List<URI> l = new ArrayList<URI>(); | |
387 List<String> response = sendCommand("anelosimus", "eximius", true); | |
388 try { | |
389 for (String responseLine : response) { | |
390 // format is <db>\t<uri> | |
391 String[] parts = responseLine.split("\t", 2); | |
392 if (parts.length != 2) | |
393 throw new MerovingianException("invalid entry: " + | |
394 responseLine); | |
395 if (parts[0].equals("*")) { | |
396 l.add(new URI(parts[1])); | |
397 } else { | |
398 l.add(new URI(parts[1] + parts[0])); | |
399 } | |
400 } | |
401 } catch (URISyntaxException e) { | |
402 throw new MerovingianException(e.getMessage()); | |
403 } | |
404 return Collections.unmodifiableList(l); | |
405 } | |
406 } |