knjappserver 0.0.6

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 (47) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +18 -0
  4. data/Gemfile.lock +39 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +19 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/bin/check_running.rb +71 -0
  10. data/bin/knjappserver_start.rb +50 -0
  11. data/knjappserver.gemspec +107 -0
  12. data/lib/conf/README +1 -0
  13. data/lib/conf/conf_example.rb +109 -0
  14. data/lib/conf/conf_vars_example.rb +3 -0
  15. data/lib/files/database_schema.rb +111 -0
  16. data/lib/files/run/README +1 -0
  17. data/lib/include/class_customio.rb +21 -0
  18. data/lib/include/class_erbhandler.rb +36 -0
  19. data/lib/include/class_httpresp.rb +91 -0
  20. data/lib/include/class_httpserver.rb +91 -0
  21. data/lib/include/class_httpsession.rb +350 -0
  22. data/lib/include/class_httpsession_knjengine.rb +189 -0
  23. data/lib/include/class_httpsession_mongrel.rb +75 -0
  24. data/lib/include/class_httpsession_webrick.rb +75 -0
  25. data/lib/include/class_knjappserver.rb +455 -0
  26. data/lib/include/class_knjappserver_cleaner.rb +109 -0
  27. data/lib/include/class_knjappserver_errors.rb +117 -0
  28. data/lib/include/class_knjappserver_logging.rb +272 -0
  29. data/lib/include/class_knjappserver_mailing.rb +97 -0
  30. data/lib/include/class_knjappserver_threadding.rb +87 -0
  31. data/lib/include/class_knjappserver_web.rb +23 -0
  32. data/lib/include/class_log.rb +81 -0
  33. data/lib/include/class_log_access.rb +103 -0
  34. data/lib/include/class_log_data.rb +42 -0
  35. data/lib/include/class_log_data_link.rb +16 -0
  36. data/lib/include/class_log_data_value.rb +34 -0
  37. data/lib/include/class_log_link.rb +51 -0
  38. data/lib/include/class_session.rb +43 -0
  39. data/lib/include/gettext_funcs.rb +10 -0
  40. data/lib/include/magic_methods.rb +59 -0
  41. data/lib/knjappserver.rb +1 -0
  42. data/lib/pages/logs_latest.rhtml +57 -0
  43. data/lib/pages/logs_show.rhtml +32 -0
  44. data/lib/pages/spec.rhtml +10 -0
  45. data/spec/knjappserver_spec.rb +110 -0
  46. data/spec/spec_helper.rb +12 -0
  47. metadata +188 -0
