Module: Asciidoctor::Diagram::Java

Defined in:
lib/asciidoctor-diagram/util/java.rb,
lib/asciidoctor-diagram/util/java_jruby.rb,
lib/asciidoctor-diagram/util/java_socket.rb

Defined Under Namespace

Classes: CommandServer

Constant Summary collapse

CRLF =
"\r\n".encode(Encoding::US_ASCII)
STATUS_LINE =
Regexp.new("HTTP/1.1 (\\d+) (.*)\r\n".encode(Encoding::US_ASCII))
JDK_KEY =
'HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit'
JRE_KEY =
'HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment'

Class Method Summary collapse

Class Method Details

.classpathObject



7
8
9
# File 'lib/asciidoctor-diagram/util/java.rb', line 7

def self.classpath
  @classpath ||= Dir[File.join(File.dirname(__FILE__), '*.jar')]
end

.create_error(prefix_msg, response) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/asciidoctor-diagram/util/java.rb', line 87

def self.create_error(prefix_msg, response)
  content_type = response[:headers]['content-type'] || 'text/plain'
  if content_type.start_with? 'application/json'
    json = JSON.parse(response[:body].force_encoding(Encoding::UTF_8))
    ruby_bt = Kernel.caller(2)
    java_bt = json['stk'].map { |java_line| "#{java_line[0]}:#{java_line[3]}: in '#{java_line[2]}'" }
    error = RuntimeError.new("#{prefix_msg}: #{json['msg']}")
    error.set_backtrace java_bt + ruby_bt
    raise error
  elsif content_type.start_with? 'text/plain'
    raise "#{prefix_msg}: #{response[:reason]} #{response[:body].force_encoding(Encoding::UTF_8)}"
  else
    raise "#{prefix_msg}: #{response[:reason]}"
  end
end

.environment_variable(key) ⇒ Object



8
9
10
# File 'lib/asciidoctor-diagram/util/java_jruby.rb', line 8

def self.environment_variable(key)
  ENV[key] || ENV_JAVA[key.downcase.gsub('_', '.')]
end

.find_javaObject



109
110
111
112
113
114
115
116
117
118
# File 'lib/asciidoctor-diagram/util/java.rb', line 109

def self.find_java
  case ::Asciidoctor::Diagram::Platform.os
    when :windows
      path_to(ENV['JAVA_HOME'], 'bin/java.exe') || registry_lookup || ::Asciidoctor::Diagram::Which.which('java')
    when :macosx
      path_to(ENV['JAVA_HOME'], 'bin/java') || path_to(::Asciidoctor::Diagram::Cli.run('/usr/libexec/java_home')[:out].strip, 'bin/java') || ::Asciidoctor::Diagram::Which.which('java')
    else
      path_to(ENV['JAVA_HOME'], 'bin/java') || ::Asciidoctor::Diagram::Which.which('java')
  end
end

.format_request(req, io) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/asciidoctor-diagram/util/java.rb', line 13

def self.format_request(req, io)
  io.set_encoding Encoding::US_ASCII
  io.write "POST #{req[:url]} HTTP/1.1"
  io.write CRLF

  headers = req[:headers]
  if headers
    headers.each_pair do |key, value|
      io.write "#{key}: #{value}"
      io.write CRLF
    end
  end

  if req[:body]
    unless headers && headers['Content-Length']
      io.write 'Content-Length: '
      io.write req[:body].bytesize.to_s
      io.write CRLF
    end

    unless headers && headers['Content-Type']
      io.write 'Content-Type: text/plain; charset='
      io.write req[:body].encoding.name
      io.write CRLF
    end
  end

  io.write CRLF

  io.set_encoding Encoding::BINARY
  io.write req[:body]
end

.instanceObject



52
53
54
55
56
57
58
59
60
61
62
# File 'lib/asciidoctor-diagram/util/java_socket.rb', line 52

def self.instance
  unless defined?(@command_server) && @command_server
    server = CommandServer.new(java, classpath)
    @command_server = server
    at_exit do
      server.shutdown
    end
  end

  @command_server
end

.javaObject



103
104
105
106
107
# File 'lib/asciidoctor-diagram/util/java.rb', line 103

def self.java
  @java_exe ||= find_java
  raise "Could not find Java executable" unless @java_exe
  @java_exe
end

.java_exe(java_home) ⇒ Object



151
152
153
154
155
156
157
158
159
# File 'lib/asciidoctor-diagram/util/java.rb', line 151

def self.java_exe(java_home)
  java = File.expand_path('bin/java.exe', java_home)

  if File.executable?(java)
    java
  else
    nil
  end
end

.loadObject



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/asciidoctor-diagram/util/java_jruby.rb', line 12

