rhoconnect 4.0.0.beta.12 → 4.0.0.beta.24

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.
Files changed (88) hide show
  1. data/CHANGELOG.md +34 -0
  2. data/Gemfile +5 -8
  3. data/Gemfile.lock +34 -32
  4. data/Rakefile +2 -12
  5. data/bench/run_bench.sh +1 -1
  6. data/bench/spec/bench_spec_helper.rb +3 -5
  7. data/bin/rhoconnect +20 -13
  8. data/commands/dtach/dtach_install.rb +3 -3
  9. data/commands/generators/update.rb +1 -0
  10. data/commands/parser.rb +180 -0
  11. data/commands/rhoconnect/config.rb +8 -4
  12. data/commands/rhoconnect/get_token.rb +3 -1
  13. data/commands/rhoconnect/routes.rb +34 -0
  14. data/commands/rhoconnect/secret.rb +1 -1
  15. data/commands/rhoconnect/set_admin_password.rb +2 -1
  16. data/commands/rhoconnect/startbg.rb +4 -4
  17. data/commands/rhoconnect/stop.rb +2 -2
  18. data/commands/rhoconnect_console/console.rb +6 -6
  19. data/commands/rhoconnect_spec/spec.rb +2 -1
  20. data/commands/rhoconnect_war/war.rb +1 -0
  21. data/commands/utilities/redis_runner.rb +2 -2
  22. data/doc/data-partitioning.txt +20 -7
  23. data/doc/extending-rhoconnect-server.txt +36 -7
  24. data/doc/push-client-setup-rps.txt +3 -3
  25. data/doc/source-adapters-js.txt +41 -4
  26. data/doc/source-adapters.txt +3 -0
  27. data/generators/templates/application/rcgemfile +1 -5
  28. data/generators/templates/application/spec/spec_helper.rb +0 -9
  29. data/generators/templates/source/models/js/model.js +8 -0
  30. data/generators/templates/source/models/ruby/model.rb +5 -11
  31. data/install.sh +2 -2
  32. data/installer/unix-like/rho_connect_install_constants.rb +3 -3
  33. data/installer/utils/delete_from_s3.rb +3 -6
  34. data/installer/utils/download_from_s3.rb +5 -9
  35. data/installer/utils/verify_checksum.rb +16 -11
  36. data/js-adapters/ballroom.js +9 -0
  37. data/js-adapters/exceptions.js +60 -0
  38. data/js-adapters/node.rb +14 -5
  39. data/js-adapters/node_channel.rb +68 -48
  40. data/js-adapters/rhoconnect_helpers.js +8 -2
  41. data/js-adapters/router.js +16 -14
  42. data/lib/rhoconnect.rb +5 -5
  43. data/lib/rhoconnect/client.rb +2 -2
  44. data/lib/rhoconnect/condition/add_parameter.rb +21 -0
  45. data/lib/rhoconnect/controller/clients_controller.rb +1 -1
  46. data/lib/rhoconnect/controller/js_base.rb +1 -2
  47. data/lib/rhoconnect/controller/system_controller.rb +33 -10
  48. data/lib/rhoconnect/db_adapter.rb +1 -1
  49. data/lib/rhoconnect/document.rb +11 -3
  50. data/lib/rhoconnect/handler/changes.rb +20 -19
  51. data/lib/rhoconnect/handler/changes/engine.rb +48 -25
  52. data/lib/rhoconnect/handler/changes/hooks.rb +36 -0
  53. data/lib/rhoconnect/handler/changes/runner.rb +12 -2
  54. data/lib/rhoconnect/handler/helpers.rb +4 -4
  55. data/lib/rhoconnect/jobs/source_job.rb +1 -1
  56. data/lib/rhoconnect/model/base.rb +32 -8
  57. data/lib/rhoconnect/model/helpers.rb +15 -0
  58. data/lib/rhoconnect/model/helpers/find_duplicates_on_update.rb +85 -0
  59. data/lib/rhoconnect/model/js_base.rb +23 -28
  60. data/lib/rhoconnect/server.rb +23 -18
  61. data/lib/rhoconnect/source.rb +10 -17
  62. data/lib/rhoconnect/store.rb +36 -57
  63. data/lib/rhoconnect/test_methods.rb +5 -4
  64. data/lib/rhoconnect/utilities.rb +7 -5
  65. data/lib/rhoconnect/version.rb +2 -2
  66. data/lib/rhoconnect/web-console/server.rb +17 -16
  67. data/rhoconnect.gemspec +23 -24
  68. data/spec/apps/rhotestapp/controllers/js/js_sample_controller.js +4 -0
  69. data/spec/apps/rhotestapp/models/js/js_sample.js +36 -0
  70. data/spec/apps/rhotestapp/models/ruby/sample_adapter.rb +34 -19
  71. data/spec/async_spec.rb +1 -1
  72. data/spec/cli/cli_spec.rb +69 -31
  73. data/spec/client_sync_spec.rb +30 -13
  74. data/spec/controllers/js_base_spec.rb +10 -4
  75. data/spec/jobs/source_job_spec.rb +1 -1
  76. data/spec/models/{js_model_spec.rb → js_base_spec.rb} +63 -13
  77. data/spec/server/server_spec.rb +20 -0
  78. data/spec/server/stats_spec.rb +7 -17
  79. data/spec/source_adapter_spec.rb +6 -0
  80. data/spec/source_sync_spec.rb +219 -54
  81. data/spec/spec_helper.rb +8 -13
  82. data/spec/store_spec.rb +6 -4
  83. data/spec/test_methods_spec.rb +4 -4
  84. metadata +14 -27
  85. data/commands/execute.rb +0 -48
  86. data/commands/utilities/utilities.rb +0 -6
  87. data/generators/templates/application/controllers/application_controller.rb +0 -17
  88. data/lib/rhoconnect/js_adapter.rb +0 -79
