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.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +39 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/bin/check_running.rb +71 -0
- data/bin/knjappserver_start.rb +50 -0
- data/knjappserver.gemspec +107 -0
- data/lib/conf/README +1 -0
- data/lib/conf/conf_example.rb +109 -0
- data/lib/conf/conf_vars_example.rb +3 -0
- data/lib/files/database_schema.rb +111 -0
- data/lib/files/run/README +1 -0
- data/lib/include/class_customio.rb +21 -0
- data/lib/include/class_erbhandler.rb +36 -0
- data/lib/include/class_httpresp.rb +91 -0
- data/lib/include/class_httpserver.rb +91 -0
- data/lib/include/class_httpsession.rb +350 -0
- data/lib/include/class_httpsession_knjengine.rb +189 -0
- data/lib/include/class_httpsession_mongrel.rb +75 -0
- data/lib/include/class_httpsession_webrick.rb +75 -0
- data/lib/include/class_knjappserver.rb +455 -0
- data/lib/include/class_knjappserver_cleaner.rb +109 -0
- data/lib/include/class_knjappserver_errors.rb +117 -0
- data/lib/include/class_knjappserver_logging.rb +272 -0
- data/lib/include/class_knjappserver_mailing.rb +97 -0
- data/lib/include/class_knjappserver_threadding.rb +87 -0
- data/lib/include/class_knjappserver_web.rb +23 -0
- data/lib/include/class_log.rb +81 -0
- data/lib/include/class_log_access.rb +103 -0
- data/lib/include/class_log_data.rb +42 -0
- data/lib/include/class_log_data_link.rb +16 -0
- data/lib/include/class_log_data_value.rb +34 -0
- data/lib/include/class_log_link.rb +51 -0
- data/lib/include/class_session.rb +43 -0
- data/lib/include/gettext_funcs.rb +10 -0
- data/lib/include/magic_methods.rb +59 -0
- data/lib/knjappserver.rb +1 -0
- data/lib/pages/logs_latest.rhtml +57 -0
- data/lib/pages/logs_show.rhtml +32 -0
- data/lib/pages/spec.rhtml +10 -0
- data/spec/knjappserver_spec.rb +110 -0
- data/spec/spec_helper.rb +12 -0
- 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
|