@@ -0,0 +1,455 @@
1
+ require "#{File.dirname(__FILE__)}/class_knjappserver_errors"
2
+ require "#{File.dirname(__FILE__)}/class_knjappserver_logging"
3
+ require "#{File.dirname(__FILE__)}/class_knjappserver_mailing"
4
+ require "#{File.dirname(__FILE__)}/class_knjappserver_threadding"
5
+ require "#{File.dirname(__FILE__)}/class_knjappserver_web"
6
+ require "#{File.dirname(__FILE__)}/class_knjappserver_cleaner"
7
+
8
+ require "timeout"
9
+ require "digest"
10
+
11
+ class Knjappserver
12
+ attr_reader :config, :httpserv, :db, :db_handler, :ob, :translations, :paused, :should_restart, :events, :mod_event, :paused, :db_handler, :gettext, :sessions, :logs_access_pending, :threadpool, :vars, :magic_vars, :types, :eruby_cache
13
+ attr_accessor :served, :should_restart, :should_restart_done
14
+
15
+ autoload :ERBHandler, "#{File.dirname(__FILE__)}/class_erbhandler"
16
+
17
+ def initialize(config)
18
+ @config = {
19
+ :timeout => 30,
20
+ :default_page => "index.rhtml",
21
+ :default_filetype => "text/html",
22
+ :max_requests_working => 20
23
+ }.merge(config)
24
+
25
+ @config[:timeout] = 30 if !@config.has_key?(:timeout)
26
+ @config[:engine_knjengine] = true if !@config[:engine_knjengine] and !@config[:engine_webrick] and !@config[:engine_mongrel]
27
+ raise "No ':doc_root' was given in arguments." if !@config.has_key?(:doc_root)
28
+
29
+ if !@config.has_key?(:handlers)
30
+ @erbhandler = Knjappserver::ERBHandler.new
31
+ @config[:handlers] = [
32
+ {
33
+ :file_ext => "rhtml",
34
+ :callback => @erbhandler.method(:erb_handler)
35
+ },{
36
+ :path => "/fckeditor",
37
+ :mount => "/usr/share/fckeditor"
38
+ }
39
+ ]
40
+ end
41
+
42
+ @paused = 0
43
+ @paused_mutex = Mutex.new
44
+ @should_restart = false
45
+ @mod_events = {}
46
+ @served = 0
47
+ @mod_files = {}
48
+ @sessions = {}
49
+ @eruby_cache = {}
50
+
51
+ @path_knjappserver = File.dirname(__FILE__)
52
+ if @config[:knjrbfw_path]
53
+ @path_knjrbfw = @config[:knjrbfw_path]
54
+ elsif $knjappserver_config and $knjappserver_config["knjrbfw"]
55
+ @path_knjrbfw = $knjappserver_config["knjrbfw"]
56
+ else
57
+ @path_knjrbfw = ""
58
+ end
59
+
60
+
61
+ #If auto-restarting is enabled - start the modified events-module.
62
+ if @config[:autorestart]
63
+ paths = [
64
+ "#{@path_knjappserver}/../knjappserver.rb",
65
+ "#{@path_knjappserver}/class_knjappserver.rb",
66
+ "#{@path_knjappserver}/class_customio.rb"
67
+ ]
68
+
69
+ print "Auto restarting.\n" if @config[:debug]
70
+ @mod_event = Knj::Event_filemod.new(:wait => 2, :paths => paths) do |event, path|
71
+ print "File changed - restart server: #{path}\n"
72
+ @should_restart = true
73
+ @mod_event.destroy if @mod_event
74
+ end
75
+ end
76
+
77
+ @types = {
78
+ :ico => "image/x-icon",
79
+ :jpeg => "image/jpeg",
80
+ :jpg => "image/jpeg",
81
+ :gif => "image/gif",
82
+ :png => "image/png",
83
+ :html => "text/html",
84
+ :htm => "text/html",
85
+ :rhtml => "text/html",
86
+ :css => "text/css",
87
+ :xml => "text/xml",
88
+ :js => "text/javascript"
89
+ }
90
+ @types = @types.merge(@config[:filetypes]) if @config[:filetypes]
91
+
92
+
93
+ files = [
94
+ "#{@path_knjappserver}/class_httpresp.rb",
95
+ "#{@path_knjappserver}/class_httpserver.rb",
96
+ "#{@path_knjappserver}/class_httpsession.rb",
97
+ "#{@path_knjappserver}/class_session.rb",
98
+ "#{@path_knjappserver}/class_log.rb",
99
+ "#{@path_knjappserver}/class_log_access.rb",
100
+ "#{@path_knjappserver}/class_log_data_value.rb",
101
+ "#{@path_knjrbfw}knjrbfw.rb",
102
+ "#{@path_knjrbfw}knj/objects.rb",
103
+ "#{@path_knjrbfw}knj/web.rb",
104
+ "#{@path_knjrbfw}knj/datet.rb",
105
+ "#{@path_knjrbfw}knj/thread.rb",
106
+ "#{@path_knjrbfw}knj/threadhandler.rb",
107
+ "#{@path_knjrbfw}knj/knjdb/libknjdb.rb"
108
+ ]
109
+ files.each do |file|
110
+ STDOUT.print "Loading: '#{file}'.\n" if @config[:debug]
111
+
112
+ if @config[:autorestart]
113
+ self.loadfile(file)
114
+ else
115
+ require file
116
+ end
117
+ end
118
+
119
+
120
+ print "Setting up database.\n" if @config[:debug]
121
+ if @config[:db].is_a?(Knj::Db)
122
+ @db = @config[:db]
123
+ elsif @config[:db].is_a?(Hash)
124
+ @db = Knj::Db.new(@config[:db])
125
+ else
126
+ raise "Unknown object given as db: '#{@config[:db].class.name}'."
127
+ end
128
+
129
+
130
+ print "Starting objects.\n" if @config[:debug]
131
+ @ob = Knj::Objects.new(
132
+ :db => db,
133
+ :class_path => @path_knjappserver,
134
+ :module => Knjappserver,
135
+ :datarow => true,
136
+ :knjappserver => self
137
+ )
138
+ @ob.events.connect(:no_date) do |event, classname|
139
+ "[no date]"
140
+ end
141
+
142
+
143
+ if @config[:httpsession_db_args]
144
+ @db_handler = Knj::Db.new(@config[:httpsession_db_args])
145
+ else
146
+ @db_handler = @db
147
+ end
148
+
149
+
150
+ #Start the Knj::Gettext_threadded- and Knj::Translations modules for translations.
151
+ print "Loading Gettext and translations.\n" if @config[:debug]
152
+ @translations = Knj::Translations.new(:db => @db)
153
+ if @config[:locales_root]
154
+ @gettext = Knj::Gettext_threadded.new("dir" => config[:locales_root])
155
+ end
156
+
157
+ if @config[:locales_gettext_funcs]
158
+ require "#{@path_knjappserver}/gettext_funcs"
159
+ end
160
+
161
+ if @config[:magic_methods] or !@config.has_key?(:magic_methods)
162
+ print "Loading magic-methods.\n" if @config[:debug]
163
+ require "#{@path_knjappserver}/magic_methods"
164
+ end
165
+
166
+ if @config[:customio] or !@config.has_key?(:customio)
167
+ print "Loading custom-io.\n" if @config[:debug]
168
+ require "#{@path_knjappserver}/class_customio.rb"
169
+ cio = Knjappserver::CustomIO.new
170
+ $stdout = cio
171
+ end
172
+
173
+
174
+ #Save the PID to the run-file.
175
+ print "Setting run-file.\n" if @config[:debug]
176
+ require "tmpdir"
177
+ tmpdir = "#{Dir.tmpdir}/knjappserver"
178
+ tmppath = "#{tmpdir}/run_#{@config[:title]}"
179
+
180
+ Dir.mkdir(tmpdir) if !File.exists?(tmpdir)
181
+ File.open(tmppath, "w") do |fp|
182
+ fp.write(Process.pid)
183
+ end
184
+
185
+
186
+ #Set up various events for the appserver.
187
+ print "Loading events.\n" if @config[:debug]
188
+ @events = Knj::Event_handler.new
189
+ @events.add_event(
190
+ :name => :check_page_access,
191
+ :connections_max => 1
192
+ )
193
+ @events.add_event(
194
+ :name => :ob,
195
+ :connections_max => 1
196
+ )
197
+
198
+
199
+ #Set up the 'vars'-variable that can be used to set custom global variables for web-requests.
200
+ @vars = Knj::Hash_methods.new
201
+ @magic_vars = {}
202
+
203
+
204
+ #Initialize the various feature-modules.
205
+ print "Init threadding.\n" if @config[:debug]
206
+ initialize_threadding
207
+
208
+ print "Init mailing.\n" if @config[:debug]
209
+ initialize_mailing
210
+
211
+ print "Init errors.\n" if @config[:debug]
212
+ initialize_errors
213
+
214
+ print "Init logging.\n" if @config[:debug]
215
+ initialize_logging
216
+
217
+ print "Init cleaner.\n" if @config[:debug]
218
+ initialize_cleaner
219
+
220
+
221
+ #Start the appserver.
222
+ print "Spawning appserver.\n" if @config[:debug]
223
+ @httpserv = Knjappserver::Httpserver.new(self)
224
+
225
+
226
+ #Clear memory at exit.
227
+ at_exit do
228
+ self.stop
229
+ end
230
+
231
+
232
+ print "Appserver spawned.\n" if @config[:debug]
233
+ end
234
+
235
+ def loadfile(fpath)
236
+ if !@config[:autorestart]
237
+ require fpath
238
+ return nil
239
+ end
240
+
241
+ rpath = Knj::Php.realpath(fpath)
242
+ raise "No such filepath: #{fpath}" if !rpath or !File.exists?(rpath)
243
+
244
+ return true if @mod_files[rpath]
245
+
246
+ @mod_event.args[:paths] << rpath
247
+ @mod_files = rpath
248
+
249
+ require rpath
250
+ return false
251
+ end
252
+
253
+ def start
254
+ print "Starting appserver.\n" if @config[:debug]
255
+ Thread.current[:knjappserver] = {:kas => self} if !Thread.current[:knjappserver]
256
+
257
+ if @config[:autoload]
258
+ print "Autoloading #{@config[:autoload]}\n" if @config[:debug]
259
+ require @config[:autoload]
260
+ end
261
+
262
+ begin
263
+ @threadpool.start if @threadpool
264
+ print "Threadpool startet.\n" if @config[:debug]
265
+ @httpserv.start
266
+ print "Appserver startet.\n" if @config[:debug]
267
+ rescue Interrupt
268
+ print "Got interrupt - stopping appserver.\n" if @config[:debug]
269
+ stop
270
+ end
271
+ end
272
+
273
+ def stop
274
+ proc_stop = proc{
275
+ print "Stopping appserver for real.\n" if @config[:debug]
276
+ @httpserv.stop if @httpserv and @httpserv.respond_to?(:stop)
277
+
278
+ print "Stopping threadpool.\n" if @config[:debug]
279
+ @threadpool.stop if @threadpool
280
+
281
+ print "Cleaning out loaded sessions.\n" if @config[:debug]
282
+ if @sessions
283
+ @sessions.each do |ip, ip_sessions|
284
+ ip_sessions.each do |session_hash, session_data|
285
+ session_data[:dbobj].flush
286
+ @ob.unset(session_data[:dbobj])
287
+ session_data[:hash].clear
288
+ ip_sessions.delete(session_hash)
289
+ session_data.clear
290
+ end
291
+ end
292
+ @sessions.clear
293
+ end
294
+
295
+ print "Stopping databases.\n" if @config[:debug]
296
+ @db.destroy if @db.is_a?(Knj::Threadhandler)
297
+ @db.close if @db.is_a?(Knj::Db)
298
+
299
+ @db_handler.destroy if @db.is_a?(Knj::Threadhandler)
300
+ @db_handler.close if @db_handler.is_a?(Knj::Db)
301
+ }
302
+
303
+ #If we cant get a paused-execution in 10 secs - we just force the stop.
304
+ begin
305
+ Timeout.timeout(10) do
306
+ self.paused_exec do
307
+ proc_stop.call
308
+ end
309
+ end
310
+ rescue Timeout::Error
311
+ proc_stop.call
312
+ end
313
+ end
314
+
315
+ # Stop running any more http requests - make them wait.
316
+ def pause
317
+ @paused += 1
318
+ end
319
+
320
+ def unpause
321
+ @paused -= 1
322
+ end
323
+
324
+ def paused?
325
+ return true if @paused > 0
326
+ return false
327
+ end
328
+
329
+ def paused_exec
330
+ self.pause
331
+
332
+ begin
333
+ loop do
334
+ if @httpserv.working_count > 0
335
+ sleep 0.2
336
+ next
337
+ end
338
+
339
+ @paused_mutex.synchronize do
340
+ yield
341
+ end
342
+
343
+ break
344
+ end
345
+ ensure
346
+ self.unpause
347
+ end
348
+ end
349
+
350
+ def working?
351
+ return true if @httpserv and @httpserv.working_count > 0
352
+ return false
353
+ end
354
+
355
+ def self.data
356
+ raise "Could not register current thread." if !Thread.current[:knjappserver]
357
+ return Thread.current[:knjappserver]
358
+ end
359
+
360
+ def session_fromid(args)
361
+ ip = args[:ip].to_s
362
+ idhash = args[:idhash].to_s
363
+ ip = "bot" if idhash == "bot"
364
+
365
+ @sessions[ip] = {} if !@sessions.has_key?(ip)
366
+
367
+ if !@sessions[ip].has_key?(idhash)
368
+ session = @ob.get_by(:Session, {"idhash" => args[:idhash]})
369
+ if !session
370
+ session = @ob.add(:Session, {
371
+ :idhash => idhash,
372
+ :ip => ip
373
+ })
374
+ end
375
+
376
+ @sessions[ip][idhash] = {
377
+ :dbobj => session,
378
+ :hash => {}
379
+ }
380
+ end
381
+
382
+ @sessions[ip][idhash][:time_lastused] = Time.now
383
+ return @sessions[ip][idhash]
384
+ end
385
+
386
+ def trans(obj, key)
387
+ args = {}
388
+ args[:locale] = _session[:locale] if _session[:locale] and !args[:locale]
389
+ args[:locale] = _httpsession.data[:locale] if _httpsession.data[:locale] and !args[:locale]
390
+ @translations.get(obj, key, args)
391
+ end
392
+
393
+ def trans_set(obj, values)
394
+ args = {}
395
+ args[:locale] = _session[:locale] if _session[:locale] and !args[:locale]
396
+ args[:locale] = _httpsession.data[:locale] if _httpsession.data[:locale] and !args[:locale]
397
+ @translations.set(obj, values, args)
398
+ end
399
+
400
+ def trans_del(obj)
401
+ @translations.delete(obj)
402
+ end
403
+
404
+ def import(filepath)
405
+ _httpsession.eruby.import(filepath)
406
+ end
407
+
408
+ def update_db
409
+ require "rubygems"
410
+ require "#{@config[:knjdbrevision_path]}knjdbrevision"
411
+
412
+ dbschemapath = "#{File.dirname(__FILE__)}/../files/database_schema.rb"
413
+ raise "'#{dbschemapath}' did not exist." if !File.exists?(dbschemapath)
414
+ require dbschemapath
415
+ raise "No schema-variable was spawned." if !$tables
416
+
417
+ dbpath = "#{File.dirname(__FILE__)}/../files/database.sqlite3"
418
+ dbrev = Knjdbrevision.new
419
+ dbrev.init_db($tables, @db)
420
+ end
421
+
422
+ def join
423
+ raise "No http-server or http-server not running." if !@httpserv or !@httpserv.thread_accept
424
+
425
+ begin
426
+ @httpserv.thread_accept.join
427
+ @httpserv.thread_restart.join
428
+ rescue Interrupt
429
+ stop
430
+ end
431
+
432
+ if @should_restart
433
+ loop do
434
+ if @should_restart_done
435
+ STDOUT.print "Ending join because the restart is done.\n"
436
+ break
437
+ end
438
+
439
+ sleep 1
440
+ end
441
+ end
442
+ end
443
+
444
+ def define_magic_var(method_name, var)
445
+ @magic_vars[method_name] = var
446
+
447
+ if !Object.respond_to?(method_name)
448
+ Object.send(:define_method, method_name) do
449
+ return Thread.current[:knjappserver][:kas].magic_vars[method_name] if Thread.current[:knjappserver] and Thread.current[:knjappserver][:kas]
450
+ return $knjappserver[:knjappserver].magic_vars[method_name] if $knjappserver and $knjappserver[:knjappserver]
451
+ raise "Could not figure out the object: '#{method_name}'."
452
+ end
453
+ end
454
+ end
455
+ end
@@ -0,0 +1,109 @@
1
+ class Knjappserver
2
+ def initialize_cleaner
3
+ if @config[:autorestart]
4
+ time = 1
5
+ else
6
+ time = 15
7
+ end
8
+
9
+ #This should not be runned via _kas.timeout because timeout wont run when @should_restart is true! - knj
10
+ Knj::Thread.new do
11
+ loop do
12
+ sleep time
13
+
14
+ if @config.has_key?(:restart_when_used_memory) and !@should_restart
15
+ mbs_used = (Knj::Php.memory_get_usage / 1024) / 1024
16
+ #STDOUT.print "Used: #{mbs_used}\n"
17
+
18
+ if mbs_used.to_i >= @config[:restart_when_used_memory].to_i
19
+ STDOUT.print "Memory is over #{@config[:restart_when_used_memory]} - restarting.\n"
20
+ @should_restart = true
21
+ end
22
+ end
23
+
24
+ if @should_restart and !@should_restart_done and !@should_restart_runnning
25
+ begin
26
+ @should_restart_runnning = true
27
+
28
+ #When we begin to restart it should go as fast as possible - so start by flushing out any emails waiting...
29
+ STDOUT.print "Flushing mails.\n"
30
+ self.mail_flush
31
+
32
+ #Lets try to find a time where no thread is working within the next 30 seconds. If we cant - we interrupt after 10 seconds and restart the server.
33
+ begin
34
+ Timeout.timeout(30) do
35
+ loop do
36
+ working_count = self.httpserv.working_count
37
+ working = false
38
+
39
+ if working_count > 0
40
+ working = true
41
+ STDOUT.print "Someone is working - wait two sec and try to restart again!\n"
42
+ end
43
+
44
+ if !working
45
+ STDOUT.print "Found window where no sessions were active - restarting!\n"
46
+ break
47
+ else
48
+ sleep 0.2
49
+ end
50
+
51
+ STDOUT.print "Trying to find window with no active sessions to restart...\n"
52
+ end
53
+ end
54
+ rescue Timeout::Error
55
+ STDOUT.print "Could not find a timing window for restarting... Forcing restart!\n"
56
+ end
57
+
58
+ #Flush emails again if any are pending (while we tried to find a window to restart)...
59
+ STDOUT.print "Flushing mails.\n"
60
+ self.mail_flush
61
+
62
+ STDOUT.print "Stopping appserver.\n"
63
+ self.stop
64
+
65
+ STDOUT.print "Figuring out restart-command.\n"
66
+ mycmd = @config[:restart_cmd]
67
+
68
+ if !mycmd or mycmd.to_s.strip.length <= 0
69
+ fpath = Knj::Php.realpath(File.dirname(__FILE__) + "/../knjappserver.rb")
70
+ mycmd = Knj::Os.executed_cmd
71
+
72
+ STDOUT.print "Previous cmd: #{mycmd}\n"
73
+ mycmd = mycmd.gsub(/\s+knjappserver.rb/, " #{Knj::Strings.unixsafe(fpath)}")
74
+ end
75
+
76
+ STDOUT.print "Restarting knjAppServer with command: #{mycmd}\n"
77
+ @should_restart_done = true
78
+ print exec(mycmd)
79
+ exit
80
+ rescue Exception => e
81
+ STDOUT.puts e.inspect
82
+ STDOUT.puts e.backtrace
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ self.timeout(:time => 300) do
89
+ STDOUT.print "Cleaning sessions on appserver.\n" if @config[:debug]
90
+
91
+ self.paused_exec do
92
+ time_check = Time.now.to_i - 300
93
+ @sessions.each do |ip, ip_sessions|
94
+ ip_sessions.each do |session_hash, session_data|
95
+ if session_data[:time_lastused].to_i <= time_check
96
+ session_data[:dbobj].flush
97
+ @ob.unset(session_data[:dbobj])
98
+ session_data[:hash].clear
99
+ ip_sessions.delete(session_hash)
100
+ session_data.clear
101
+ end
102
+ end
103
+
104
+ @sessions.delete(ip) if ip_sessions.empty?
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,117 @@
1
+ class Knjappserver
2
+ attr_reader :error_emails_pending
3
+
4
+ def initialize_errors
5
+ @error_emails_pending = {}
6
+ @error_emails_pending_mutex = Mutex.new
7
+
8
+ if @config[:error_emails_time]
9
+ @error_emails_time = @config[:error_emails_time]
10
+ elsif @config[:debug]
11
+ @error_emails_time = 5
12
+ else
13
+ @error_emails_time = 180
14
+ end
15
+
16
+ self.timeout(:time => @error_emails_time) do
17
+ self.flush_error_emails
18
+ end
19
+ end
20
+
21
+ def flush_error_emails
22
+ @error_emails_pending_mutex.synchronize do
23
+ send_time_older_than = Time.new.to_i - @error_emails_time
24
+
25
+ @error_emails_pending.each do |backtrace_hash, error_email|
26
+ if send_time_older_than < error_email[:last_time].to_i and error_email[:messages].length < 1000
27
+ next
28
+ end
29
+
30
+ @config[:error_report_emails].each do |email|
31
+ next if !email
32
+
33
+ if error_email[:messages].length == 1
34
+ html = error_email[:messages].first
35
+ else
36
+ html = "<b>First time:</b> #{Knj::Datet.in(error_email[:first_time]).out}<br />"
37
+ html += "<b>Last time:</b> #{Knj::Datet.in(error_email[:last_time]).out}<br />"
38
+ html += "<b>Number of errors:</b> #{error_email[:messages].length}<br />"
39
+ count = 0
40
+
41
+ error_email[:messages].each do |error_msg|
42
+ count += 1
43
+
44
+ if count > 10
45
+ html += "<br /><br /><b><i>Limiting to showing 10 out of #{error_email[:messages].length} messages.</i></b>"
46
+ break
47
+ end
48
+
49
+ html += "<br /><br />"
50
+ html += "<b>Message #{count}</b><br />"
51
+ html += error_msg
52
+ end
53
+ end
54
+
55
+ self.mail(
56
+ :to => email,
57
+ :subject => error_email[:subject],
58
+ :html => html,
59
+ :from => @config[:error_report_from]
60
+ )
61
+ end
62
+
63
+ @error_emails_pending.delete(backtrace_hash)
64
+ end
65
+ end
66
+ end
67
+
68
+ def handle_error(e, args = {})
69
+ @error_emails_pending_mutex.synchronize do
70
+ if !Thread.current[:knjappserver] or !Thread.current[:knjappserver][:httpsession]
71
+ STDOUT.print "Error: "
72
+ STDOUT.puts e.inspect
73
+ STDOUT.print "\n"
74
+ STDOUT.puts e.backtrace
75
+ STDOUT.print "\n\n"
76
+ end
77
+
78
+ if @config.has_key?(:smtp_args) and @config[:error_report_emails] and (!args.has_key?(:email) or args[:email])
79
+ backtrace_hash = Knj::ArrayExt.array_hash(e.backtrace)
80
+
81
+ if !@error_emails_pending.has_key?(backtrace_hash)
82
+ @error_emails_pending[backtrace_hash] = {
83
+ :first_time => Time.new,
84
+ :messages => [],
85
+ :subject => sprintf("Error @ %s", @config[:title]) + " (#{e.message})"
86
+ }
87
+ end
88
+
89
+ html = "An error occurred.<br /><br />"
90
+ html += "<b>#{e.class.name.html}: #{e.message.html}</b><br /><br />"
91
+
92
+ e.backtrace.each do |line|
93
+ html += line.html + "<br />"
94
+ end
95
+
96
+ html += "<br /><b>Post:</b><br /><pre>#{Knj::Php.print_r(_post, true)}</pre>" if _post
97
+ html += "<br /><b>Get:</b><br /><pre>#{Knj::Php.print_r(_get, true)}</pre>" if _get
98
+ html += "<br /><b>Server:</b><br /><pre>#{Knj::Php.print_r(_server, true).html}</pre>" if _server
99
+ html += "<br /><b>Cookie:</b><br /><pre>#{Knj::Php.print_r(_cookie, true).html}</pre>" if _meta
100
+ html += "<br /><b>Session:</b><br /><pre>#{Knj::Php.print_r(_session, true).html}</pre>" if _session
101
+ html += "<br /><b>Session hash:</b><br /><pre>#{Knj::Php.print_r(_session_hash, true).html}</pre>" if _session_hash
102
+
103
+ error_hash = @error_emails_pending[backtrace_hash]
104
+ error_hash[:last_time] = Time.new
105
+ error_hash[:messages] << html
106
+ end
107
+ end
108
+ end
109
+
110
+ def on_error_go_back(&block)
111
+ begin
112
+ block.call
113
+ rescue => e
114
+ self.alert(e.message).back
115
+ end
116
+ end
117
+ end