@@ -5,15 +5,12 @@
5
5
  gem 'win32-process', '= 0.6.6', :platforms => [:mswin, :mingw]
6
6
 
7
7
  # use thin and eventmachine everywhere except JRuby
8
- platforms :ruby, :ruby_19, :mingw, :mingw_19 do
8
+ platforms :ruby, :mingw do
9
9
  gem "eventmachine", "~> 1.0.0"
10
10
  # using thin by default
11
11
  gem 'thin'
12
- end
13
-
14
12
  # for async framework
15
13
  # for Async, Eventful execution
16
- platforms :ruby_19, :mingw_19 do
17
14
  gem 'rack-fiber_pool'
18
15
  gem 'async-rack'
19
16
  end
@@ -37,5 +34,4 @@ group :test do
37
34
  gem 'rspec', '~> 2.10.0'
38
35
  gem 'rack-test', '>= 0.5.3', :require => "rack/test"
39
36
  gem 'rhomobile-debug', ">= 1.0.2"
40
- gem 'capybara'
41
37
  end
@@ -17,15 +17,6 @@ include Rhoconnect
17
17
  require 'rhoconnect/application/init'
18
18
  require 'rhoconnect/test_methods'
19
19
 
20
- require 'capybara'
21
- require 'capybara/dsl'
22
-
23
- Capybara.app = Sinatra::Application
24
-
25
- RSpec.configure do |config|
26
- config.include Capybara
27
- end
28
-
29
20
  shared_examples_for "SpecHelper" do
30
21
  include Rhoconnect::TestMethods
31
22
 
@@ -43,6 +43,14 @@ var <%=class_name%> = function(){
43
43
  // TODO: Logout from the data source if necessary.
44
44
  resp.send(true);
45
45
  };
46
+
47
+ this.storeBlob = function(resp){
48
+ // TODO: Handle post requests for blobs here.
49
+ // Reference the blob object's path with resp.params.path.
50
+ new rc.Exception(
51
+ resp, "Please provide some code to handle blobs if you are using them."
52
+ );
53
+ };
46
54
  };
