taskjuggler 3.1.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +44 -0
- data/bin/tj3webd +4 -0
- data/data/css/tjreport.css +14 -5
- data/data/tjp.vim +22 -7
- data/examples/Fedora-20/reports.tji +2 -4
- data/examples/Scrum/Product Burndown.csv +26 -0
- data/examples/Scrum/Sprint 1 Burndown.csv +26 -0
- data/examples/Scrum/Sprint 2 Burndown.csv +26 -0
- data/examples/Scrum/Sprint 3 Burndown.csv +26 -0
- data/examples/Scrum/scrum.tjp +141 -0
- data/examples/Tutorial/tutorial.tjp +13 -7
- data/lib/taskjuggler/Attributes.rb +2 -3
- data/lib/taskjuggler/HTMLDocument.rb +25 -18
- data/lib/taskjuggler/Journal.rb +85 -65
- data/lib/taskjuggler/KeywordDocumentation.rb +25 -13
- data/lib/taskjuggler/LeaveList.rb +1 -1
- data/lib/taskjuggler/Limits.rb +3 -5
- data/lib/taskjuggler/MessageHandler.rb +173 -19
- data/lib/taskjuggler/Painter.rb +75 -0
- data/lib/taskjuggler/Painter/BasicShapes.rb +76 -0
- data/lib/taskjuggler/Painter/Color.rb +273 -0
- data/lib/taskjuggler/Painter/Element.rb +56 -0
- data/lib/taskjuggler/Painter/FontData.rb +221 -0
- data/lib/taskjuggler/Painter/FontMetrics.rb +125 -0
- data/lib/taskjuggler/Painter/FontMetricsData.rb +151 -0
- data/lib/taskjuggler/Painter/Group.rb +77 -0
- data/lib/taskjuggler/Painter/Points.rb +47 -0
- data/lib/taskjuggler/Painter/Primitives.rb +100 -0
- data/lib/taskjuggler/Painter/SVGSupport.rb +36 -0
- data/lib/taskjuggler/Painter/Text.rb +36 -0
- data/lib/taskjuggler/Project.rb +46 -29
- data/lib/taskjuggler/ProjectFileParser.rb +24 -22
- data/lib/taskjuggler/ProjectFileScanner.rb +2 -2
- data/lib/taskjuggler/PropertyTreeNode.rb +26 -34
- data/lib/taskjuggler/Query.rb +8 -5
- data/lib/taskjuggler/RealFormat.rb +3 -0
- data/lib/taskjuggler/Resource.rb +3 -5
- data/lib/taskjuggler/ResourceScenario.rb +19 -7
- data/lib/taskjuggler/RichText.rb +4 -6
- data/lib/taskjuggler/RichText/FunctionExample.rb +1 -1
- data/lib/taskjuggler/RichText/FunctionHandler.rb +5 -6
- data/lib/taskjuggler/RichText/Parser.rb +4 -6
- data/lib/taskjuggler/RichText/RTFNavigator.rb +1 -1
- data/lib/taskjuggler/RichText/RTFQuery.rb +2 -3
- data/lib/taskjuggler/RichText/RTFReport.rb +1 -1
- data/lib/taskjuggler/RichText/RTFReportLink.rb +1 -2
- data/lib/taskjuggler/RichText/RTFWithQuerySupport.rb +1 -1
- data/lib/taskjuggler/RichText/Scanner.rb +6 -6
- data/lib/taskjuggler/RichText/Snip.rb +1 -2
- data/lib/taskjuggler/RuntimeConfig.rb +9 -6
- data/lib/taskjuggler/ScenarioData.rb +4 -3
- data/lib/taskjuggler/Scoreboard.rb +6 -0
- data/lib/taskjuggler/SheetHandlerBase.rb +25 -8
- data/lib/taskjuggler/SimpleQueryExpander.rb +14 -5
- data/lib/taskjuggler/SyntaxReference.rb +1 -2
- data/lib/taskjuggler/TableColumnSorter.rb +84 -0
- data/lib/taskjuggler/Task.rb +3 -5
- data/lib/taskjuggler/TaskJuggler.rb +36 -29
- data/lib/taskjuggler/TaskScenario.rb +154 -66
- data/lib/taskjuggler/TextParser.rb +24 -17
- data/lib/taskjuggler/TextParser/Scanner.rb +16 -11
- data/lib/taskjuggler/TextParser/SourceFileInfo.rb +20 -15
- data/lib/taskjuggler/TimeSheets.rb +6 -12
- data/lib/taskjuggler/Tj3AppBase.rb +35 -16
- data/lib/taskjuggler/Tj3Config.rb +1 -1
- data/lib/taskjuggler/TjpSyntaxRules.rb +239 -49
- data/lib/taskjuggler/XMLElement.rb +9 -2
- data/lib/taskjuggler/apps/Tj3.rb +43 -37
- data/lib/taskjuggler/apps/Tj3Client.rb +62 -112
- data/lib/taskjuggler/apps/Tj3Daemon.rb +66 -29
- data/lib/taskjuggler/apps/Tj3Man.rb +5 -5
- data/lib/taskjuggler/apps/Tj3SsReceiver.rb +10 -13
- data/lib/taskjuggler/apps/Tj3SsSender.rb +13 -16
- data/lib/taskjuggler/apps/Tj3TsReceiver.rb +10 -13
- data/lib/taskjuggler/apps/Tj3TsSender.rb +12 -15
- data/lib/taskjuggler/apps/Tj3TsSummary.rb +12 -15
- data/lib/taskjuggler/apps/Tj3WebD.rb +99 -0
- data/lib/taskjuggler/daemon/Daemon.rb +50 -10
- data/lib/taskjuggler/daemon/DaemonConnector.rb +127 -0
- data/lib/taskjuggler/daemon/ProcessIntercom.rb +36 -21
- data/lib/taskjuggler/daemon/ProjectBroker.rb +122 -112
- data/lib/taskjuggler/daemon/ProjectServer.rb +78 -46
- data/lib/taskjuggler/daemon/ReportServer.rb +52 -28
- data/lib/taskjuggler/daemon/ReportServlet.rb +92 -21
- data/lib/taskjuggler/daemon/WebServer.rb +75 -22
- data/lib/taskjuggler/daemon/WelcomePage.rb +1 -0
- data/lib/taskjuggler/reports/AccountListRE.rb +3 -3
- data/lib/taskjuggler/reports/CSVFile.rb +9 -2
- data/lib/taskjuggler/reports/ChartPlotter.rb +453 -0
- data/lib/taskjuggler/reports/Navigator.rb +1 -0
- data/lib/taskjuggler/reports/NikuReport.rb +4 -4
- data/lib/taskjuggler/reports/Report.rb +6 -18
- data/lib/taskjuggler/reports/ReportBase.rb +9 -9
- data/lib/taskjuggler/reports/ReportContext.rb +2 -2
- data/lib/taskjuggler/reports/StatusSheetReport.rb +6 -6
- data/lib/taskjuggler/reports/TableReport.rb +24 -15
- data/lib/taskjuggler/reports/TimeSheetReport.rb +5 -5
- data/lib/taskjuggler/reports/TraceReport.rb +251 -0
- data/lib/tj3webd.rb +17 -0
- data/manual/Day_To_Day_Juggling +10 -3
- data/manual/Installation +38 -19
- data/manual/Software +25 -19
- data/manual/Tutorial +119 -110
- data/manual/html/Day_To_Day_Juggling.html +7 -5
- data/manual/html/Getting_Started.html +4 -4
- data/manual/html/How_To_Contribute.html +4 -4
- data/manual/html/Installation.html +19 -11
- data/manual/html/Intro.html +4 -4
- data/manual/html/Reporting_Bugs.html +4 -4
- data/manual/html/Rich_Text_Attributes.html +4 -4
- data/manual/html/Software.html +15 -11
- data/manual/html/TaskJuggler_2x_Migration.html +4 -4
- data/manual/html/TaskJuggler_Internals.html +4 -4
- data/manual/html/The_TaskJuggler_Syntax.html +4 -4
- data/manual/html/Tutorial.html +41 -32
- data/manual/html/account.html +4 -4
- data/manual/html/account.task.html +4 -4
- data/manual/html/accountprefix.html +4 -4
- data/manual/html/accountreport.html +27 -9
- data/manual/html/accountroot.html +5 -5
- data/manual/html/active.html +4 -4
- data/manual/html/adopt.task.html +4 -4
- data/manual/html/aggregate.html +4 -4
- data/manual/html/alert.html +4 -4
- data/manual/html/alertlevels.html +4 -4
- data/manual/html/allocate.html +5 -5
- data/manual/html/alphabet.html +4 -4
- data/manual/html/alternative.html +4 -4
- data/manual/html/author.html +4 -4
- data/manual/html/balance.html +5 -5
- data/manual/html/booking.resource.html +4 -4
- data/manual/html/booking.task.html +4 -4
- data/manual/html/caption.html +5 -5
- data/manual/html/cellcolor.column.html +43 -8
- data/manual/html/celltext.column.html +4 -4
- data/manual/html/center.html +5 -5
- data/manual/html/charge.html +4 -4
- data/manual/html/chargeset.html +4 -4
- data/manual/html/columnid.html +27 -15
- data/manual/html/columns.html +5 -5
- data/manual/html/complete.html +4 -4
- data/manual/html/copyright.html +4 -4
- data/manual/html/credits.html +4 -4
- data/manual/html/css/tjreport.css +14 -5
- data/manual/html/currency.html +4 -4
- data/manual/html/currencyformat.html +5 -5
- data/manual/html/dailymax.html +5 -5
- data/manual/html/dailymin.html +5 -5
- data/manual/html/dailyworkinghours.html +4 -4
- data/manual/html/date.extend.html +4 -4
- data/manual/html/date.html +5 -5
- data/manual/html/definitions.html +4 -4
- data/manual/html/depends.html +4 -4
- data/manual/html/details.html +4 -4
- data/manual/html/disabled.html +4 -4
- data/manual/html/duration.html +4 -4
- data/manual/html/efficiency.html +4 -4
- data/manual/html/effort.html +4 -4
- data/manual/html/email.html +4 -4
- data/manual/html/enabled.html +4 -4
- data/manual/html/end.column.html +4 -4
- data/manual/html/end.html +4 -4
- data/manual/html/end.limit.html +4 -4
- data/manual/html/end.report.html +5 -5
- data/manual/html/end.timesheet.html +4 -4
- data/manual/html/endcredit.html +4 -4
- data/manual/html/epilog.html +5 -5
- data/manual/html/export.html +4 -4
- data/manual/html/extend.html +4 -4
- data/manual/html/fail.html +43 -8
- data/manual/html/fdl.html +4 -4
- data/manual/html/flags.account.html +4 -4
- data/manual/html/flags.html +4 -4
- data/manual/html/flags.journalentry.html +4 -4
- data/manual/html/flags.report.html +5 -5
- data/manual/html/flags.resource.html +4 -4
- data/manual/html/flags.statussheet.html +4 -4
- data/manual/html/flags.task.html +4 -4
- data/manual/html/flags.timesheet.html +4 -4
- data/manual/html/fontcolor.column.html +43 -8
- data/manual/html/footer.html +5 -5
- data/manual/html/formats.html +5 -5
- data/manual/html/functions.html +4 -4
- data/manual/html/gapduration.html +4 -4
- data/manual/html/gaplength.html +4 -4
- data/manual/html/halign.center.html +4 -4
- data/manual/html/halign.column.html +43 -8
- data/manual/html/halign.left.html +4 -4
- data/manual/html/halign.right.html +4 -4
- data/manual/html/hasalert.html +4 -4
- data/manual/html/header.html +5 -5
- data/manual/html/headline.html +7 -7
- data/manual/html/height.html +72 -0
- data/manual/html/hideaccount.html +46 -11
- data/manual/html/hidejournalentry.html +5 -5
- data/manual/html/hidereport.html +43 -8
- data/manual/html/hideresource.html +44 -9
- data/manual/html/hidetask.html +44 -9
- data/manual/html/icalreport.html +4 -4
- data/manual/html/include.macro.html +4 -4
- data/manual/html/include.project.html +4 -4
- data/manual/html/include.properties.html +4 -4
- data/manual/html/index.html +2 -1
- data/manual/html/inherit.extend.html +4 -4
- data/manual/html/interval1.html +4 -4
- data/manual/html/interval2.html +4 -4
- data/manual/html/interval3.html +4 -4
- data/manual/html/interval4.html +4 -4
- data/manual/html/isactive.html +4 -4
- data/manual/html/ischildof.html +4 -4
- data/manual/html/isdependencyof.html +4 -4
- data/manual/html/isdutyof.html +4 -4
- data/manual/html/isfeatureof.html +4 -4
- data/manual/html/isleaf.html +4 -4
- data/manual/html/ismilestone.html +4 -4
- data/manual/html/isongoing.html +4 -4
- data/manual/html/isresource.html +4 -4
- data/manual/html/isresponsibilityof.html +4 -4
- data/manual/html/istask.html +4 -4
- data/manual/html/journalattributes.html +11 -7
- data/manual/html/journalentry.html +4 -4
- data/manual/html/journalmode.html +5 -5
- data/manual/html/leaveallowance.html +5 -5
- data/manual/html/leaves.html +5 -6
- data/manual/html/left.html +5 -5
- data/manual/html/length.html +4 -4
- data/manual/html/limits.allocate.html +4 -4
- data/manual/html/limits.html +4 -4
- data/manual/html/limits.resource.html +4 -4
- data/manual/html/limits.task.html +4 -4
- data/manual/html/listitem.column.html +4 -4
- data/manual/html/listtype.column.html +4 -4
- data/manual/html/loadunit.html +5 -5
- data/manual/html/logicalexpression.html +8 -44
- data/manual/html/logicalflagexpression.html +4 -4
- data/manual/html/macro.html +4 -4
- data/manual/html/managers.html +4 -4
- data/manual/html/mandatory.html +4 -4
- data/manual/html/maxend.html +4 -4
- data/manual/html/maximum.html +5 -5
- data/manual/html/maxstart.html +4 -4
- data/manual/html/milestone.html +4 -4
- data/manual/html/minend.html +4 -4
- data/manual/html/minimum.html +5 -5
- data/manual/html/minstart.html +4 -4
- data/manual/html/monthlymax.html +5 -5
- data/manual/html/monthlymin.html +5 -5
- data/manual/html/navbar.html +10 -4
- data/manual/html/navigator.html +4 -4
- data/manual/html/newtask.html +4 -4
- data/manual/html/nikureport.html +4 -4
- data/manual/html/note.task.html +4 -4
- data/manual/html/now.html +4 -4
- data/manual/html/numberformat.html +5 -5
- data/manual/html/onend.html +4 -4
- data/manual/html/onstart.html +4 -4
- data/manual/html/opennodes.html +5 -5
- data/manual/html/overtime.booking.html +4 -4
- data/manual/html/period.column.html +4 -4
- data/manual/html/period.limit.html +4 -4
- data/manual/html/period.report.html +5 -5
- data/manual/html/period.task.html +4 -4
- data/manual/html/persistent.html +4 -4
- data/manual/html/precedes.html +4 -4
- data/manual/html/priority.html +4 -4
- data/manual/html/priority.timesheet.html +4 -4
- data/manual/html/project.html +4 -4
- data/manual/html/projectid.html +4 -4
- data/manual/html/projectid.task.html +4 -4
- data/manual/html/projectids.html +4 -4
- data/manual/html/projection.html +5 -7
- data/manual/html/prolog.html +5 -5
- data/manual/html/properties.html +11 -5
- data/manual/html/purge.html +5 -5
- data/manual/html/rate.html +4 -4
- data/manual/html/rate.resource.html +4 -4
- data/manual/html/reference.extend.html +4 -4
- data/manual/html/remaining.html +4 -4
- data/manual/html/replace.html +4 -4
- data/manual/html/reportprefix.html +4 -4
- data/manual/html/resource.html +4 -10
- data/manual/html/resourceattributes.html +4 -4
- data/manual/html/resourceprefix.html +4 -4
- data/manual/html/resourcereport.html +28 -10
- data/manual/html/resourceroot.html +5 -5
- data/manual/html/resources.limit.html +4 -4
- data/manual/html/responsible.html +4 -4
- data/manual/html/richtext.extend.html +4 -4
- data/manual/html/right.html +5 -5
- data/manual/html/rollupaccount.html +44 -9
- data/manual/html/rollupresource.html +44 -9
- data/manual/html/rolluptask.html +44 -9
- data/manual/html/scale.column.html +4 -4
- data/manual/html/scenario.html +4 -22
- data/manual/html/scenario.ical.html +4 -4
- data/manual/html/scenarios.export.html +4 -4
- data/manual/html/scenarios.html +5 -5
- data/manual/html/scenariospecific.extend.html +4 -4
- data/manual/html/scheduled.html +4 -4
- data/manual/html/scheduling.html +4 -4
- data/manual/html/select.html +4 -4
- data/manual/html/selfcontained.html +5 -5
- data/manual/html/shift.allocate.html +4 -4
- data/manual/html/shift.html +4 -4
- data/manual/html/shift.resource.html +5 -5
- data/manual/html/shift.task.html +4 -4
- data/manual/html/shift.timesheet.html +4 -4
- data/manual/html/shifts.allocate.html +4 -4
- data/manual/html/shifts.resource.html +4 -4
- data/manual/html/shifts.task.html +4 -4
- data/manual/html/shorttimeformat.html +4 -4
- data/manual/html/sloppy.booking.html +4 -4
- data/manual/html/sloppy.projection.html +5 -5
- data/manual/html/sortaccounts.html +5 -5
- data/manual/html/sortjournalentries.html +5 -5
- data/manual/html/sortresources.html +5 -5
- data/manual/html/sorttasks.html +5 -5
- data/manual/html/start.column.html +4 -4
- data/manual/html/start.html +4 -4
- data/manual/html/start.limit.html +4 -4
- data/manual/html/start.report.html +5 -5
- data/manual/html/startcredit.html +4 -4
- data/manual/html/status.statussheet.html +4 -4
- data/manual/html/status.timesheet.html +4 -4
- data/manual/html/statussheet.html +4 -4
- data/manual/html/statussheetreport.html +4 -4
- data/manual/html/strict.projection.html +5 -5
- data/manual/html/summary.html +4 -4
- data/manual/html/supplement.html +4 -4
- data/manual/html/supplement.resource.html +4 -10
- data/manual/html/supplement.task.html +4 -28
- data/manual/html/tagfile.html +4 -4
- data/manual/html/task.html +4 -28
- data/manual/html/task.statussheet.html +4 -4
- data/manual/html/task.timesheet.html +4 -4
- data/manual/html/taskattributes.html +4 -4
- data/manual/html/taskprefix.html +4 -4
- data/manual/html/taskreport.html +28 -10
- data/manual/html/taskroot.html +5 -5
- data/manual/html/text.extend.html +4 -4
- data/manual/html/textreport.html +27 -9
- data/manual/html/timeformat.html +5 -5
- data/manual/html/timeoff.nikureport.html +4 -4
- data/manual/html/timesheet.html +4 -4
- data/manual/html/timesheetreport.html +23 -5
- data/manual/html/timezone.export.html +4 -4
- data/manual/html/timezone.html +4 -4
- data/manual/html/timezone.report.html +5 -5
- data/manual/html/timezone.shift.html +4 -4
- data/manual/html/timingresolution.html +4 -4
- data/manual/html/title.column.html +4 -4
- data/manual/html/title.html +5 -5
- data/manual/html/toc.html +207 -179
- data/manual/html/tooltip.column.html +45 -10
- data/manual/html/tracereport.html +405 -0
- data/manual/html/trackingscenario.html +6 -6
- data/manual/html/treelevel.html +4 -4
- data/manual/html/vacation.html +4 -4
- data/manual/html/vacation.resource.html +4 -4
- data/manual/html/vacation.shift.html +4 -4
- data/manual/html/warn.html +43 -8
- data/manual/html/weeklymax.html +5 -5
- data/manual/html/weeklymin.html +5 -5
- data/manual/html/weekstartsmonday.html +4 -4
- data/manual/html/weekstartssunday.html +6 -6
- data/manual/html/width.column.html +6 -6
- data/manual/html/width.html +72 -0
- data/manual/html/work.html +4 -4
- data/manual/html/workinghours.project.html +4 -4
- data/manual/html/workinghours.resource.html +4 -4
- data/manual/html/workinghours.shift.html +4 -4
- data/manual/html/yearlyworkingdays.html +4 -4
- data/spec/Color_spec.rb +60 -0
- data/spec/ProjectBroker_spec.rb +3 -2
- data/spec/StatusSheets_spec.rb +5 -4
- data/spec/TableColumnSorter_spec.rb +78 -0
- data/spec/TimeSheets_spec.rb +6 -2
- data/spec/Tj3Daemon_spec.rb +2 -2
- data/spec/TraceReport_spec.rb +117 -0
- data/taskjuggler.gemspec +1 -1
- data/test/MessageChecker.rb +3 -1
- data/test/ReferenceGenerator.rb +1 -1
- data/test/TestSuite/CSV-Reports/Leave.tjp +1 -1
- data/test/TestSuite/CSV-Reports/refs/resourcereport_with_tasks.csv +3 -0
- data/test/TestSuite/CSV-Reports/refs/taskcounter.csv +9 -0
- data/test/TestSuite/CSV-Reports/refs/taskreport_with_resources.csv +19 -16
- data/test/TestSuite/CSV-Reports/refs/weekly.csv +1 -0
- data/test/TestSuite/Export-Reports/refs/LogicalExpression.tjp +14 -2
- data/test/TestSuite/Export-Reports/refs/tutorial.tjp +98 -86
- data/test/TestSuite/Scheduler/Correct/Leaves.tjp +25 -0
- data/test/TestSuite/Syntax/Correct/Leave.tjp +1 -1
- data/test/TestSuite/Syntax/Correct/LogicalExpression.tjp +9 -1
- data/test/TestSuite/Syntax/Correct/TraceReport.tjp +10 -0
- data/test/TestSuite/Syntax/Correct/tutorial.tjp +10 -4
- data/test/test_CSV-Reports.rb +3 -3
- data/test/test_Export-Reports.rb +91 -86
- data/test/test_Journal.rb +15 -12
- data/test/test_Limits.rb +3 -3
- data/test/test_Project.rb +1 -2
- data/test/test_ProjectFileScanner.rb +1 -1
- data/test/test_PropertySet.rb +1 -1
- data/test/test_Query.rb +5 -6
- data/test/test_ReportGenerator.rb +15 -7
- data/test/test_RichText.rb +4 -3
- data/test/test_Scheduler.rb +19 -7
- data/test/test_ShiftAssignments.rb +2 -2
- data/test/test_SimpleQueryExpander.rb +29 -2
- data/test/test_Syntax.rb +14 -5
- metadata +49 -10
- data/lib/taskjuggler/LogFile.rb +0 -73
@@ -11,24 +11,36 @@
|
|
11
11
|
# published by the Free Software Foundation.
|
12
12
|
#
|
13
13
|
|
14
|
+
require 'taskjuggler/AppConfig'
|
14
15
|
require 'taskjuggler/Log'
|
15
|
-
require 'taskjuggler/
|
16
|
+
require 'taskjuggler/MessageHandler'
|
16
17
|
|
17
18
|
class TaskJuggler
|
18
19
|
|
19
20
|
module ProcessIntercomIface
|
20
21
|
|
22
|
+
include MessageHandler
|
23
|
+
|
21
24
|
# This function catches all unhandled exceptions in the passed block.
|
22
25
|
def trap
|
23
|
-
log = LogFile.instance
|
24
|
-
|
25
26
|
begin
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
MessageHandlerInstance.instance.trapSetup = true
|
28
|
+
res = yield
|
29
|
+
MessageHandlerInstance.instance.trapSetup = false
|
30
|
+
res
|
31
|
+
rescue => e
|
32
|
+
# Any exception here is a fata error. We try hard to terminate the DRb
|
33
|
+
# thread and then exit the program.
|
34
|
+
begin
|
35
|
+
fatal('pi_crash_trap', "#{e}\n#{e.backtrace.join("\n")}\n\n" +
|
36
|
+
"#{'*' * 79}\nYou have triggered a bug in " +
|
37
|
+
"#{AppConfig.softwareName} version #{AppConfig.version}!\n" +
|
38
|
+
"Please see the user manual on how to get this bug fixed!\n" +
|
39
|
+
"#{'*' * 79}\n")
|
40
|
+
rescue RuntimeError
|
41
|
+
@server.terminate
|
42
|
+
return false
|
43
|
+
end
|
32
44
|
end
|
33
45
|
end
|
34
46
|
|
@@ -54,12 +66,13 @@ class TaskJuggler
|
|
54
66
|
|
55
67
|
module ProcessIntercom
|
56
68
|
|
69
|
+
include MessageHandler
|
70
|
+
|
57
71
|
def initIntercom
|
58
72
|
# This is the authentication key that clients will need to provide to
|
59
73
|
# execute DRb methods.
|
60
74
|
@authKey = generateAuthKey
|
61
75
|
|
62
|
-
@log = LogFile.instance
|
63
76
|
# This flag will be set to true by DRb method calls to terminate the
|
64
77
|
# process.
|
65
78
|
@terminate = false
|
@@ -73,14 +86,14 @@ class TaskJuggler
|
|
73
86
|
end
|
74
87
|
|
75
88
|
def terminate
|
76
|
-
|
89
|
+
debug('', 'Terminating on external request')
|
77
90
|
@terminate = true
|
78
91
|
end
|
79
92
|
|
80
93
|
def connect(stdout, stderr, stdin, silent)
|
81
94
|
# Set the client lock.
|
82
95
|
@clientConnection.lock
|
83
|
-
|
96
|
+
debug('', 'Rerouting ProjectServer standard IO to client')
|
84
97
|
# Make sure that all output to STDOUT and STDERR is sent to the client.
|
85
98
|
# Input is read from the client STDIN. We save a copy of the old file
|
86
99
|
# handles so we can restore then later again.
|
@@ -90,18 +103,18 @@ class TaskJuggler
|
|
90
103
|
$stdout = stdout if stdout
|
91
104
|
$stderr = stderr if stdout
|
92
105
|
$stdin = stdin if stdin
|
93
|
-
|
106
|
+
debug('', 'IO is now routed to the client')
|
94
107
|
Log.silent = silent
|
95
108
|
true
|
96
109
|
end
|
97
110
|
|
98
111
|
def disconnect
|
99
|
-
|
112
|
+
debug('', 'Restoring IO')
|
100
113
|
Log.silent = true
|
101
114
|
$stdout = @stdout if @stdout
|
102
115
|
$stderr = @stderr if @stderr
|
103
116
|
$stdin = @stdin if @stdin
|
104
|
-
|
117
|
+
debug('', 'Standard IO has been restored')
|
105
118
|
# Release the client lock
|
106
119
|
@clientConnection.unlock
|
107
120
|
true
|
@@ -113,10 +126,11 @@ class TaskJuggler
|
|
113
126
|
|
114
127
|
def checkKey(authKey, command)
|
115
128
|
if authKey == @authKey
|
116
|
-
|
129
|
+
debug('', "Accepted authentication key for command '#{command}'")
|
117
130
|
else
|
118
|
-
|
119
|
-
|
131
|
+
warning('auth_key_rejected',
|
132
|
+
"Rejected wrong authentication key #{authKey}" +
|
133
|
+
"for command '#{command}'")
|
120
134
|
return false
|
121
135
|
end
|
122
136
|
true
|
@@ -126,7 +140,7 @@ class TaskJuggler
|
|
126
140
|
# client connection timer.
|
127
141
|
def restartTimer
|
128
142
|
@timeLock.synchronize do
|
129
|
-
|
143
|
+
debug('', 'Reseting client connection timer')
|
130
144
|
@timerStart = Time.new
|
131
145
|
end
|
132
146
|
end
|
@@ -156,9 +170,10 @@ class TaskJuggler
|
|
156
170
|
sleep 1
|
157
171
|
end
|
158
172
|
if timerExpired?
|
159
|
-
|
173
|
+
warning('drb_timeout_shutdown',
|
174
|
+
'Shutting down DRb server due to timeout')
|
160
175
|
else
|
161
|
-
|
176
|
+
debug('', 'Shutting down the DRb server')
|
162
177
|
end
|
163
178
|
DRb.stop_service
|
164
179
|
break
|
@@ -17,8 +17,8 @@ require 'drb'
|
|
17
17
|
require 'drb/acl'
|
18
18
|
require 'taskjuggler/daemon/Daemon'
|
19
19
|
require 'taskjuggler/daemon/ProjectServer'
|
20
|
-
require 'taskjuggler/daemon/WebServer'
|
21
20
|
require 'taskjuggler/TjTime'
|
21
|
+
require 'taskjuggler/MessageHandler'
|
22
22
|
|
23
23
|
class TaskJuggler
|
24
24
|
|
@@ -37,8 +37,9 @@ class TaskJuggler
|
|
37
37
|
# daemon.
|
38
38
|
class ProjectBroker < Daemon
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
include MessageHandler
|
41
|
+
|
42
|
+
attr_accessor :authKey, :port, :uriFile, :projectFiles, :logStdIO
|
42
43
|
|
43
44
|
def initialize
|
44
45
|
super
|
@@ -66,15 +67,6 @@ class TaskJuggler
|
|
66
67
|
# will be seperate set of files for each process.
|
67
68
|
@logStdIO = !@daemonize
|
68
69
|
|
69
|
-
# Reference to WEBrick object.
|
70
|
-
@webServer = nil
|
71
|
-
|
72
|
-
# Port used by the web server
|
73
|
-
@webServerPort = 8080
|
74
|
-
|
75
|
-
# True if web server should be activated
|
76
|
-
@enableWebServer = false
|
77
|
-
|
78
70
|
# This flag will be set to true to terminate the daemon.
|
79
71
|
@terminate = false
|
80
72
|
end
|
@@ -83,7 +75,7 @@ class TaskJuggler
|
|
83
75
|
# To ensure a certain level of security, the user must provide an
|
84
76
|
# authentication key to authenticate the client to this server.
|
85
77
|
unless @authKey
|
86
|
-
|
78
|
+
error('pb_no_auth_key', <<'EOT'
|
87
79
|
You must set an authentication key in the configuration file. Create a file
|
88
80
|
named .taskjugglerrc or taskjuggler.rc that contains at least the following
|
89
81
|
lines. Replace 'your_secret_key' with some random character sequence.
|
@@ -96,18 +88,7 @@ EOT
|
|
96
88
|
|
97
89
|
# In daemon mode, we fork twice and only the 2nd child continues here.
|
98
90
|
super()
|
99
|
-
|
100
|
-
|
101
|
-
if @enableWebServer
|
102
|
-
begin
|
103
|
-
# The web server must be started before we turn SAFE mode on.
|
104
|
-
@webServer = WebServer.new(self, @webServerPort)
|
105
|
-
@log.info("TaskJuggler web server is listening on port " +
|
106
|
-
"#{@webServerPort}")
|
107
|
-
rescue
|
108
|
-
@log.fatal("Cannot start web server: #{$!}")
|
109
|
-
end
|
110
|
-
end
|
91
|
+
debug('', "Starting project broker")
|
111
92
|
|
112
93
|
# Setup a DRb server to handle the incomming requests from the clients.
|
113
94
|
brokerIface = ProjectBrokerIface.new(self)
|
@@ -116,9 +97,9 @@ EOT
|
|
116
97
|
DRb.install_acl(ACL.new(%w[ deny all
|
117
98
|
allow 127.0.0.1 ]))
|
118
99
|
@uri = DRb.start_service("druby://127.0.0.1:#{@port}", brokerIface).uri
|
119
|
-
|
100
|
+
info('daemon_uri', "TaskJuggler daemon is listening on #{@uri}")
|
120
101
|
rescue
|
121
|
-
|
102
|
+
error('port_in_use', "Cannot listen on port #{@port}: #{$!}")
|
122
103
|
end
|
123
104
|
|
124
105
|
if @port == 0 && @uriFile
|
@@ -127,7 +108,7 @@ EOT
|
|
127
108
|
begin
|
128
109
|
File.open(@uriFile, 'w') { |f| f.write @uri }
|
129
110
|
rescue
|
130
|
-
|
111
|
+
error('cannot_write_uri', "Cannot write URI file #{@uriFile}: #{$!}")
|
131
112
|
end
|
132
113
|
end
|
133
114
|
|
@@ -141,19 +122,19 @@ EOT
|
|
141
122
|
# some other work asynchronously.
|
142
123
|
startHousekeeping
|
143
124
|
|
144
|
-
|
145
|
-
DRb.
|
125
|
+
debug('', 'Shutting down ProjectBroker DRb server')
|
126
|
+
DRb.stop_service
|
146
127
|
|
147
128
|
# If we have created a URI file, we need to delete it again.
|
148
129
|
if @port == 0 && @uriFile
|
149
130
|
begin
|
150
131
|
File.delete(@uriFile)
|
151
132
|
rescue
|
152
|
-
|
133
|
+
error('cannot_delete_uri', "Cannot delete URI file .tj3d.uri: #{$!}")
|
153
134
|
end
|
154
135
|
end
|
155
136
|
|
156
|
-
|
137
|
+
info('daemon_terminated', 'TaskJuggler daemon terminated')
|
157
138
|
end
|
158
139
|
|
159
140
|
# All remote commands must provide the proper authentication key. Usually
|
@@ -161,10 +142,11 @@ EOT
|
|
161
142
|
# configuration file.
|
162
143
|
def checkKey(authKey, command)
|
163
144
|
if authKey == @authKey
|
164
|
-
|
145
|
+
debug('', "Accepted authentication key for command '#{command}'")
|
165
146
|
else
|
166
|
-
|
167
|
-
|
147
|
+
warning('wrong_auth_key',
|
148
|
+
"Rejected wrong authentication key '#{authKey}' " +
|
149
|
+
"for command '#{command}'")
|
168
150
|
return false
|
169
151
|
end
|
170
152
|
true
|
@@ -172,7 +154,7 @@ EOT
|
|
172
154
|
|
173
155
|
# This command will initiate the termination of the daemon.
|
174
156
|
def stop
|
175
|
-
|
157
|
+
debug('', 'Terminating on client request')
|
176
158
|
|
177
159
|
# Shut down the web server if we've started one.
|
178
160
|
if @webServer
|
@@ -216,7 +198,7 @@ EOT
|
|
216
198
|
# associated to. Just use a large enough random number.
|
217
199
|
tag = rand(9999999999999)
|
218
200
|
|
219
|
-
|
201
|
+
debug('', "Pushing #{tag} to load Queue")
|
220
202
|
@projectsToLoad.push(tag)
|
221
203
|
|
222
204
|
# Now we have to wait until the project shows up in the @projects
|
@@ -236,23 +218,23 @@ EOT
|
|
236
218
|
sleep 0.1 unless pr
|
237
219
|
end
|
238
220
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
221
|
+
debug('', "Found tag #{tag} in list of loaded projects with URI " +
|
222
|
+
"#{pr.uri}")
|
223
|
+
|
224
|
+
res = false
|
243
225
|
|
244
226
|
# Open a DRb connection to the ProjectServer process
|
245
227
|
begin
|
246
228
|
projectServer = DRbObject.new(nil, pr.uri)
|
247
229
|
rescue
|
248
|
-
|
230
|
+
warning('pb_cannot_get_ps', "Can't get ProjectServer object: #{$!}")
|
249
231
|
return false
|
250
232
|
end
|
251
233
|
begin
|
252
234
|
# Hook up IO from requestor to the ProjectServer process.
|
253
235
|
projectServer.connect(pr.authKey, stdOut, stdErr, stdIn, silent)
|
254
236
|
rescue
|
255
|
-
|
237
|
+
warning('pb_cannot_connect_io', "Can't connect IO: #{$!}")
|
256
238
|
return false
|
257
239
|
end
|
258
240
|
|
@@ -261,7 +243,7 @@ EOT
|
|
261
243
|
begin
|
262
244
|
res = projectServer.loadProject(pr.authKey, [ cwd, *args ])
|
263
245
|
rescue
|
264
|
-
|
246
|
+
warning('pb_load_failed', "Loading of project failed: #{$!}")
|
265
247
|
return false
|
266
248
|
end
|
267
249
|
|
@@ -269,7 +251,7 @@ EOT
|
|
269
251
|
begin
|
270
252
|
projectServer.disconnect(pr.authKey)
|
271
253
|
rescue
|
272
|
-
|
254
|
+
warning('pb_cannot_disconnect_io', "Can't disconnect IO: #{$!}")
|
273
255
|
return false
|
274
256
|
end
|
275
257
|
|
@@ -278,7 +260,8 @@ EOT
|
|
278
260
|
|
279
261
|
def removeProject(indexOrId)
|
280
262
|
@projects.synchronize do
|
281
|
-
# Find all projects with the IDs in indexOrId and mark them as
|
263
|
+
# Find all projects with the IDs in indexOrId and mark them as
|
264
|
+
# :obsolete.
|
282
265
|
if /^[0-9]$/.match(indexOrId)
|
283
266
|
index = indexOrId.to_i - 1
|
284
267
|
if index >= 0 && index < @projects.length
|
@@ -315,7 +298,7 @@ EOT
|
|
315
298
|
end
|
316
299
|
|
317
300
|
if project.nil?
|
318
|
-
|
301
|
+
debug('', "No project with ID #{projectId} found")
|
319
302
|
return [ nil, nil ]
|
320
303
|
end
|
321
304
|
[ project.uri, project.authKey ]
|
@@ -371,7 +354,7 @@ EOT
|
|
371
354
|
# Don't accept updates for already obsolete entries.
|
372
355
|
next if project.state == :obsolete
|
373
356
|
|
374
|
-
|
357
|
+
debug('', "Updating state for #{id} to #{state}")
|
375
358
|
# Only update the record that has the matching key
|
376
359
|
if project.authKey == projectKey
|
377
360
|
project.id = id if id
|
@@ -385,7 +368,7 @@ EOT
|
|
385
368
|
@projects.each do |p|
|
386
369
|
if p != project && p.id == id
|
387
370
|
p.state = :obsolete
|
388
|
-
|
371
|
+
debug('', "Marking entry with ID #{id} as obsolete")
|
389
372
|
end
|
390
373
|
end
|
391
374
|
project.readySince = TjTime.new
|
@@ -409,64 +392,64 @@ EOT
|
|
409
392
|
private
|
410
393
|
|
411
394
|
def startHousekeeping
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
elsif p.state == :failed
|
434
|
-
# Start removal of entries that didn't parse.
|
435
|
-
p.state = :obsolete
|
436
|
-
end
|
395
|
+
begin
|
396
|
+
cntr = 0
|
397
|
+
loop do
|
398
|
+
if @terminate
|
399
|
+
# Give the caller a chance to properly terminate the connection.
|
400
|
+
sleep 0.5
|
401
|
+
break
|
402
|
+
elsif !@projectsToLoad.empty?
|
403
|
+
loadProject(@projectsToLoad.pop)
|
404
|
+
else
|
405
|
+
# Send termination command to all obsolute ProjectServer
|
406
|
+
# objects. To minimize the locking of @projects we collect the
|
407
|
+
# obsolete items first.
|
408
|
+
termList = []
|
409
|
+
@projects.synchronize do
|
410
|
+
@projects.each do |p|
|
411
|
+
if p.state == :obsolete
|
412
|
+
termList << p
|
413
|
+
elsif p.state == :failed
|
414
|
+
# Start removal of entries that didn't parse.
|
415
|
+
p.state = :obsolete
|
437
416
|
end
|
438
417
|
end
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
418
|
+
end
|
419
|
+
# And then send them a termination command.
|
420
|
+
termList.each { |p| p.terminateServer }
|
421
|
+
|
422
|
+
# Check every 10 seconds that the ProjectServer processes are
|
423
|
+
# still alive. If not, remove them from the list.
|
424
|
+
if (cntr += 1) > 10
|
425
|
+
@projects.synchronize do
|
426
|
+
@projects.each do |p|
|
427
|
+
unless p.ping
|
428
|
+
termList << p unless termList.include?(p)
|
450
429
|
end
|
451
430
|
end
|
452
|
-
cntr = 0
|
453
431
|
end
|
432
|
+
cntr = 0
|
433
|
+
end
|
454
434
|
|
455
|
-
|
456
|
-
|
457
|
-
|
435
|
+
# The housekeeping thread rarely needs to so something. Make
|
436
|
+
# sure it's sleeping most of the time.
|
437
|
+
sleep 1
|
458
438
|
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
end
|
439
|
+
# Remove the obsolete records from the @projects list.
|
440
|
+
@projects.synchronize do
|
441
|
+
@projects.delete_if { |p| termList.include?(p) }
|
463
442
|
end
|
464
443
|
end
|
465
|
-
rescue
|
466
|
-
$stderr.print $!.to_s
|
467
|
-
$stderr.print $!.backtrace.join("\n")
|
468
|
-
@log.fatal("ProjectBroker housekeeping error: #{$!}")
|
469
444
|
end
|
445
|
+
rescue => exception
|
446
|
+
# TjRuntimeError exceptions are simply passed through.
|
447
|
+
if exception.is_a?(TjRuntimeError)
|
448
|
+
raise TjRuntimeError, $!
|
449
|
+
end
|
450
|
+
|
451
|
+
fatal('pb_housekeeping_error',
|
452
|
+
"ProjectBroker housekeeping error: #{$!}")
|
470
453
|
end
|
471
454
|
end
|
472
455
|
|
@@ -475,11 +458,11 @@ EOT
|
|
475
458
|
tag = rand(9999999999999)
|
476
459
|
project = tagOrProject
|
477
460
|
# The 2nd element of the Array is the *.tjp file name.
|
478
|
-
|
461
|
+
debug('', "Loading project #{tagOrProject[1]} with tag #{tag}")
|
479
462
|
else
|
480
463
|
tag = tagOrProject
|
481
464
|
project = nil
|
482
|
-
|
465
|
+
debug('', "Loading project for tag #{tag}")
|
483
466
|
end
|
484
467
|
pr = ProjectRecord.new(tag)
|
485
468
|
ps = ProjectServer.new(@authKey, project, @logStdIO)
|
@@ -500,6 +483,8 @@ EOT
|
|
500
483
|
# these methods for remote access.
|
501
484
|
class ProjectBrokerIface
|
502
485
|
|
486
|
+
include MessageHandler
|
487
|
+
|
503
488
|
def initialize(broker)
|
504
489
|
@broker = broker
|
505
490
|
end
|
@@ -517,15 +502,25 @@ EOT
|
|
517
502
|
|
518
503
|
# This function catches all unhandled exceptions in the passed block.
|
519
504
|
def trap
|
520
|
-
log = LogFile.instance
|
521
|
-
|
522
505
|
begin
|
523
506
|
yield
|
524
|
-
rescue
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
507
|
+
rescue => e
|
508
|
+
# TjRuntimeError exceptions are simply passed through.
|
509
|
+
if e.is_a?(TjRuntimeError)
|
510
|
+
raise TjRuntimeError, $!
|
511
|
+
end
|
512
|
+
|
513
|
+
# Any exception here is a fata error. We try hard to terminate the DRb
|
514
|
+
# thread and then exit the program.
|
515
|
+
begin
|
516
|
+
fatal('pb_crash_trap', "#{e}\n#{e.backtrace.join("\n")}\n\n" +
|
517
|
+
"#{'*' * 79}\nYou have triggered a bug in " +
|
518
|
+
"#{AppConfig.softwareName} version #{AppConfig.version}!\n" +
|
519
|
+
"Please see the user manual on how to get this bug fixed!\n" +
|
520
|
+
"#{'*' * 79}\n")
|
521
|
+
rescue RuntimeError
|
522
|
+
@broker.stop
|
523
|
+
end
|
529
524
|
end
|
530
525
|
end
|
531
526
|
|
@@ -549,11 +544,24 @@ EOT
|
|
549
544
|
when :update
|
550
545
|
@broker.update
|
551
546
|
else
|
552
|
-
|
547
|
+
fatal('unknown_command', 'Unknown command #{cmd} called')
|
553
548
|
end
|
554
549
|
end
|
555
550
|
end
|
556
551
|
|
552
|
+
def getProjectList(authKey)
|
553
|
+
return false unless @broker.checkKey(authKey, 'getProjectList')
|
554
|
+
|
555
|
+
trap { @broker.getProjectList }
|
556
|
+
end
|
557
|
+
|
558
|
+
def getProject(authKey, id)
|
559
|
+
return false unless @broker.checkKey(authKey, 'getProject')
|
560
|
+
|
561
|
+
debug('', "PID: #{id} Class: #{id.class}")
|
562
|
+
trap { @broker.getProject(id) }
|
563
|
+
end
|
564
|
+
|
557
565
|
def updateState(authKey, projectKey, id, status, modified)
|
558
566
|
return false unless @broker.checkKey(authKey, 'updateState')
|
559
567
|
|
@@ -566,6 +574,8 @@ EOT
|
|
566
574
|
# one entry for each project in the @projects list.
|
567
575
|
class ProjectRecord < Monitor
|
568
576
|
|
577
|
+
include MessageHandler
|
578
|
+
|
569
579
|
attr_accessor :authKey, :uri, :files, :id, :state, :readySince, :modified,
|
570
580
|
:reloading
|
571
581
|
attr_reader :tag
|
@@ -592,19 +602,18 @@ EOT
|
|
592
602
|
# True if the reload has already been triggered.
|
593
603
|
@reloading = false
|
594
604
|
|
595
|
-
@log = LogFile.instance
|
596
605
|
@projectServer = nil
|
597
606
|
end
|
598
607
|
|
599
608
|
def ping
|
600
609
|
return true unless @uri
|
601
610
|
|
602
|
-
|
611
|
+
debug('', "Sending ping to ProjectServer #{@uri}")
|
603
612
|
begin
|
604
613
|
@projectServer = DRbObject.new(nil, @uri) unless @projectServer
|
605
614
|
@projectServer.ping(@authKey)
|
606
615
|
rescue
|
607
|
-
|
616
|
+
warning('ping_failed', "Ping failed: #{$!}")
|
608
617
|
return false
|
609
618
|
end
|
610
619
|
true
|
@@ -615,11 +624,12 @@ EOT
|
|
615
624
|
return unless @uri
|
616
625
|
|
617
626
|
begin
|
618
|
-
|
627
|
+
debug('', "Sending termination request to ProjectServer #{@uri}")
|
619
628
|
@projectServer = DRbObject.new(nil, @uri) unless @projectServer
|
620
629
|
@projectServer.terminate(@authKey)
|
621
630
|
rescue
|
622
|
-
|
631
|
+
error('proj_serv_term_failed',
|
632
|
+
"Termination of ProjectServer failed: #{$!}")
|
623
633
|
end
|
624
634
|
@uri = nil
|
625
635
|
end
|