haplo 2.5.0-java → 2.5.4-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/debuggers.rb ADDED
@@ -0,0 +1,263 @@
1
+ # Haplo Plugin Tool http://docs.haplo.org/dev/tool/plugin
2
+ # (c) Haplo Services Ltd 2006 - 2016 http://www.haplo-services.com
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+
7
+
8
+ module PluginTool
9
+
10
+ @@profile_output = nil
11
+
12
+ def self.request_profile(options)
13
+ output_file = $stdout
14
+ close_output_file = false
15
+ if options.profile_file
16
+ output_file = File.open(options.profile_file, "a")
17
+ close_output_file = true
18
+ end
19
+
20
+ formatter = nil
21
+ case options.profile_format || 'tree'
22
+ when 'tree'
23
+ @@profile_output = ProfilerFormatterTree.new(output_file)
24
+ when 'raw'
25
+ @@profile_output = ProfilerFormatterRaw.new(output_file)
26
+ else
27
+ puts "Unknown profiler format: #{options.profile_format}"
28
+ exit 1
29
+ end
30
+
31
+ if 'OK' == PluginTool.post("/api/development-plugin-loader/debugger-profile-start", {:min => options.profile})
32
+ puts "JavaScript profiler started."
33
+ else
34
+ puts "Error starting JavaScript profiler."
35
+ exit 1
36
+ end
37
+ at_exit do
38
+ puts
39
+ puts "Disabling JavaScript profiler..."
40
+ PluginTool.post("/api/development-plugin-loader/debugger-profile-stop")
41
+ puts "JavaScript profiler disabled."
42
+ output_file.close if close_output_file
43
+ end
44
+ end
45
+
46
+ class ProfilerFormatter
47
+ def initialize(output_file)
48
+ @output_file = output_file
49
+ end
50
+ def format(report)
51
+ _format(report, @output_file)
52
+ @output_file.flush
53
+ end
54
+ end
55
+
56
+ class ProfilerFormatterRaw < ProfilerFormatter
57
+ def _format(report, output_file)
58
+ output_file.write(report)
59
+ end
60
+ end
61
+
62
+ class ProfilerFormatterTree < ProfilerFormatter
63
+ def _format(report, output_file)
64
+ report.split("\n").each do |line|
65
+ depth, time, percent, count, position = line.split("\t")
66
+ if depth == 'REPORT'
67
+ output_file.write("PROFILE -- #{Time.new(time)}\n")
68
+ elsif depth == 'OMIT'
69
+ output_file.write((" "*time.to_i)+"... children omitted\n")
70
+ else
71
+ output_file.write((" "*depth.to_i)+"#{percent} #{count} #{time.to_i / 1000000} #{position}\n")
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ def self.profiler_handle_report(report)
78
+ if @@profile_output
79
+ @@profile_output.format(report)
80
+ end
81
+ end
82
+
83
+ # -------------------------------------------------------------------------
84
+
85
+ def self.request_coverage(options)
86
+
87
+ format = options.coverage_format || 'raw'
88
+ if format != 'raw'
89
+ puts "Unknown coverage format: #{format}"
90
+ exit 1
91
+ end
92
+
93
+ if 'OK' == PluginTool.post("/api/development-plugin-loader/debugger-coverage-start")
94
+ puts "Coverage capture started."
95
+ else
96
+ puts "Error starting coverage capture."
97
+ exit 1
98
+ end
99
+
100
+ at_exit do
101
+ coverage = PluginTool.post("/api/development-plugin-loader/debugger-coverage-stop")
102
+ # TODO: Check errors
103
+ File.open(options.coverage_file, "w") { |f| f.write coverage }
104
+ end
105
+
106
+ end
107
+
108
+ end
109
+
110
+ # -------------------------------------------------------------------------
111
+
112
+ module DebugAdapterProtocolTunnel
113
+
114
+ @@dap_plugins = nil
115
+ @@dap_debugger_option = nil
116
+ @@dap_server = nil
117
+ @@dap_connection = nil
118
+
119
+ def self.prepare(plugins, options)
120
+ @@dap_plugins = plugins
121
+ @@dap_debugger_option = options.debugger
122
+ raise "BAD DEBUGGER OPTION #{@@dap_debugger_option}" unless @@dap_debugger_option =~ /\A(\d+)\z/
123
+ @@dap_server = TCPServer.new("127.0.0.1", @@dap_debugger_option.to_i)
124
+ Thread.new do
125
+ while true
126
+ connection = @@dap_server.accept
127
+ if connection
128
+ if @@dap_connection
129
+ connection.close
130
+ else
131
+ @@dap_connection = DAPConnection.new(connection, @@dap_plugins)
132
+ Thread.new do
133
+ @@dap_connection.run
134
+ _stop_remote_debugger()
135
+ @@dap_connection = nil
136
+ end
137
+ end
138
+ end
139
+ end
140
+ rescue => e
141
+ # ignore
142
+ end
143
+ at_exit do
144
+ @@dap_server.close
145
+ @@dap_connection.close if @@dap_connection
146
+ _stop_remote_debugger()
147
+ end
148
+ end
149
+
150
+ def self._stop_remote_debugger
151
+ puts "DEBUGGER: Stopping remote debugger..."
152
+ result = PluginTool.post("/api/development-plugin-loader/debugger-dap-stop")
153
+ if result == 'OK'
154
+ puts "DEBUGGER: Remote debugger stopped."
155
+ else
156
+ puts "DEBUGGER: Error stopping remote debugger, application server may be in non-functioning state."
157
+ end
158
+ end
159
+
160
+ def self.log_message_from_server(text)
161
+ if @@dap_connection
162
+ @@dap_connection._write({
163
+ 'type' => 'event',
164
+ 'event' => 'output',
165
+ 'body' => {
166
+ 'category' => 'console',
167
+ 'output' => text+"\n"
168
+ }
169
+ })
170
+ end
171
+ end
172
+
173
+ def self.dap_message_from_server(json)
174
+ if @@dap_connection
175
+ @@dap_connection._write(JSON.parse(json))
176
+ end
177
+ end
178
+
179
+ class DAPConnection
180
+ def initialize(connection, plugins)
181
+ @connection = connection
182
+ @plugins = plugins
183
+ @running = false
184
+ @next_seq = 1
185
+ @write_mutex = Mutex.new
186
+ @_dump_messages = (ENV['HAPLO_DAP_DEBUG'] == '1')
187
+ end
188
+
189
+ def close
190
+ begin
191
+ @connection.close
192
+ rescue => e
193
+ # ignore any errors
194
+ end
195
+ end
196
+
197
+ def run
198
+ begin
199
+ run2
200
+ rescue => e
201
+ puts "DEBUGGER: Local connection closed"
202
+ end
203
+ self.close
204
+ end
205
+
206
+ def run2
207
+ @running = true
208
+ have_initialized = false
209
+ while @running
210
+ header = @connection.readline
211
+ blank = @connection.readline
212
+ if header =~ /\Acontent-length:\s+(\d+)\r\n\z/i && blank == "\r\n"
213
+ body = @connection.read($1.to_i)
214
+ message = JSON.parse(body)
215
+ puts "DAP READ: #{JSON.pretty_generate(message)}\n" if @_dump_messages
216
+ unless have_initialized
217
+ if message['type'] == 'request' && message['command'] == 'initialize'
218
+ puts "DEBUGGER: Local connection from #{message['arguments']['clientName']} (#{message['arguments']['clientID']})\nDEBUGGER: Starting remote debugger..."
219
+ plugin_locations = {}
220
+ @plugins.each { |p| plugin_locations[p.name] = p.plugin_dir }
221
+ start_response = PluginTool.post("/api/development-plugin-loader/debugger-dap-start", {
222
+ :plugin_locations => JSON.generate(plugin_locations)
223
+ })
224
+ unless start_response =~ /\ATOKEN: (.+)\z/
225
+ raise "Remote debugger failed to start"
226
+ end
227
+ @token = $1
228
+ puts "DEBUGGER: Remote debugger started."
229
+ have_initialized = true
230
+ else
231
+ raise "Expected initialize message after DAP connection"
232
+ end
233
+ end
234
+ # TODO: Send messages in another thread, so that they can be batched together
235
+ message_response = PluginTool.post_with_json_response("/api/development-plugin-loader/debugger-dap-messages", {
236
+ :token => @token,
237
+ :messages => JSON.generate([message])
238
+ })
239
+ if message_response['error']
240
+ puts "DEBUGGER: Server responded with error: #{message_response['error']}"
241
+ else
242
+ message_response['messages'].each do |response|
243
+ _write(response) if response
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ def _write(message)
251
+ @write_mutex.synchronize do
252
+ m = {'seq' => @next_seq}
253
+ @next_seq += 1
254
+ m.merge!(message)
255
+ puts "DAP WRITE: #{JSON.pretty_generate(m)}\n" if @_dump_messages
256
+ msg_json = JSON.generate(m)
257
+ @connection.write("Content-Length: #{msg_json.bytesize}\r\n\r\n")
258
+ @connection.write(msg_json)
259
+ end
260
+ end
261
+ end
262
+
263
+ end
data/lib/notifications.rb CHANGED
@@ -69,6 +69,12 @@ module PluginTool
69
69
  when 'log '