47
55
 
48
56
  module.exports = new <%=class_name%>();
@@ -38,15 +38,9 @@ class <%=class_name%> < Rhoconnect::Model::Base
38
38
  # TODO: Logout from the data source if necessary
39
39
  end
40
40
 
41
- # Calling super here returns rack tempfile path:
42
- # i.e. /var/folders/J4/J4wGJ-r6H7S313GEZ-Xx5E+++TI
43
- # Note: This tempfile is removed when server stops or crashes...
44
- # See http://rack.rubyforge.org/doc/Multipart.html for more info
45
- #
46
- # Uncomment this code and override it by creating a copy of the file somewhere
47
- # and returning the path to that file (then don't call super!):
48
- # i.e. /mnt/myimages/soccer.png
49
- #def store_blob(object,field_name,blob)
50
- # super #=> returns blob[:tempfile]
51
- #end
41
+ def store_blob(object,field_name,blob)
42
+ # TODO: Handle post requests for blobs here.
43
+ # make sure you store the blob object somewhere permanently
44
+ raise "Please provide some code to handle blobs if you are using them."
45
+ end
52
46
  end
data/install.sh CHANGED
@@ -258,10 +258,10 @@ rubyVersion=""
258
258
  rubyBinDir=""
259
259
 
260
260
  # Define ruby and nodejs versions to be installed ...
261
- RUBY_PATCH="p392"
261
+ RUBY_PATCH="p429"
262
262
  RUBY_VERSION="ruby-1.9.3-${RUBY_PATCH}"
263
263
 
264
- NODE_VERSION=v0.10.4
264
+ NODE_VERSION=v0.10.5
265
265
  NODE_URL=http://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}.tar.gz
266
266
  ARCH=$([[ `uname -m` == x86_64 ]] && echo "x64" || echo "x86")
267
267
  NODE_BIN_URL=http://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-${ARCH}.tar.gz
@@ -8,10 +8,10 @@ module Constants
8
8
  "libaprutil1-dev",
9
9
  "dtach"]
10
10
 
11
- RUBY = "ruby-1.9.3-p392"
12
- REDIS = "redis-2.6.10"
11
+ RUBY = "ruby-1.9.3-p429"
12
+ REDIS = "redis-2.6.13"
13
13
  SQLITE3 = "sqlite-autoconf-3071502"
14
- NGINX = "nginx-1.3.13"
14
+ NGINX = "nginx-1.4.1"
15
15
  PASSENGER_ROOT = "/opt/rhoconnect/lib/ruby/gems/1.9.1/gems/passenger"
16
16
 
17
17
  SOFTWARE = [ REDIS, SQLITE3, RUBY, NGINX ]
@@ -9,7 +9,6 @@ USER_DATA = `wget -q -O - http://169.254.169.254/latest/user-data`.split("\n")
9
9
 
10
10
  bucket_name = ARGV[0].strip
11
11
  channel = ARGV[1].strip
12
- files = []
13
12
 
14
13
  def get_access_keys
15
14
  access_key = USER_DATA[0].to_s.strip
@@ -20,11 +19,9 @@ end #get_access_keys
20
19
 
21
20
  AWS::S3::Base.establish_connection!(get_access_keys)
22
21
 
23
- objects = Bucket.objects(bucket_name)
24
-
25
- objects.each do |obj|
26
- files << "#{obj.key}" if obj.key =~ /^#{channel}/
27
- end
22
+ objects = Bucket.objects(bucket_name, :prefix => channel)
23
+ files = []
24
+ objects.each { |obj| files << "#{obj.key}" }
28
25
 
29
26
  files.each do |file|
30
27
  puts "Deleting #{file}"
@@ -9,7 +9,6 @@ USER_DATA = `wget -q -O - http://169.254.169.254/latest/user-data`.split("\n")
9
9
 
