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

Sign up to get free protection for your applications and to get access to all the features.
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)