70
70
  # Output from console.log()
71
71
  puts "LOG:#{data}"
72
+ DebugAdapterProtocolTunnel.log_message_from_server(data)
73
+ when 'DAP1'
74
+ DebugAdapterProtocolTunnel.dap_message_from_server(data)
75
+ when 'prof'
76
+ # Profiler report
77
+ PluginTool.profiler_handle_report(data)
72
78
  when 'audt'
73
79
  decoded = JSON.parse(data)
74
80
  kind = decoded.find { |name,value| name == 'auditEntryType' }.last
data/lib/packing.rb CHANGED
@@ -50,7 +50,10 @@ module PluginTool
50
50
  end
51
51
  # Minimise file?
52
52
  unless filename =~ /\A(js|test)\//
53
- data = minimiser.process(data, filename)
53
+ # Is this file explicitly excluded from minimisation?
54
+ unless plugin.exclude_files_from_minimisation.include?(filename)
55
+ data = minimiser.process(data, filename)
56
+ end
54
57
  end
55
58
  hash = Digest::SHA256.hexdigest(data)
56
59
  # Make sure output directory exists, write file
data/lib/plugin.rb CHANGED
@@ -102,6 +102,17 @@ module PluginTool
102
102
  end
103
103
  end
104
104
 
105
+ def exclude_files_from_minimisation
106
+ @exclude_files_from_minimisation ||= begin
107
+ # developer.json file might specify files which should skip minimisation when packing
108
+ if developer_json['excludeFromMinimisation'].kind_of?(Array)
109
+ developer_json['excludeFromMinimisation']
110
+ else
111
+ []
112
+ end
113
+ end
114
+ end
115
+
105
116
  # ---------------------------------------------------------------------------------------------------------
