Class: Universa::UMI

Inherits:
Object
  • Object
show all
Defined in:
lib/universa/umi.rb

Overview

Universa Method Invocation remote interface.

By default, it creates UMI interface to the included UMI server which gives almost full access to the Universa Java API:

Uasge:

>> umi = Universa::UMI.new()
>> # create a new key and new contract with this key as creator:
>> contract = umi.instantiate "Contract", umi.instantiate("PrivateKey", 2048)

Use #instantiate to create new instances of remote classes, which return Ref instances, and just call their methods as if these are usual ruby methods. For example in the example above:

address = contract.getKeysToSignWith()[0].getPublicKey().getShortAddress().toString()

In the sample above all the methods are called on the remote side, returning links to remote objects which are all Ref instances, and the last ‘toString()` call return a string, which is converted to ruby string and saved into variable. This sentence, therefore, get the first signer key and transofrms it to the string short address.

Having several ‘UMI` interfaces.

It is possible to have several UMI instances, by default, it will create separate process with isolated data space, which is perfectly safe to use in various scenarios.

It still means the object from different interfaces can’t be interchanged. Ref instances created by one interface should be used with this interface only, or the InterchangeError will be raised.

Remote exceptions

If remote part will thow an Exception performing a method, it will be raised as an instance of Farcall::RemoteError class which carries remote exception information.

Transport level

UMI uses Farcall transport in woth JSON adapter and “n” as separator.

Constant Summary collapse

@@session_log_path =
nil

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path = nil, version_check: /./, system: "UMI", log: nil, convert_case: true, factory: nil) ⇒ UMI

Create UMI instance. It starts the private child process wit UMI server and securely connects to it so no other connection could occur.

# create UNI interface
umi = Universa::UMI.new()
# create a new key and new contract with this key as creator:
contract = umi.instantiate "Contract", umi.instantiate("PrivateKey", 2048)
contract.seal()  # binary packed string returned
contract.check() #=> true

Parameters:

  • path (String) (defaults to: nil)

    to custom UMI server build. Use bundled one (leave as nil)

  • version_check (Regexp) (defaults to: /./)

    check version against

  • system (String) (defaults to: "UMI")

    expected on the remote side. ‘UMI’ us a universa umi server.

  • convert_case (Boolean) (defaults to: true)

    it true, convert ruby style snake case ‘get_some_stuff()` to java style lower camel case `getSomeStuff()` while calling methods. Does not affect class names on #instantiate.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/universa/umi.rb', line 76

def initialize(path = nil, version_check: /./, system: "UMI", log: nil, convert_case: true, factory: nil)
  log ||= @@session_log_path
  path ||= File.expand_path(File.split(__FILE__)[0] + "/../../bin/umi/bin/umi")
  @in, @out, @err, @wtr = Open3.popen3("#{path} #{log ? "-log #{log}" : ''}")
  @endpoint = Farcall::Endpoint.new(
      Farcall::JsonTransport.create(delimiter: "\n", input: @out, output: @in)
  )
  @lock = Monitor.new
  @cache = {}
  @closed = false
  @convert_case, @factory = convert_case, factory
  @references = {}
  start_cleanup_queue
  @version = call("version")
  raise Error, "Unsupported system: #{@version}" if @version.system != "UMI"
  raise Error, "Unsupported version: #{@version}" if @version.version !~ /0\.8\.\d+/
rescue Errno::ENOENT
  @err and STDERR.puts @err.read
  raise Error, "missing java binaries"
end

Class Method Details

.session_log_path=(path) ⇒ Object

Set the detault UMI session log path (including file) that will be used if no log parameter will be passed to the UMI constructor



56
57
58
# File 'lib/universa/umi.rb', line 56

def self.session_log_path= path
  @@session_log_path = path
end

Instance Method Details

#closeObject

Close child process. No remote calls should occur after it.



144
145
146
147
148
149
150
151
152
# File 'lib/universa/umi.rb', line 144

def close
  @queue.push :poison_pill
  @cleanup_thread.join
  @closed = true
  @endpoint.close
  @in.close
  @out.close
  @wtr.value.exited?
end

#core_versionObject

Returns Universa network library core version.

Returns:

  • Universa network library core version



103
104
105
106
107
# File 'lib/universa/umi.rb', line 103

def core_version
  @core_version ||= begin
    invoke_static "Core", "getVersion"
  end
end

#find_by_remote_id(remote_id) ⇒ Object

debug use only. Looks for the cached e.g. (alive) remote object. Does not check the remote side.



161
162
163
# File 'lib/universa/umi.rb', line 161

def find_by_remote_id remote_id
  @lock.synchronize {@cache[remote_id]&.get}
end

#get_field(remote_object, name) ⇒ Object



130
131
132
# File 'lib/universa/umi.rb', line 130

def get_field(remote_object, name)
  encode_result call("get_field", remote_object._remote_id, name)
end

#inspectObject

short data label for UMI interface



155
156
157
# File 'lib/universa/umi.rb', line 155

def inspect
  "<UMI:#{__id__}:#{version}>"
end

#instantiate(object_class_name, *args, adapter: nil) ⇒ Ref

Create instance of some Universa Java API class, for example ‘Contract’, passing any arguments to its constructor. The returned reference could be used much like local instance, nu the actual work will happen in the child process. Use references as much as possible as they take all the housekeeping required, like memory leaks prevention and direct method calling.

Returns:

  • (Ref)

    reference to the remotely created object. See Ref.



115
116
117
118
# File 'lib/universa/umi.rb', line 115

def instantiate(object_class_name, *args, adapter: nil)
  ensure_open
  create_reference call("instantiate", object_class_name, *prepare_args(args)), adapter
end

#invoke(ref, method, *args) ⇒ Object

Invoke method by name. Should not be used directly; use Ref instance to call its methods.



121
122
123
124
125
126
127
128
# File 'lib/universa/umi.rb', line 121

def invoke(ref, method, *args)
  ensure_open
  ref._umi == self or raise InterchangeError
  @convert_case and method = method.to_s.camelize_lower
  # p ["invoke", ref._remote_id, method, *prepare_args(args)]
  result = call("invoke", ref._remote_id, method, *prepare_args(args))
  encode_result result
end

#invoke_static(class_name, method, *args) ⇒ Object



139
140
141
# File 'lib/universa/umi.rb', line 139

def invoke_static(class_name, method, *args)
  encode_result call("invoke", class_name, method.to_s.camelize_lower, *prepare_args(args))
end

#set_field(remote_object, name, value) ⇒ Object



134
135
136
137
# File 'lib/universa/umi.rb', line 134

def set_field(remote_object, name, value)
  call("set_field", remote_object._remote_id, name, prepare(value))
  value
end

#versionObject

Returns version of the connected UMI server. It is different from the gem version.

Returns:

  • version of the connected UMI server. It is different from the gem version.



98
99
100
# File 'lib/universa/umi.rb', line 98

def version
  @version.version
end

#with_trace(&block) ⇒ Object

Execute the block with trace mode on. Will spam the output with protocol information. These calls could be nested, on exit it restores previous trace state



167
168
169
170
171
172
# File 'lib/universa/umi.rb', line 167

def with_trace &block
  current_state, @trace = @trace, true
  result = block.call()
  @trace = current_state
  result
end