10
10
  bucket_name = 'rhoconnect'
11
11
  channel = ARGV[0]
12
- files = []
13
12
 
14
13
  exit if ARGV.length != 1
15
14
 
@@ -21,16 +20,13 @@ end #cmd
21
20
  def get_access_keys
22
21
  access_key = USER_DATA[0].to_s.strip
23
22
  secret_access_key = USER_DATA[1].to_s.strip
24
- keys = { :access_key_id => access_key,
25
- :secret_access_key => secret_access_key}
26
- end #get_access_keys
23
+ keys = { :access_key_id => access_key, :secret_access_key => secret_access_key}
24
+ end
27
25
 
28
26
  AWS::S3::Base.establish_connection!(get_access_keys)
29
-
30
- objects = Bucket.objects(bucket_name)
31
- objects.each do |obj|
32
- files << "#{obj.key}" if obj.key =~ /^#{channel}/
33
- end
27
+ objects = Bucket.objects(bucket_name, :prefix => channel)
28
+ files = []
29
+ objects.each { |obj| files << "#{obj.key}" }
34
30
 
35
31
  # Remove the files before downloading new in case they already exist
36
32
  cmd "rm -rf #{channel}" if File.directory? channel
@@ -25,25 +25,30 @@ dir_to_compare = ARGV[1]
25
25
 
26
26
  # Downoad Packages from S3
27
27
  cmd "ruby ./installer/utils/download_from_s3.rb #{dir_to_compare}"
28
-
29
28
  # Create the checksum to compare against the one from the S3 repo
30
29
  cmd "ruby ./installer/utils/create_sha1.rb #{dir_to_compare} ."
30
+ old_sha1_h = YAML.load(File.read(checksum_hash))
31
+ new_sha1_h = YAML.load(File.read("./sha1_hash"))
32
+
33
+ match = true
34
+ old_sha1_h.each do |k,v|
35
+ unless new_sha1_h[k]
36
+ puts "Error! Checksum mismatch for file #{v}"
37
+ match = false
38
+ end
39
+ end
31
40
 
32
- old_sha1_h = YAML.load(File.read(checksum_hash))
33
- new_sha1_h = YAML.load(File.read("./sha1_hash"))
34
-
35
- if checksum(old_sha1_h.keys) != checksum(new_sha1_h.keys)
41
+ unless match
42
+ puts
36
43
  puts "Checksums do not match!"
37
-
38
44
  puts "Expected sha1/file"
39
- old_sha1_h = YAML.load(File.read(checksum_hash)).sort
45
+ old_sha1_h = YAML.load(File.read(checksum_hash)).sort
40
46
  old_sha1_h.each { |k,v| puts "#{k}: #{v}" }
41
-
42
47
  puts "Got sha1/file"
43
- new_sha1_h = YAML.load(File.read("./sha1_hash")).sort
48
+ new_sha1_h = YAML.load(File.read("./sha1_hash")).sort
44
49
  new_sha1_h.each { |k,v| puts "#{k}: #{v}" }
45
-
50
+ puts
46
51
  exit 2
47
- end
52
+ end
48
53
 
49
54
  puts "Checksums match!"
@@ -19,6 +19,15 @@ var _m_name = null;
19
19
  //TODO: Don't have test-specific code here
20
20
  var prefixDir = '/spec/apps/rhotestapp';
21
21
 
