Skip to content

Commit e5a9499

Browse files
committed
Jython: Use Java HTTP API
1 parent 55a453d commit e5a9499

File tree

1 file changed

+93
-74
lines changed

1 file changed

+93
-74
lines changed

scan/client/scanclient.py

Lines changed: 93 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
'''
2-
Copyright (c) 2014
2+
Copyright (c) 2014-2018
33
All rights reserved. Use is subject to license terms and conditions.
4-
Created on Dec 30, 2014
5-
Updated on Mar 19,2015
64
@author: Yongxiang Qiu, Kay Kasemir
75
'''
86

97
import time
108
import xml.etree.ElementTree as ET
119
import urllib
12-
import urllib2
1310
from scan.client.logdata import parseXMLData
1411
from scan.commands.commandsequence import CommandSequence
1512
from scaninfo import ScanInfo
1613

17-
18-
# Bug https://github.com/PythonScanClient/PyScanClient/issues/18
14+
# Python code uses urllib2.
15+
# When accessed from jython, there were two problems.
1916
#
17+
# 1) https://github.com/PythonScanClient/PyScanClient/issues/18
2018
# urllib2 is based on _socket.py.
2119
# Depending on how BOY invokes jython,
2220
# even a new threadLocalStateInterpreter and a newly compiled
@@ -25,61 +23,54 @@
2523
# its threadLocalStateInterpreter.
2624
# Calls to urllib2 will then receive a connection error based on
2725
# "RejectedExecutionException: event executor terminated".
26+
# Workaround was to re-create the _socket.NIO_GROUP
27+
# and call sys.registerCloser(_socket._shutdown_threadpool)
28+
#
29+
# 2) https://github.com/ControlSystemStudio/cs-studio/issues/2535
30+
# After updating to jython 2.7.1, the HTTP 'POST' appeared limited
31+
# to sending 65k of data.
32+
#
33+
# Workaround for both is to use Java HTTP API for jython
2834
import os
2935
if os.name == 'java':
30-
import sys, _socket
31-
# Log detail of the _socket code
32-
#import logging
33-
#logging.basicConfig(level=logging.DEBUG)
34-
#log = logging.getLogger("_socket")
35-
#log.setLevel(level=logging.DEBUG)
36-
37-
def checkSocketLib():
38-
# Workaround: Detect closed NIO_GROUP and ee-create it
39-
try:
40-
if _socket.NIO_GROUP.isShutdown():
41-
# print "RE-CREATEING NIO_GROUP!!!!!!!!!"
42-
# _socket._NUM_THREADS is 10. Using only 2 threads.
43-
_socket.NIO_GROUP = _socket.NioEventLoopGroup(2, _socket.DaemonThreadFactory("PyScan-Netty-Client-%s"))
44-
sys.registerCloser(_socket._shutdown_threadpool)
45-
except AttributeError:
46-
print "Jython _socket.py has changed from jython_2.7.0"
47-
else:
48-
def checkSocketLib():
49-
# C-Python _socket.py needs no fix
50-
pass
36+
import java.lang
37+
from java.io import BufferedReader, InputStreamReader, OutputStream
38+
from java.net import HttpURLConnection, URL
5139

52-
53-
class ScanClient(object):
54-
"""Client interface to the scan server
55-
56-
:param host: The IP address or name of scan server host.
57-
:param port: The TCP port of the scan server.
58-
59-
Example:
40+
def perform_request(url, method='GET', data=None):
41+
try:
42+
connection = URL(url).openConnection()
43+
connection.setRequestProperty("Connection", "close")
44+
connection.setRequestProperty("User-Agent", "PyScanClient")
45+
connection.setRequestProperty("Accept", "text/xml")
46+
connection.setDoOutput(True)
47+
connection.setRequestMethod(method)
48+
if data is not None:
49+
data = java.lang.String(data).getBytes()
50+
connection.setRequestProperty("Content-Type", "text/xml")
51+
connection.setRequestProperty("Content-Length", str(len(data)))
52+
out = connection.getOutputStream()
53+
out.write(data)
54+
out.close()
6055

61-
>>> client = ScanClient('localhost')
62-
"""
63-
__baseURL = None
64-
__serverResource = "/server"
65-
__serverInfoResource = "/info"
66-
__simulateResource = "/simulate"
67-
__scansResource = "/scans"
68-
__scansCompletedResource = "/completed"
69-
__scanResource = "/scan"
70-
71-
def __init__(self, host='localhost', port=4810):
72-
self.__host = host
73-
self.__port = int(port) #no matter what type of 'port' input, self._port keeps to be int.
74-
#May implement a one to one host+port with instance in the future.
75-
self.__baseURL = "http://"+self.__host+':'+str(self.__port)
56+
inp = BufferedReader(InputStreamReader(connection.getInputStream()))
57+
result = java.lang.StringBuilder()
58+
while True:
59+
line = inp.readLine()
60+
if line is None:
61+
break
62+
result.append(line).append('\n')
63+
inp.close()
64+
result = result.toString()
65+
connection.disconnect()
66+
return result
67+
except java.lang.Exception as e:
68+
raise Exception("%s: %s" % (url, str(e)))
7669