106
117
 
107
118
  def command(cmd, errors)
@@ -180,11 +191,16 @@ module PluginTool
180
191
  hash = action
181
192
  # Minimise file before uploading?
182
193
  if @options.minimiser != nil && filename =~ /\A(static|template)\//
183
- size_before = data.length
184
- data = @options.minimiser.process(data, filename)
185
- size_after = data.length
186
- hash = Digest::SHA256.hexdigest(data)
187
- puts " minimisation: #{size_before} -> #{size_after} (#{(size_after * 100) / size_before}%)"
194
+ # Is this file explicitly excluded from minimisation?
195
+ unless exclude_files_from_minimisation.include?(filename)
196
+ size_before = data.length
197
+ data = @options.minimiser.process(data, filename)
198
+ size_after = data.length
199
+ hash = Digest::SHA256.hexdigest(data)
200
+ puts " minimisation: #{size_before} -> #{size_after} (#{(size_after * 100) / size_before}%)"
201
+ else
202
+ puts " minimisation: skipped by developer.json, unmodified file uploaded"
203
+ end
188
204
  end
189
205
  r = PluginTool.post_with_json_response("/api/development-plugin-loader/put-file/#{@loaded_plugin_id}", params, {:file => [filename, data]})
190
206
  if r["result"] == 'success'
data/lib/plugin_tool.rb CHANGED
@@ -23,7 +23,7 @@ NO_DEPENDENCY_COMMANDS.delete('list')
23
23
  PLUGIN_SEARCH_PATH = ['.']
24
24
 
25
25
  # Options for passing to plugin objects
26
- options = Struct.new(:output, :minimiser, :no_dependency, :with_dependency, :exclude_with_prefix, :no_console, :show_system_audit, :args, :force, :turbo, :server_substring, :restrict_to_app_id).new
26
+ options = Struct.new(:output, :minimiser, :no_dependency, :with_dependency, :exclude_with_prefix, :no_console, :show_system_audit, :args, :force, :turbo, :debugger, :profile, :profile_file, :profile_format, :coverage_file, :coverage_format, :server_substring, :restrict_to_app_id).new
27
27
 
28
28
  # Parse arguments
29
29
  show_help = false