def self.load
  if defined?(@loaded) && @loaded
    return
  end

  classpath.flatten.each do |j|
    raise "Classpath item #{j} does not exist" unless File.exist?(j)
    require j
  end

  # Strange issue seen with JRuby where 'java.class.path' has the value ':'.
  # This causes issue in PlantUML which splits the class path string and then
  # raises errors when trying to handle empty strings.
  java_cp = ::Java.java.lang.System.getProperty("java.class.path")
  new_java_cp = java_cp.split(File::PATH_SEPARATOR)
                       .reject { |p| p.empty? }
                       .join(File::PATH_SEPARATOR)
  ::Java.java.lang.System.setProperty("java.class.path", new_java_cp)

  @loaded = true
end

.parse_body(io) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/asciidoctor-diagram/util/java.rb', line 66

def self.parse_body(io)
  body = {}

  io.set_encoding Encoding::US_ASCII
  headers = {}
  until (header = io.readline(CRLF).strip).empty?
    key, value = header.split ':', 2
    headers[key.downcase] = value.strip
  end

  body[:headers] = headers

  content_length = headers['content-length']
  if content_length
    io.set_encoding Encoding::BINARY
    body[:body] = io.read(content_length.to_i)
  end

  body
end

.parse_response(io) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/asciidoctor-diagram/util/java.rb', line 48

def self.parse_response(io)
  resp = {}

  io.set_encoding Encoding::US_ASCII
  status_line = io.readline(CRLF)
  status_line_parts = STATUS_LINE.match status_line
  unless status_line_parts
    raise "Unexpected HTTP status line: #{status_line}"
  end

  resp[:code] = status_line_parts[1].to_i
  resp[:reason] = status_line_parts[2]

  resp.merge! parse_body(io)

  resp
end

.path_to(java_home, java_binary) ⇒ Object



120
121
122
123
124
125
126
127
# File 'lib/asciidoctor-diagram/util/java.rb', line 120

def self.path_to(java_home, java_binary)
  exe_path = File.expand_path(java_binary, java_home)
  if File.executable?(exe_path)
    exe_path
  else
    nil
  end
end

.registry_anyObject



146
147
148
149
# File 'lib/asciidoctor-diagram/util/java.rb', line 146

def self.registry_any()
  java_homes = registry_query('HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft', 'JavaHome', :recursive => true).values
  java_homes.map { |path| java_exe(path) }.find { |exe| !exe.nil? }
end

.registry_current(key) ⇒ Object



136
137
138
139
140
141
142
143
144
# File 'lib/asciidoctor-diagram/util/java.rb', line 136

def self.registry_current(key)
  current_version = registry_query(key, 'CurrentVersion')
  if current_version
    java_home = registry_query("#{key}\\#{current_version}", 'JavaHome')
    java_exe(java_home)
  else
    nil
  end
end

.registry_lookupObject



132
133
134
# File 'lib/asciidoctor-diagram/util/java.rb', line 132

def self.registry_lookup
  registry_current(JRE_KEY) || registry_current(JDK_KEY) || registry_any()
end

.registry_query(key, value = nil, opts = {}) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/asciidoctor-diagram/util/java.rb', line 161

def self.registry_query(key, value = nil, opts = {})
  args = ['reg', 'query']
  args << key
  args << '/v' << value unless value.nil?
  args << '/s' if opts[:recursive]

  begin
    lines = ::Asciidoctor::Diagram::Cli.run(*args)[:out].lines.reject { |l| l.strip.empty? }.each
  rescue
    lines = [].each
  end

  result = {}

  while true
    begin
      begin
        k = lines.next
      rescue StopIteration
        break
      end

      unless k.start_with? key
        next
      end

      v = nil
      begin
        v = lines.next.strip if lines.peek.start_with?(' ')
      rescue StopIteration
        break
      end

      if !k.valid_encoding? || (v && !v.valid_encoding?)
        next
      end

      if v && (md = /([^\s]+)\s+(REG_[^\s]+)\s+(.+)/.match(v))
        v_name = md[1]
        v_value = md[3]
        result["#{k}\\#{v_name}"] = v_value
      else
        result[k] = v
      end
    end
  end

  if value && !opts[:recursive]
    result.values[0]
  else
    result
  end
end

.send_request(req) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/asciidoctor-diagram/util/java_jruby.rb', line 34

def self.send_request(req)
  cp = ::Java.org.asciidoctor.diagram.CommandProcessor.new()

  req_io = StringIO.new
  format_request(req, req_io)
  req_io.close

  response = cp.processRequest(req_io.string.to_java_bytes)

  resp_io = StringIO.new(String.from_java_bytes(response))
  resp = parse_response(resp_io)
  resp_io.close

  resp
end