#------------------------------------------------------------------------ # (The MIT License) # # Copyright (c) 2008-2011 Rhomobile, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # http://rhomobile.com #------------------------------------------------------------------------ require 'rho/render' require 'rho/rhoapplication' require 'rho/rhoerror' require 'rhodes' module Rho def self.get_app RHO.get_instance().get_app('app') end def self.support_transitions?() is_bb6 = System::get_property('platform') == 'Blackberry' && (System::get_property('os_version') =~ /^6\.0/) System::get_property('platform') == 'APPLE' || System::get_property('platform') == 'ANDROID' || is_bb6 end def self.close_database(arg_partition=nil) if arg_partition puts "Close database for partition: #{arg_partition}" ::Rho::RHO.get_db_partitions()[arg_partition].close else ::Rho::RHO.get_db_partitions().each do |partition, db| puts "Close database for partition: #{partition}" db.close end end end class RHO APPLICATIONS = {} APPNAME = 'app' @@rho_framework = nil @@native_bar_initialized = nil @@use_new_orm = false def self.get_instance @@rho_framework end def self.use_new_orm return @@use_new_orm end def initialize(app_manifest_filename=nil) puts "Calling RHO.initialize #{app_manifest_filename}, use_new_orm: #{Rho::RhoConfig.use_new_orm.to_i}" @@use_new_orm = (Rho::RhoConfig.use_new_orm.to_i > 0 ? true : false) if(@@use_new_orm) puts "MZV_DEBUG: we are in use_new_orm true #{@@use_new_orm}" else puts "MZV_DEBUG: we are in use_new_orm false #{@@use_new_orm}" end # Initialize application and sources @@rho_framework = self @db_partitions = {} partitions = ['app','user','local'] partitions.each do |partition| @db_partitions[partition] = Rhom::RhomDbAdapter.new(Rho::RhoFSConnector::get_db_fullpathname(partition), partition) end require 'rhom' unless @@use_new_orm if app_manifest_filename load_models_from_file(app_manifest_filename) else load_models_from_file(Rho::RhoFSConnector::get_app_manifest_filename) end else Rhom::Rhom.get_instance().load_models(app_manifest_filename) end end attr_reader :db_partitions def self.get_src_db(src_name=nil) if src_name unless @@use_new_orm src_partition = Rho::RhoConfig.sources[src_name]['partition'] @@rho_framework.db_partitions[src_partition] else models = Rho::NewORMModel.enumerate model = models.detect { |m| m.model_name == src_name } src_partition = model.partition @@rho_framework.db_partitions[src_partition] end else @@rho_framework.db_partitions['user'] end end def self.get_user_db @@rho_framework.db_partitions['user'] end def self.get_db_partitions @@rho_framework.db_partitions end def init_app puts "init_app" begin get_app(APPNAME) rescue Exception => e trace_msg = e.backtrace.join("\n") puts 'Application initialize failed: ' + e.inspect + ";Trace: #{trace_msg}" end end def init_nativebar return if @@native_bar_initialized begin get_app(APPNAME).init_nativebar rescue Exception => e trace_msg = e.backtrace.join("\n"); puts "Application's native bar initialization failed: #{e.inspect}; Trace: #{trace_msg}" end @@native_bar_initialized = true end def on_config_conflicts(conflicts) begin get_app(APPNAME).on_reinstall_config_update(conflicts) rescue Exception => e trace_msg = e.backtrace.join("\n") puts 'Application on_reinstall_config_update failed: ' + e.inspect + ";Trace: #{trace_msg}" end end def activate_app begin get_app(APPNAME).on_activate_app rescue Exception => e trace_msg = e.backtrace.join("\n") puts 'Application activate failed: ' + e.inspect + ";Trace: #{trace_msg}" end end def deactivate_app begin get_app(APPNAME).on_deactivate_app rescue Exception => e trace_msg = e.backtrace.join("\n") puts 'Application deactivate failed: ' + e.inspect + ";Trace: #{trace_msg}" end end def ui_created begin get_app(APPNAME).on_ui_created rescue Exception => e trace_msg = e.backtrace.join("\n") puts '"UI created" callback failed: ' + e.inspect + ";Trace: #{trace_msg}" end end def ui_destroyed begin get_app(APPNAME).on_ui_destroyed @@native_bar_initialized = false rescue Exception => e trace_msg = e.backtrace.join("\n"); puts '"UI destroyed" callback failed: ' + e.inspect + ";Trace: #{trace_msg}" end end def raise_rhoerror(errCode) raise Rho::RhoError.new(errCode) end def set_app(appname, app) APPLICATIONS[appname] = app end def get_app(appname) if (APPLICATIONS[appname].nil?) require 'application' Object.const_get('AppApplication').new unless APPLICATIONS[appname].initialized? msg = "RhoApplication was not correctly initialized (forget to call 'super' in AppApplication.initialize ?)" puts msg raise msg end end APPLICATIONS[appname] end if Rho::System.isRhoSimulator def load_models_from_file(app_manifest_filename=nil) require 'rhoappmanifest' fappManifest = Rho::AppManifest.enumerate_models(Rho::RhoFSConnector.get_app_path('app').chop) _load_models_from_file(fappManifest) fappManifest.close end else def load_models_from_file(app_manifest_filename=nil) f = File.open(app_manifest_filename) _load_models_from_file(f) f.close end end def _load_models_from_file(f) f.each do |line| str = line.chomp if str != nil and str.length > 0 puts "model file: #{str}" model_name = File.basename(File.dirname(str)) Rho::RhoConfig::add_source(model_name, {:loaded => false, :file_path => str}) end end end def self.check_sources_migration(uniq_sources) uniq_sources.each do |source| check_source_migration(source) end end def self.check_source_migration(source) return unless source.has_key?('migrate_version') db = ::Rho::RHO.get_src_db(source['name']) if !get_instance().get_app(APPNAME).on_migrate_source(source['migrate_version'], source) db.execute_batch_sql(source['schema']['sql']) end db.update_into_table('sources', {"schema_version"=>source['schema_version']},{"name"=>source['name']}) end def reset_db_on_sync_user_changed() puts "reset_db_on_sync_user_changed" begin get_app(APPNAME).on_sync_user_changed rescue Exception => e trace_msg = e.backtrace.join("\n") puts 'Application reset_db_on_sync_user_changed failed: ' + e.inspect + ";Trace: #{trace_msg}" end end def load_server_sources(data) puts "load_server_sources : #{data}" begin res = Rho::JSON.parse(data) skip_schema = false if res['skip_schema'] skip_schema = res['skip_schema'].to_i() > 0 end puts "skip_schema = #{skip_schema}" if res['partition'] str_partition = res['partition'] puts "reload sources for partition: #{str_partition}" db = @db_partitions[ str_partition ] #puts "sources before: #{Rho::RhoConfig::sources()}" db.start_transaction begin mapProps = {} mapFreezed = {} Rho::RhoConfig::sources().each do |key,value| if value['partition']==str_partition mapProps[key] = value['property'] mapFreezed[key] = value['freezed'] end end Rho::RhoConfig::sources().delete_if {|key, value| value['partition']==str_partition } arSrcs = db.select_from_table('sources','source_id, name, sync_priority, partition, sync_type, schema, schema_version, associations, blob_attribs', {'partition'=>str_partition} ) arSrcs.each do |src| if src if src['schema'] && src['schema'].length() > 0 if !skip_schema #puts "src['schema'] : #{src['schema']}" hashSchema = Rho::JSON.parse(src['schema']) #puts "hashSchema : #{hashSchema}" src['schema'] = hashSchema src['schema']['sql'] = ::Rho::RHO.make_createsql_script( src['name'], hashSchema) src['schema_version'] = hashSchema['version'] db.update_into_table('sources', {"schema"=>src['schema']['sql'], "schema_version"=>src['schema_version']},{"name"=>src['name']}) #if str_partition != 'user' # @db_partitions['user'].update_into_table('sources', {"schema"=>src['schema']['sql'], "schema_version"=>src['schema_version']},{"name"=>src['name']}) #end end else props = mapProps[src['name']] if props src['property'] = props end freezed = mapFreezed[src['name']] if freezed src['freezed'] = freezed end end end src[:loaded] = true Rho::RhoConfig::sources()[ src['name'] ] = src end db.commit rescue Exception => e db.rollback raise end ::Rho::RHO.init_sync_source_properties(Rho::RhoConfig::sources().values()) #puts "sources after: #{Rho::RhoConfig::sources()}" return end rescue Exception => e puts "Error load_server_sources: #{e}" puts "Trace: #{e.backtrace}" end raise ArgumentError, "load_server_sources should be called only from bulk sync with partition parameter!" end def self.load_all_sources unless @@use_new_orm Rho::RHO.get_instance().load_all_sync_sources() end end @all_models_loaded = false def load_all_sync_sources() unless Rho::RHO.use_new_orm return if @all_models_loaded puts "load_all_sync_sources" begin Rho::RhoConfig.sources.values.each do |src| next if src[:loaded] load_model(src['name'],false) end init_sources() rescue Exception => e puts "Error load_all_sync_sources: #{e}" puts "Trace: #{e.backtrace}" end else @all_models_loaded = true end end def load_model(modelName, init_db = true) return nil if !Rho::RhoConfig.sources.has_key?(modelName) || Rho::RhoConfig.sources[modelName][:loaded] Rho::RhoConfig.sources[modelName][:loaded] = true puts "load_model: #{modelName}" Rhom::RhomObjectFactory.init_object(modelName) require "#{Rho::RhoConfig.sources[modelName][:file_path]}" puts "model name: #{modelName}" modelClass = nil modelClass = Object.const_get(modelName) if Object.const_defined?(modelName) if modelClass puts "model class found" if modelClass.respond_to?( :get_model_params ) Rho::RhoConfig::add_loaded_source(modelName,modelClass.get_model_params()) modelClass.reset_model_params() partition = Rho::RhoConfig::sources[modelName]['partition'] db = ::Rho::RHO.get_db_partitions()[partition] if !db db = Rhom::RhomDbAdapter.new(Rho::RhoFSConnector::get_db_fullpathname(partition), partition) ::Rho::RHO.get_db_partitions()[partition] = db end if init_db hash_migrate = {} db.start_transaction begin uniq_sources = [Rho::RhoConfig::sources[modelName]] init_db_sources(db, uniq_sources, partition, hash_migrate) puts "Migrate schema sources: #{hash_migrate}" ::Rho::RHO.init_schema_sources_partition(uniq_sources, hash_migrate, partition, db) ::Rho::RHO.check_sources_migration(uniq_sources) ::Rho::RHO.init_sync_source_properties(uniq_sources) #SyncEngine.update_blob_attribs(partition, Rho::RhoConfig::sources[modelName]['source_id'].to_i() ) db.commit rescue Exception => e trace_msg = e.backtrace.join("\n") puts "exception when init_db_sources: #{e}; Trace:" + trace_msg db.rollback end end else puts "ERROR: Invalid model definition. Add 'include Rhom::PropertyBag' or 'include Rhom::FixedSchema' to model class" end else puts "ERROR: cannot load model : #{modelClass}" end modelClass end def find_src_byname(uniq_sources, src_name) uniq_sources.each do |source| return source if src_name == source['name'] end nil end # setup the sources table and model attributes for all applications def init_sources() return unless defined? Rho::RhoConfig::sources @all_models_loaded = true uniq_sources = Rho::RhoConfig::sources.values puts 'init_sources: ' #+ uniq_sources.inspect uniq_sources.each do |source| source['str_associations'] = "" end uniq_sources.each do |source| partition = source['partition'] @db_partitions[partition] = nil unless @db_partitions[partition] if source['belongs_to'] source['belongs_to'].each do |hash_pair| attrib = hash_pair.keys[0] src_name = hash_pair.values[0] associationsSrc = find_src_byname(uniq_sources, src_name) if !associationsSrc puts "Error: belongs_to '#{source['name']}' : source name '#{src_name}' does not exist." next end str_associations = associationsSrc['str_associations'] str_associations = "" unless str_associations str_associations += ',' if str_associations.length() > 0 str_associations += source['name'] + ',' + attrib associationsSrc['str_associations'] = str_associations end end end #user partition should alwayse exist @db_partitions['user'] = nil unless @db_partitions['user'] hash_migrate = {} puts "@db_partitions : #{@db_partitions}" @db_partitions.each do |partition, db| db = Rhom::RhomDbAdapter.new(Rho::RhoFSConnector::get_db_fullpathname(partition), partition) unless db db.start_transaction begin init_db_sources(db, uniq_sources, partition,hash_migrate) #SyncEngine.update_blob_attribs(partition, -1 ) db.commit rescue Exception => e trace_msg = e.backtrace.join("\n") puts "exception when init_db_sources: #{e}; Trace:" + trace_msg db.rollback end @db_partitions[partition] = db end puts "Migrate schema sources: #{hash_migrate}" ::Rho::RHO.init_schema_sources(hash_migrate) ::Rho::RHO.check_sources_migration(uniq_sources) #@db_partitions.each do |partition, db| # SyncEngine.update_blob_attribs(partition, -1 ) #end ::Rho::RHO.init_sync_source_properties(uniq_sources) end def self.init_sync_source_properties(uniq_sources) if defined?(RHOCONNECT_CLIENT_PRESENT) uniq_sources.each do|src| ['pass_through', 'full_update'].each do |prop| next unless src.has_key?(prop) Rho::RhoConnectClient.set_source_property(src['source_id'], prop, src[prop] ? src[prop].to_s() : '' ) end end uniq_sources.each do|src| if src.has_key?('freezed') || !src['schema'].nil? hash_props = !src['schema'].nil? ? src['schema']["property"] : src["property"] if (!hash_props.nil?) str_props = hash_props.keys.join(',') Rho::RhoConnectClient.set_source_property(src['source_id'], 'freezed', str_props ) end end end end end def self.processIndexes(index_param, src_name, is_unique) return "" unless index_param strUnique = 'UNIQUE' if is_unique strRes = "" if index_param.is_a?( String ) strRes = index_param else nInd = 0 index_param.each do |index_name, index_cols| strCols = "" if index_cols.is_a?(String) index_cols = index_cols.split(',') end index_cols.each do |col| strCols += ',' if strCols.length() > 0 strCols += "\"#{col}\"" end #strIndName = "rhoIndex" + (is_unique ? "U" : "" ) + "_#{nInd}" strIndex = "CREATE #{strUnique} INDEX \"#{index_name}\" ON \"#{src_name}\" (#{strCols});\r\n" strRes += strIndex nInd += 1 end end strRes end def self.init_schema_sources(hash_migrate) uniq_sources = Rho::RhoConfig::sources.values puts 'init_schema_sources' ::Rho::RHO.get_db_partitions().each do |partition, db| db.start_transaction begin init_schema_sources_partition(uniq_sources, hash_migrate, partition, db) db.commit rescue Exception => e trace_msg = e.backtrace.join("\n") puts "exception when init_schema_sources: #{e}; Trace:" + trace_msg db.rollback end end puts 'END init_schema_sources' end def self.init_schema_sources_partition(uniq_sources, hash_migrate, partition, db) uniq_sources.each do |source| next unless partition == source['partition'] next unless source['schema'] call_migrate = false if db.table_exist?(source['name']) next unless hash_migrate.has_key?(source['name']) call_migrate = true end strCreate =nil if source['schema'].is_a?(String) strCreate = source['schema'] else raise ArgumentError, "schema parameter should be Hash!" unless source['schema'].is_a?(Hash) strCreate = make_createsql_script(source['name'], source['schema']) end #puts "source['schema'] : #{source['schema']}" #hashSchema = Rho::JSON.parse(source['schema']) #puts "hashSchema : #{hashSchema}" #src['schema'] = hashSchema #source['schema']['sql'] = strCreate #src['schema_version'] = hashSchema['version'] #strCreate = source['schema'] #puts "strCreate: #{strCreate}" if call_migrate db.update_into_table('sources', {"schema"=>strCreate},{"name"=>source['name']}) #::Rho::RHO.get_user_db().update_into_table('sources', {"schema"=>strCreate},{"name"=>source['name']}) if db != ::Rho::RHO.get_user_db() source['migrate_version'] = hash_migrate[ source['name'] ] source['schema']['sql'] = strCreate else db.execute_batch_sql(strCreate) db.update_into_table('sources', {"schema"=>strCreate, "schema_version"=>source['schema_version']},{"name"=>source['name']}) #::Rho::RHO.get_user_db().update_into_table('sources', {"schema"=>strCreate, "schema_version"=>source['schema_version']},{"name"=>source['name']}) if db != ::Rho::RHO.get_user_db() end end System.update_blob_attribs(partition, -1 ) end def self.make_createsql_script(name,schema_attr) strCreate = schema_attr['sql'] strCreate = "" unless strCreate if schema_attr['property'] arCols = schema_attr['property'] puts " we are here in arcols : #{arCols.class.name}, #{arCols.inspect}" strCols = arCols.collect do |col, type| "\"#{col}\" " + case type[0] when :integer 'integer' when :float 'float' when :date 'integer' when :time 'integer' else 'varchar' end + " default null" end.join(',') strCols += ',' if strCols.length() > 0 strCols += "\"object\" varchar(255) PRIMARY KEY" strCreate = "CREATE TABLE \"#{name}\" ( #{strCols} )" end strCreate += ";\r\n" if strCreate && strCreate.length() > 0 strCreate += processIndexes(schema_attr['index'], name, false) strCreate += ";\r\n" if strCreate && strCreate.length() > 0 strCreate += processIndexes(schema_attr['unique_index'], name, true) strCreate end def process_blob_attribs(source, db) return source['str_blob_attribs'] if source['str_blob_attribs'] source['str_blob_attribs'] = "" if source['schema'] props = source['schema']['property'] else props = source['property'] end if props str = "" props.each do |name, ar_type| if ar_type && ar_type.is_a?(String) ar_type = ar_type.split(',') end if ar_type && ar_type[0].to_s == 'blob' str += ',' if str.length()>0 str += name.to_s() + ',' str += (ar_type[1].to_s() == 'overwrite' ? '1' : '0') end end source['str_blob_attribs'] = str end source['str_blob_attribs'] end def get_start_id(db_sources, db_partition) start_id = 0 db_sources.each do |db_src| next unless db_partition == db_src['partition'] src_id = db_src['source_id'] start_id = src_id if src_id > start_id end if start_id < Rho::RhoConfig.max_config_srcid()[db_partition] start_id = Rho::RhoConfig.max_config_srcid()[db_partition]+2 else start_id += 1 end puts "start_id: #{start_id}" start_id end def find_src_byname(db_sources, name) db_sources.each do |db_src| return db_src if db_src['name'] == name end nil end def init_db_sources(db, uniq_sources, db_partition, hash_migrate) puts "init_db_sources" db_sources = db.select_from_table('sources','sync_priority,source_id,partition, sync_type, schema_version, associations, source_attribs, blob_attribs, name') start_id = get_start_id(db_sources, db_partition) uniq_sources.each do |source| partition = source['partition'] next unless partition == db_partition #puts "init_db_sources(#{source['name']}) : #{source}" name = source['name'] sync_priority = source['sync_priority'] sync_type = source['sync_type'] schema_version = source['schema_version'] associations = source['str_associations'] blob_attribs = process_blob_attribs(source, db) # TODO: the same for :full_update option source_attribs = '' if source['pass_through'] source_attribs = 'pass_through' puts "init_db_sources: store :pass_through option into sources table for #{name} model" end # TODO: the same for :full_update option #puts "blob_attribs : #{blob_attribs}" #attribs = db.select_from_table('sources','sync_priority,source_id,partition, sync_type, schema_version, associations, blob_attribs', {'name'=>name}) attribs = find_src_byname(db_sources,name) if attribs if attribs['sync_priority'].to_i != sync_priority.to_i db.update_into_table('sources', {"sync_priority"=>sync_priority},{"name"=>name}) end if attribs['sync_type'] != sync_type db.update_into_table('sources', {"sync_type"=>sync_type},{"name"=>name}) end if attribs['schema_version'] != schema_version if db_partition == partition hash_migrate[name] = attribs['schema_version'] else db.update_into_table('sources', {"schema_version"=>schema_version},{"name"=>name}) end end if attribs['partition'] != partition db.update_into_table('sources', {"partition"=>partition},{"name"=>name}) end if attribs['associations'] != associations db.update_into_table('sources', {"associations"=>associations},{"name"=>name}) end if attribs['blob_attribs'] != blob_attribs db.update_into_table('sources', {"blob_attribs"=>blob_attribs},{"name"=>name}) end if attribs['source_attribs'] != source_attribs db.update_into_table('sources', {"source_attribs" => source_attribs},{"name" => name}) end if !source['source_id'] source['source_id'] = attribs['source_id'].to_i Rho::RhoConfig::sources[name]['source_id'] = attribs['source_id'].to_i end else #puts "Rho::RhoConfig.use_bulk_model : #{Rho::RhoConfig.use_bulk_model}" if Rho::RhoConfig.use_bulk_model.to_s != 'true' && Rho::RhoConfig.use_bulk_model.to_s != '1' if !source['source_id'] source['source_id'] = start_id Rho::RhoConfig::sources[name]['source_id'] = start_id start_id += 1 end db.insert_into_table('sources', {"source_id"=>source['source_id'],"name"=>name, "sync_priority"=>sync_priority, "sync_type"=>sync_type, "partition"=>partition, "schema_version"=>source['schema_version'], 'associations'=>associations, 'blob_attribs'=>blob_attribs, 'source_attribs' => source_attribs}) end end end end def serve(req) begin RhoProfiler.start_counter('CTRL_ACTION') res = init_response if (req['proc']) @params = RhoSupport::query_params req RhoController.process_rho_object(@params) req['proc'].call( @params['body'] ) else puts "RHO serve: " + (req ? "#{req['request-uri']}" : '') get_app(req['application']).send :serve, req, res init_nativebar Rho::RhoController.clean_cached_metadata() Rho::RhoConfig.clean_cached_changed end ret = send_response(res) RhoProfiler.stop_counter('CTRL_ACTION') return ret rescue Exception => e ret = send_error(e) RhoProfiler.stop_counter('CTRL_ACTION') return ret ensure RhoApplication::set_current_controller(nil) end end def serve_hash(req) begin RhoProfiler.start_counter('CTRL_ACTION') puts "RHO serve: " + (req ? "#{req['request-uri']}" : '') res = init_response get_app(req['application']).send :serve, req, res init_nativebar Rho::RhoController.clean_cached_metadata() Rho::RhoConfig.clean_cached_changed ret = send_response_hash(res) RhoProfiler.stop_counter('CTRL_ACTION') return ret rescue Exception => e ret = send_error(e,500,true) RhoProfiler.stop_counter('CTRL_ACTION') return ret ensure RhoApplication::set_current_controller(nil) end end def serve_index(index_name, req) begin RhoProfiler.start_counter('INDEX_ACTION') # TODO: Removed hardcoded appname get_app(APPNAME).set_menu puts "RHO serve_index: " + (req ? "#{req['request-uri']}" : '') res = init_response res['request-body'] = RhoController::renderfile(index_name, req, res) init_nativebar Rho::RhoController.clean_cached_metadata() Rho::RhoConfig.clean_cached_changed ret = send_response(res) RhoProfiler.stop_counter('INDEX_ACTION') return ret rescue Exception => e ret = send_error(e) RhoProfiler.stop_counter('INDEX_ACTION') return ret end end def serve_index_hash(index_name, req) begin RhoProfiler.start_counter('INDEX_ACTION') # TODO: Removed hardcoded appname get_app(APPNAME).set_menu puts "RHO serve_index: " + (req ? "#{req['request-uri']}" : '') res = init_response res['request-body'] = RhoController::renderfile(index_name, req, res) init_nativebar Rho::RhoController.clean_cached_metadata() Rho::RhoConfig.clean_cached_changed ret = send_response_hash(res) RhoProfiler.stop_counter('INDEX_ACTION') return ret rescue Exception => e ret = send_error(e, 500, true) RhoProfiler.stop_counter('INDEX_ACTION') return ret end end def make_httpdate(t) _RFC2822_DAY_NAME = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ] _RFC2822_MONTH_NAME = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] sprintf('%s, %02d %s %d %02d:%02d:%02d GMT', _RFC2822_DAY_NAME[t.wday], t.day, _RFC2822_MONTH_NAME[t.mon-1], t.year, t.hour, t.min, t.sec) end def init_response(status=200,message="OK",body="") res = Hash.new res['status'] = status res['message'] = message res['headers'] = { 'Date' => make_httpdate(Time.now),#.httpdate, 'Content-Type' => 'text/html; charset=utf-8', 'Content-Length' => 0, 'Connection' => 'close' } res['request-body'] = body res end CR = "\x0d" LF = "\x0a" CRLF = "\x0d\x0a" def send_response(res) res['headers']['Content-Length'] = !res['request-body'].nil? && res['request-body'].is_a?(String) ? res['request-body'].bytesize : 0 data = "HTTP/1.1 #{res['status'].to_s} #{res['message']}" + CRLF res['headers'].each{|key, value| tmp = key.gsub(/\bwww|^te$|\b\w/){|s| s.upcase } data << "#{tmp}: #{value}" << CRLF } data << "Pragma: no-cache" << CRLF data << "Cache-Control: must-revalidate" << CRLF data << "Cache-Control: no-cache" << CRLF data << "Cache-Control: no-store" << CRLF data << "Expires: 0" << CRLF data << CRLF if ( !res['request-body'].nil? && res['request-body'].is_a?(String)) data << res['request-body'] end data end def send_response_hash(res) resp = Hash.new res['headers']['Content-Length'] = !res['request-body'].nil? && res['request-body'].is_a?(String) ? res['request-body'].bytesize : 0 res['headers'].each{|key, value| tmp = key.gsub(/\bwww|^te$|\b\w/){|s| s.upcase } resp[tmp] = value } resp['request-body'] = !res['request-body'].nil? && res['request-body'].is_a?(String) ? res['request-body'] : "" resp['status'] = res['status'] resp['message'] = res['message'] resp end @@current_exception = nil def self.current_exception @@current_exception end def send_error(exception=nil,status=500,hash=false) if exception trace_msg = exception.backtrace.join("\n") puts "App error: #{exception.message}\n #{trace_msg}" end body='' err_page = nil if exception && exception.is_a?(::Rhom::RecordNotFound) err_page = RhoApplication::get_app_path(APPNAME) + 'E400' + RHO_ERB_EXT err_page = nil unless ::Rho::file_exist?(err_page) elsif exception err_page = RhoApplication::get_app_path(APPNAME) + 'E500' + RHO_ERB_EXT err_page = nil unless ::Rho::file_exist?(err_page) end if err_page @@current_exception = exception puts 'show error: ' + @@current_exception.inspect body = RhoController::renderfile(err_page) return send_response_hash(init_response(200,"OK",body)) if hash return send_response(init_response(200,"OK",body)) end body << <<-_HTML_STRING_
_HTML_STRING_
body << 'Error: ' << exception.message << "
" if exception
body << "Trace:
\n" << exception.backtrace.join("
\n") if exception
body << <<-_HTML_STRING_