@@ -37,6 +37,12 @@ opts = GetoptLong.new(
37
37
  ['--server', '-s', GetoptLong::REQUIRED_ARGUMENT],
38
38
  ['--force', GetoptLong::NO_ARGUMENT],
39
39
  ['--turbo', GetoptLong::NO_ARGUMENT],
40
+ ['--debugger', GetoptLong::REQUIRED_ARGUMENT],
41
+ ['--profile', GetoptLong::REQUIRED_ARGUMENT],
42
+ ['--profile-file', GetoptLong::REQUIRED_ARGUMENT],
43
+ ['--profile-format', GetoptLong::REQUIRED_ARGUMENT],
44
+ ['--coverage-file', GetoptLong::REQUIRED_ARGUMENT],
45
+ ['--coverage-format', GetoptLong::REQUIRED_ARGUMENT],
40
46
  ['--output', GetoptLong::REQUIRED_ARGUMENT],
41
47
  ['--pack-restrict-to-app-id', GetoptLong::REQUIRED_ARGUMENT],
42
48
  ['--no-console', '-n', GetoptLong::NO_ARGUMENT],
@@ -72,6 +78,18 @@ opts.each do |opt, argument|
72
78
  options.force = true
73
79
  when '--turbo'
74
80
  options.turbo = true
81
+ when '--debugger'
82
+ options.debugger = argument
83
+ when '--profile'
84
+ options.profile = argument.to_f
85
+ when '--profile-file'
86
+ options.profile_file = argument
87
+ when '--profile-format'
88
+ options.profile_format = argument
89
+ when '--coverage-file'
90
+ options.coverage_file = argument
91
+ when '--coverage-format'
92
+ options.coverage_format = argument
75
93
  end
76
94
  end
77
95
  # Handle rest of command line -- first arg is the command, the rest are passed on
@@ -325,6 +343,13 @@ end
325
343
  unless LOCAL_ONLY_COMMANDS[PLUGIN_TOOL_COMMAND]
326
344
  PluginTool.custom_behaviour.server_ready(plugins, PLUGIN_TOOL_COMMAND, options)
327
345
  plugins.each { |p| p.setup_for_server }
346
+
347
+ if options.profile
348
+ PluginTool.request_profile(options)
349
+ end
350
+ if options.coverage_file
351
+ PluginTool.request_coverage(options)
352
+ end
328
353
  end
329
354
 
330
355
  # Run the command
@@ -343,6 +368,11 @@ PluginTool.start_syntax_check
343
368
  # Notifications support (including console)
344
369
  PluginTool.start_notifications(options) unless options.no_console
345
370
 
371
+ # If the debugger is requested, open the listening socket. Debugger initialised on connection.
372
+ if options.debugger
373
+ DebugAdapterProtocolTunnel.prepare(plugins, options)
374
+ end
375
+
346
376
  # Open watcher
347
377
  watcher = PluginTool.make_watcher(plugins.map { |p| p.plugin_dir })
348
378
 
data/lib/run.rb CHANGED
@@ -65,6 +65,7 @@ require "#{PLUGIN_TOOL_ROOT_DIR}/lib/syntax_checking.rb"
65
65
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/notifications.rb"
66
66
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/plugin.rb"
67
67
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/new_plugin.rb"
68
+ require "#{PLUGIN_TOOL_ROOT_DIR}/lib/debuggers.rb"
68
69
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/misc.rb"
69
70
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/watchers.rb"
70
71
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/minimise.rb"
data/lib/server.rb CHANGED
@@ -50,6 +50,7 @@ module PluginTool
50
50
  http.use_ssl = true
51
51
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
52
52
  http.cert_store = ssl_ca
53
+ http.read_timeout = 3600 # 1 hour, in case a test runs for a very long time
53
54
  http.start
54
55
  http
55
56
  end
data/lib/version.txt CHANGED
@@ -1 +1 @@
1
- 861823a
1
+ ccc8c26
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haplo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 2.5.4
5
5
  platform: java
6
6
  authors:
7
7
  - Haplo Services
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-25 00:00:00.000000000 Z
11
+ date: 2021-07-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Development tools for developing Haplo plugins, see https://haplo.org
14
14
  email: client.services@haplo-services.com
@@ -24,6 +24,7 @@ files:
24
24
  - lib/auth.rb
25
25
  - lib/check.rb
26
26
  - lib/custom.rb
27
+ - lib/debuggers.rb
27
28
  - lib/haplo-templates.jar
28
29
  - lib/hmac.rb
29
30
  - lib/hsvt_parser_config.rb
@@ -70,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
71
  version: '0'
71
72
  requirements: []
72
73
  rubyforge_project:
73
- rubygems_version: 2.7.6
74
+ rubygems_version: 2.7.9
74
75
  signing_key:
75
76
  specification_version: 4
76
77
  summary: Haplo Plugin Tool