22
+ //TODO: This has to be changed in the future
23
+ // The only reason for this - on Windows Node.js
24
+ // dies and leaves hanging opened sockets
25
+ // Proper way: have a global domain as a safety valve
26
+ process.on('uncaughtException', function(err) {
27
+ client_sub.quit();
28
+ client_pub.quit();
29
+ });
30
+
22
31
  var controllerName = function(n){
23
32
  _c_name = n;
24
33
  registeredControllers[_c_name] = {};
@@ -0,0 +1,60 @@
1
+ var util = require('util');
2
+
3
+ //Setup exception handlers
4
+ var AbstractException = function (resp,msg,constr) {
5
+ Error.captureStackTrace(this, constr || this);
6
+ this.message = msg || 'Error';
7
+ resp.exception = {"error": {"error_type": this.name, "message": this.message, "stacktrace": this.stack} };
8
+ resp.send(null);
9
+ };
10
+
11
+ var Exception = function (resp,msg) {
12
+ Exception.super_.call(this, resp, msg, this.constructor);
13
+ };
14
+
15
+ var LoginException = function (resp,msg) {
16
+ LoginException.super_.call(this, resp, msg, this.constructor);
17
+ };
18
+
19
+ var LogoffException = function (resp,msg) {
20
+ LogoffException.super_.call(this, resp, msg, this.constructor);
21
+ };
22
+
23
+ var ServerTimeoutException = function (resp,msg) {
24
+ ServerTimeoutException.super_.call(this, resp, msg, this.constructor);
25
+ };
26
+
27
+ var ServerErrorException = function (resp,msg) {
28
+ ServerErrorException.super_.call(this, resp, msg, this.constructor);
29
+ };
30
+
31
+ var ObjectConflictErrorException = function (resp,msg) {
32
+ ObjectConflictErrorException.super_.call(this, resp, msg, this.constructor);
33
+ };
34
+
35
+ // Setup the types for each exception
36
+ util.inherits(AbstractException, Error);
37
+ util.inherits(Exception, AbstractException);
38
+ util.inherits(LoginException, AbstractException);
39
+ util.inherits(LogoffException, AbstractException);
40
+ util.inherits(ServerTimeoutException, AbstractException);
41
+ util.inherits(ServerErrorException, AbstractException);
42
+ util.inherits(ObjectConflictErrorException, AbstractException);
43
+
44
+
45
+ // Default names
46
+ AbstractException.prototype.name = 'AbstractException';
47
+ Exception.prototype.name = 'Exception';
48
+ LoginException.prototype.name = 'LoginException';
49
+ LogoffException.prototype.name = 'LogoffException';
50
+ ServerTimeoutException.prototype.name = 'ServerTimeoutException';
51
+ ServerErrorException.prototype.name = 'ServerErrorException';
52
+ ObjectConflictErrorException.prototype.name = 'ObjectConflictErrorException';
53
+
54
+ // Public exceptions
55
+ module.exports.Exception = Exception;
56
+ module.exports.LoginException = LoginException;
57
+ module.exports.LogoffException = LogoffException;
58
+ module.exports.ServerTimeoutException = ServerTimeoutException;
59
+ module.exports.ServerErrorException = ServerErrorException;
60
+ module.exports.ObjectConflictErrorException = ObjectConflictErrorException;
@@ -7,7 +7,7 @@ module Rhoconnect
7
7
  @started = false
8
8
  @pipe = nil
9
9
 
10
- def self.shell_node
10
+ def self.shell_node(opts = {})
11
11
  package_file = File.join(Dir.pwd,'package.json')
12
12
  if not File.exists?(package_file)
13
13
  Rhoconnect.use_node = false
@@ -18,7 +18,7 @@ module Rhoconnect
18
18
  if which("node")
19
19
  begin
20
20
  if @started
21
- kill_process
21
+ kill_process(opts)
22
22
  end
23
23
  @started = true
24
24
  dir = File.expand_path(File.dirname(__FILE__))
@@ -28,7 +28,7 @@ module Rhoconnect
28
28
  file = File.join(dir,"server.js")
29
29
  args = [sub_env, "node", file, "#{$$}", Rhoconnect.environment.to_s]
30
30
  @pipe = IO.popen(args)
31
- log "Started Node.js process: #{@pipe.pid}"
31
+ log "Starting Node.js process: #{@pipe.pid}"
32
32
  @pipe
33
33
  rescue Exception=>e
34
34
  puts "Node.js startup error: #{e.message}\n"
@@ -41,9 +41,18 @@ module Rhoconnect
41
41
  end
42
42
  end
43
43
 
44
- def self.kill_process
44
+ def self.kill_process(opts = {})
45
45
  log "Stopping Node.js process: #{@pipe.pid}" if @pipe
46
- NodeChannel.exit_node
46
+ if opts[:force]
47
+ begin
48
+ Process.kill('KILL', @pipe.pid)
49
+ Process.waitpid(@pipe.pid)
50
+ rescue
51
+ # Process not found, don't worry about killing it
52
+ end
53
+ else
54
+ NodeChannel.exit_node
55
+ end
47
56
  @started = false
48
57
  @pipe = nil
49
58
  end
@@ -11,7 +11,7 @@ module Rhoconnect
11
11
  TIMEOUT = 30 # seconds
12
12
  PUB_CHANNEL = "#{$$}RedisSUB" # pub channel must link to redis sub channel
13
13
  SUB_CHANNEL = "#{$$}RedisPUB" # sub channel must link to redis pub channel
14
- @message_thread,@redis,@redis2 = nil
14
+ @message_thread,@redis_subscriber,@redis_publisher = nil
15
15
 
16
16
  class << self
17
17
  attr_accessor :thrd, :register_semaphore, :register_condition
@@ -20,22 +20,22 @@ module Rhoconnect
20
20
  TIMEOUT
21
21
  end
22
22
 
23
- def redis
23
+ def redis_subscriber
24
24
  url = Rhoconnect.redis.is_a?(Array) ? Rhoconnect.redis[0] : Rhoconnect.redis
25
25
  db_inst = RedisImpl.new
26
26
  db_inst.create(url)
27
- @redis ||= db_inst.db
27
+ @redis_subscriber ||= db_inst.db
28
28
  end
29
29
 
30
- def redis2
30
+ def redis_publisher
31
31
  url = Rhoconnect.redis.is_a?(Array) ? Rhoconnect.redis[0] : Rhoconnect.redis
32
32
  db_inst = RedisImpl.new
33
33
  db_inst.create(url)
34
- @redis2 ||= db_inst.db
34
+ @redis_publisher ||= db_inst.db
35
35
  end
36
36
 
37
37
  def exit_node
38
- NodeChannel.redis2.publish(PUB_CHANNEL,{:route => 'deregister'}.to_json)
38
+ NodeChannel.redis_publisher.publish(PUB_CHANNEL,{:route => 'deregister'}.to_json)
39
39
  end
40
40
 
41
41
  def bootstrap
@@ -54,58 +54,85 @@ module Rhoconnect
54
54
  end
55
55
 
56
56
  def publish_channel_and_wait(msg,curr_model)
57
- unique_id = UUIDTools::UUID.random_create.to_s.gsub(/\-/,"")
57
+ unique_id = get_random_uuid
58
58
  msg.merge!(:request_id => unique_id)
59
59
  RESULT_HASH[unique_id] = {}
60
60
  RESULT_HASH[unique_id][:status] = 'waiting'
61
- NodeChannel.redis2.publish(PUB_CHANNEL,msg.to_json)
62
- NodeChannel.wait_for_result(unique_id,curr_model)
61
+ publish_channel(PUB_CHANNEL,msg,unique_id,curr_model)
63
62
  end
64
63
 
65
64
  def check_channel
66
- NodeChannel.redis.subscribe(SUB_CHANNEL) do |on|
65
+ NodeChannel.redis_subscriber.subscribe(SUB_CHANNEL) do |on|
67
66
  on.message do |channel,msg|
68
67
  m = JSON.parse(msg)
69
- #puts "received message: #{m.inspect}"
70
68
  if m['exit'] == true
71
- NodeChannel.redis.unsubscribe
69
+ NodeChannel.redis_subscriber.unsubscribe
72
70
  end
73
71
  route_message(m)
74
72
  end
75
73
  end
76
74
  end
77
75
 
78
- def publish_channel(msg)
79
- NodeChannel.redis2.publish(PUB_CHANNEL,msg.to_json)
76
+ def publish_channel(channel,msg,unique_id = nil,curr_model = nil)
77
+ result = {}
78
+ num_clients = NodeChannel.redis_publisher.publish(channel,msg.to_json)
79
+ if num_clients >= 1
80
+ result = NodeChannel.wait_for_result(unique_id,curr_model) if unique_id and curr_model
81
+ else
82
+ log "ERROR: Cannot communicate with Node.js process."
83
+ if Rhoconnect.restart_node_on_error
84
+ t = Thread.new do
85
+ # Shutdown our ruby subscription by issuing redis channel instruction
86
+ NodeChannel.redis_subscriber.publish(SUB_CHANNEL,{'exit' => true}.to_json)
87
+ end
88
+ t.join
89
+ Rhoconnect.start_nodejs_channels(force: true)
90
+ end
91
+ end
92
+ result
80
93
  end
81
94
 
82
95
  def wait_for_result(key,curr_model)
83
96
  begin
84
97
  Timeout::timeout(TIMEOUT) do
85
98
  while(RESULT_HASH[key][:status] == 'waiting') do
86
- if data = RESULT_HASH[key][:process_request]
99
+ if RESULT_HASH[key][:pending_js_requests] and result_id = RESULT_HASH[key][:pending_js_requests][0]
87
100
  #do some logic and return data with memory
88
-
89
- RESULT_HASH[key][:process_result] = process_message(curr_model,data)
90
- RESULT_HASH[key][:process_request] = nil
91
- else
92
- sleep 0.001
101
+ data = RESULT_HASH[key][result_id][:process_request]
102
+ msg_result = process_message(curr_model,data)
103
+ RESULT_HASH[key][result_id][:process_result] = msg_result
104
+ RESULT_HASH[key][:pending_js_requests] = RESULT_HASH[key][:pending_js_requests].drop(1)
105
+ if RESULT_HASH[key][:pending_js_requests].length == 0 and not RESULT_HASH[key][:result].nil?
106
+ RESULT_HASH[key][:status] = 'done'
107
+ end
108
+ publish_channel(PUB_CHANNEL,msg_result)
93
109
  end
110
+ Thread.pass
94
111
  end
95
112
  end
96
113
  rescue Exception=>e
97
114
  RESULT_HASH[key][:status] = 'broken'
98
115
  RESULT_HASH[key][:result] = "exception #{e.message}\n#{e.backtrace}"
99
- log "Timeout on wait, setting JavaScript result state to broken: #{e.inspect}"
116
+ log "Timeout on wait, setting JavaScript result state to broken: #{e.message}"
100
117
  log e.backtrace.join("\n")
101
118
  end
102
-
103
119
  #request waiting either timed out or returned response
120
+ res = {}
104
121
  if RESULT_HASH[key][:status] == 'broken' or RESULT_HASH[key][:status] == 'waiting'
105
122
  res = RESULT_HASH.delete(key)
106
- elsif RESULT_HASH[key] =~ /JS ERROR/
107
- res = RESULT_HASH.delete(key)
108
- raise Exception.new(res)
123
+ elsif RESULT_HASH[key] and RESULT_HASH[key][:result].is_a?(Hash) and RESULT_HASH[key][:result]['error_type']
124
+ include Rhoconnect::Model
125
+ res = RESULT_HASH.delete(key)[:result]
126
+ klass = res['error_type']
127
+ exception = nil
128
+
129
+ if klass and const_defined?(klass)
130
+ exception = const_get(klass).new(res['message'])
131
+ else
132
+ exception = Exception.new(res['message'])
133
+ end
134
+ exception.set_backtrace(res['stacktrace'].split("\n"))
135
+ raise exception
109
136
  else
110
137
  res = RESULT_HASH.delete(key)
111
138
  end
@@ -115,25 +142,31 @@ module Rhoconnect
115
142
  def route_message(msg)
116
143
  case msg['route']
117
144
  when 'request'
118
- RESULT_HASH[msg['request_id']][:process_result] = 'waiting'
119
- RESULT_HASH[msg['request_id']][:process_request] = msg
120
- wait_for_process_result(msg['request_id'])
145
+ result_id = get_random_uuid
146
+ RESULT_HASH[msg['request_id']][:pending_js_requests] ||= []
147
+ RESULT_HASH[msg['request_id']][:pending_js_requests] << result_id
148
+ RESULT_HASH[msg['request_id']][result_id] = {}
149
+ RESULT_HASH[msg['request_id']][result_id][:process_result] = 'waiting'
150
+ RESULT_HASH[msg['request_id']][result_id][:process_request] = msg
121
151
  when 'response'
122
152
  return if RESULT_HASH[msg['request_id']] == nil
123
153
  if msg['error'] and msg['error'].size > 1
124
- RESULT_HASH[msg['request_id']][:result] = "JS ERROR: #{msg['error']}"
154
+ RESULT_HASH[msg['request_id']][:result] = msg['error']
125
155
  RESULT_HASH[msg['request_id']][:status] = 'done'
126
156
  else
127
157
  RESULT_HASH[msg['request_id']][:result] = msg["result"]
128
- RESULT_HASH[msg['request_id']][:status] = 'done'
158
+ pending_js_requests = RESULT_HASH[msg['request_id']][:pending_js_requests]
159
+ if not pending_js_requests or pending_js_requests.length == 0
160
+ RESULT_HASH[msg['request_id']][:status] = 'done'
161
+ end
129
162
  end
130
163
  when 'register'
131
164
  @register_semaphore.synchronize do
132
165
  begin
133
166
  register_routes(msg)
134
167
  rescue Exception => e
135
- puts "Error registering JavaScript routes: #{e.inspect}"
136
- puts e.backtrace.join("\n")
168
+ log "Error registering JavaScript routes: #{e.inspect}"
169
+ log e.backtrace.join("\n")
137
170
  raise e
138
171
  ensure
139
172
  @register_condition.signal
@@ -142,25 +175,14 @@ module Rhoconnect
142
175
  end
143
176
  end
144
177
 
145
- def wait_for_process_result(key)
146
- Timeout::timeout(TIMEOUT){
147
- while(RESULT_HASH[key][:process_result] == 'waiting') do
148
- sleep 0.001
149
- end
150
- }
151
- publish_channel(RESULT_HASH[key][:process_result])
152
- end
153
-
154
178
  def process_message(curr_model,data)
155
- # puts "proccessing msg #{data.inspect}"
156
179
  klass = curr_model
157
180
  klass = Object.const_get(data['kls']) if data['kls']
158
181
  if klass.respond_to?(data['function'].to_sym)
159
182
  if data['args'] and data['args'].size > 0
160
183
  if(data['function'] == 'stash_result')
161
184
  klass.send('result=',data['args'])
162
- klass.send(data['function'])
163
- process_res = nil
185
+ res = klass.send(data['function'])
164
186
  else
165
187
  res = klass.send(data['function'],*data['args'])
166
188
  end
@@ -171,12 +193,10 @@ module Rhoconnect
171
193
  raise Exception.new("Method #{data['function']} not found in model #{curr_model.class.name.to_s}.")
172
194
  end
173
195
 
174
- process_res = res
175
196
  unless res.is_a?(String) or res.is_a?(TrueClass) or res.is_a?(FalseClass) or res.nil?
176
- process_res = res.to_hash
197
+ res = res.to_hash
177
198
  end
178
-
179
- {:result=>process_res,:callback=>data['callback'],:request_id=>data['request_id'],:route=>'response'}
199
+ {:result=>res,:callback=>data['callback'],:request_id=>data['request_id'],:route=>'response'}
180
200
  end
181
201
 
182
202
  def register_routes(hsh)