77-
78-
def __repr__(self):
79-
return "ScanClient('%s', %d)" % (self.__host, self.__port)
70+
else:
71+
import urllib2
8072

81-
82-
def __do_request(self, url, method='GET', data=None):
73+
def perform_request(url, method='GET', data=None):
8374
"""Perform HTTP request with scan server
8475
8576
:param url: URL
@@ -88,7 +79,6 @@ def __do_request(self, url, method='GET', data=None):
8879
8980
:return: XML response from scan server
9081
"""
91-
checkSocketLib()
9282
response = None
9383
try:
9484
# Register a Request Object with url:
@@ -118,14 +108,43 @@ def __do_request(self, url, method='GET', data=None):
118108
return response.read()
119109
except urllib2.URLError as e:
120110
if hasattr(e, 'reason'):
121-
raise Exception("Failed to reach scan server at %s:%d: %s" % (self.__host, self.__port, e.reason))
111+
raise Exception("Failed to reach scan server at %s: %s" % (url, e.reason))
122112
elif hasattr(e, 'code'):
123-
raise Exception("Scan server at %s:%d returned error code %d" % (self.__host, self.__port, e.code))
113+
raise Exception("Scan server at %s returned error code %d" % (url, e.code))
124114
finally:
125115
if response:
126116
response.close()
127117

128-
118+
119+
class ScanClient(object):
120+
"""Client interface to the scan server
121+
122+
:param host: The IP address or name of scan server host.
123+
:param port: The TCP port of the scan server.
124+
125+
Example:
126+
127+
>>> client = ScanClient('localhost')
128+
"""
129+
__baseURL = None
130+
__serverResource = "/server"
131+
__serverInfoResource = "/info"
132+
__simulateResource = "/simulate"
133+
__scansResource = "/scans"
134+
__scansCompletedResource = "/completed"
135+
__scanResource = "/scan"
136+
137+
def __init__(self, host='localhost', port=4810):
138+
self.__host = host
139+
self.__port = int(port) #no matter what type of 'port' input, self._port keeps to be int.
140+
#May implement a one to one host+port with instance in the future.
141+
self.__baseURL = "http://"+self.__host+':'+str(self.__port)
142+
143+
144+
def __repr__(self):
145+
return "ScanClient('%s', %d)" % (self.__host, self.__port)
146+
147+
129148
def serverInfo(self):
130149
"""Get scan server information
131150
@@ -140,7 +159,7 @@ def serverInfo(self):
140159
>>> client = ScanClient()
141160
>>> print client.serverInfo()
142161
"""
143-
return self.__do_request(self.__baseURL + self.__serverResource + self.__serverInfoResource)
162+
return perform_request(self.__baseURL + self.__serverResource + self.__serverInfoResource)
144163

145164

146165
def simulate(self, cmds):
@@ -167,7 +186,7 @@ def simulate(self, cmds):
167186

168187
url = self.__baseURL + self.__simulateResource
169188

170-
result = self.__do_request(url, 'POST', scan)
189+
result = perform_request(url, 'POST', scan)
171190
xml = ET.fromstring(result)
172191
if xml.tag != 'simulation':
173192
raise Exception("Expected scan <simulation>, got <%s>" % xml.tag)
@@ -232,7 +251,7 @@ def __submitScanXML(self, scanXML, scanName, queue=True):
232251
url = self.__baseURL + self.__scanResource + '/' + scanName
233252
if not queue:
234253
url = url + "?queue=false"
235-
r = self.__do_request(url, 'POST', scanXML)
254+
r = perform_request(url, 'POST', scanXML)
236255
return r
237256

238257

@@ -260,7 +279,7 @@ def scanInfos(self):
260279
>>> infos = client.scanInfos()
261280
>>> print [ str(info) for info in infos ]
262281
"""
263-
xml = self.__do_request(self.__baseURL + self.__scansResource)
282+
xml = perform_request(self.__baseURL + self.__scansResource)
264283
scans = ET.fromstring(xml)
265284
result = list()
266285
for scan in scans.findall('scan'):
@@ -281,7 +300,7 @@ def scanInfo(self, scanID):
281300
>>> client = ScanClient()
282301
>>> print client.scanInfo(42)
283302
"""
284-
xml = self.__do_request(self.__baseURL + self.__scanResource + '/' + str(scanID))
303+
xml = perform_request(self.__baseURL + self.__scanResource + '/' + str(scanID))
285304
return ScanInfo(ET.fromstring(xml))
286305

287306

@@ -305,7 +324,7 @@ def scanCmds(self, scanID):
305324
>>> client.submit(client.scanCmds(scanid))
306325
"""
307326
url = self.__baseURL + self.__scanResource + '/' + str(scanID)+'/commands'
308-
xml = self.__do_request(url)
327+
xml = perform_request(url)
309328
return xml
310329

311330

@@ -332,7 +351,7 @@ def lastSerial(self, scanID):
332351
>>> time.sleep(10
333352
"""
334353
url = self.__baseURL + self.__scanResource + '/' + str(scanID)+'/last_serial'
335-
xml = self.__do_request(url)
354+
xml = perform_request(url)
336355
ET.fromstring(xml)
337356
return int(ET.fromstring(xml).text)
338357

@@ -352,7 +371,7 @@ def scanDevices(self, scanID=-1):
352371
:return: XML with info about devices.
353372
"""
354373
url = self.__baseURL + self.__scanResource + '/' + str(scanID)+'/devices'
355-
xml = self.__do_request(url)
374+
xml = perform_request(url)
356375
return xml
357376

358377

@@ -393,7 +412,7 @@ def pause(self, scanID=-1):
393412
>>> client.pause(id)
394413
"""
395414
url = self.__baseURL + self.__scanResource + '/' + str(scanID) + '/pause'
396-
self.__do_request(url, 'PUT')
415+
perform_request(url, 'PUT')
397416

398417

399418
def resume(self, scanID=-1):
@@ -410,7 +429,7 @@ def resume(self, scanID=-1):
410429
>>> client.resume(id)
411430
"""
412431
url=self.__baseURL + self.__scanResource + '/' + str(scanID) + '/resume'
413-
self.__do_request(url, 'PUT')
432+
perform_request(url, 'PUT')
414433

415434

416435
def abort(self, scanID=-1):
@@ -426,7 +445,7 @@ def abort(self, scanID=-1):
426445
>>> client.abort(id)
427446
"""
428447
url = self.__baseURL + self.__scanResource + '/' + str(scanID) + '/abort'
429-
self.__do_request(url, 'PUT')
448+
perform_request(url, 'PUT')
430449

431450

432451
def delete(self, scanID):
@@ -442,7 +461,7 @@ def delete(self, scanID):
442461
>>> client.abort(id)
443462
>>> client.delete(id)
444463
"""
445-
self.__do_request(self.__baseURL + self.__scanResource + '/' + str(scanID), 'DELETE')
464+
perform_request(self.__baseURL + self.__scanResource + '/' + str(scanID), 'DELETE')
446465

447466

448467
def clear(self):
@@ -454,7 +473,7 @@ def clear(self):
454473
455474
>>> client.clear()
456475
"""
457-
self.__do_request(self.__baseURL + self.__scansResource + self.__scansCompletedResource, 'DELETE')
476+
perform_request(self.__baseURL + self.__scansResource + self.__scansCompletedResource, 'DELETE')
458477

459478
def patch(self, scanID, address, property, value): # @ReservedAssignment
460479
"""Update scan on server.
@@ -482,7 +501,7 @@ def patch(self, scanID, address, property, value): # @ReservedAssignment
482501
>>> client.resume(id)
483502
"""
484503
xml = "<patch><address>%d</address><property>%s</property><value>%s</value></patch>" % (address, property, str(value))
485-
self.__do_request(self.__baseURL + self.__scanResource + '/' + str(id) + '/patch', 'PUT', xml)
504+
perform_request(self.__baseURL + self.__scanResource + '/' + str(id) + '/patch', 'PUT', xml)
486505

487506

488507
def getData(self, scanID):
@@ -519,6 +538,6 @@ def getData(self, scanID):
519538
Times in Posix milliseconds
520539
"""
521540
url = self.__baseURL + self.__scanResource + '/' + str(scanID)+'/data'
522-
xml = self.__do_request(url)
541+
xml = perform_request(url)
523542
return parseXMLData(xml)
524543

0 commit comments

Comments
 (0)