class TaskJuggler::ReportServlet

Public Class Methods

get_instance(config, options) click to toggle source
# File lib/taskjuggler/daemon/ReportServlet.rb, line 32
def self.get_instance(config, options)
  self.new(config, options)
end
new(config, options) click to toggle source
Calls superclass method
# File lib/taskjuggler/daemon/ReportServlet.rb, line 24
def initialize(config, options)
  super
  @authKey = options[0]
  @host = options[1]
  @port = options[2]
  @uri = options[3]
end

Public Instance Methods

do_GET(req, res) click to toggle source
# File lib/taskjuggler/daemon/ReportServlet.rb, line 36
def do_GET(req, res)
  debug('', "Serving URL #{req}")
  @req = req
  @res = res
  begin
    # WEBrick is returning the query elements as FormData objects. We must
    # use to_s to explicitely convert them to String objects.
    projectId = req.query['project'].to_s
    debug('', "Project ID: #{projectId}")
    reportId = req.query['report'].to_s
    debug('', "Report ID: #{reportId}")
    if projectId.empty? || reportId.empty?
      debug('', "Project welcome page requested")
      generateWelcomePage(projectId)
    else
      debug('', "Report #{reportId} of project #{projectId} requested")
      attributes = req.query['attributes'] || ''
      unless attributes.empty?
        attributes = URLParameter.decode(attributes)
      end
      debug('', "Attributes: #{attributes}")
      generateReport(projectId, reportId, attributes)
    end
  rescue
    error('get_req_failed', "Cannot serve GET request: #{req}\n#{$!}")
  end
end

Private Instance Methods

connectToBroker() click to toggle source
# File lib/taskjuggler/daemon/ReportServlet.rb, line 66
def connectToBroker
  begin
    broker = DaemonConnector.new(@authKey, @host, @port, @uri)
  rescue
    error('cannot_connect_broker',
          "Cannot connect to the TaskJuggler daemon: #{$!}\n" +
          "Please make sure you have tj3d running and listening " +
          "on port #{@port} or URI '#{@uri}'.")
  end

  broker
end
debug(id, message) click to toggle source
# File lib/taskjuggler/daemon/ReportServlet.rb, line 241
def debug(id, message)
  MessageHandlerInstance.instance.debug(id, message)
end
error(id, message) click to toggle source
# File lib/taskjuggler/daemon/ReportServlet.rb, line 234
def error(id, message)
  @res.status = 412
  @res.body = "ERROR: #{message}"
  @res['content-type'] = 'text/plain'
  MessageHandlerInstance.instance.error(id, message)
end
generateReport(projectId, reportId, attributes) click to toggle source
# File lib/taskjuggler/daemon/ReportServlet.rb, line 79
def generateReport(projectId, reportId, attributes)
  broker = connectToBroker

  # Request the Project credentials from the ProbjectBroker.
  begin
    @ps_uri, @ps_authKey = broker.getProject(projectId)
  rescue
    error('cannot_get_project_server',
          "Cannot get project server for ID #{projectId}: #{$!}")
  end

  if @ps_uri.nil?
    error('ps_uri_nil', "No project with ID #{projectId} loaded")
  end
  # Get the responsible ReportServer that can generate the report.
  begin
    @projectServer = DRbObject.new(nil, @ps_uri)
    @rs_uri, @rs_authKey = @projectServer.getReportServer(@ps_authKey)
    @reportServer = DRbObject.new(nil, @rs_uri)
  rescue
    error('cannot_get_report_server',
          "Cannot get report server: #{$!}")
  end
  # Create two StringIO buffers that will receive the $stdout and $stderr
  # text from the report server. This buffer will contain the generated
  # report as HTML encoded text. They will be send via DRb, so we have to
  # extend them with DRbUndumped.
  stdOut = StringIO.new('')
  stdOut.extend(DRbUndumped)
  stdErr = StringIO.new('')
  stdErr.extend(DRbUndumped)

  begin
    @reportServer.connect(@rs_authKey, stdOut, stdErr, $stdin, true)
  rescue => exception
    # TjRuntimeError exceptions are simply passed through.
    if exception.is_a?(TjRuntimeError)
      raise TjRuntimeError, $!
    end

    error('rs_io_connect_failed', "Can't connect IO: #{$!}")
  end

  # Ask the ReportServer to generate the reports with the provided ID.
  retVal = true
  begin
    retVal = @reportServer.generateReport(@rs_authKey, reportId, false, nil,
                                          attributes)
  rescue
    stdOut.rewind
    stdErr.rewind
    error('rs_generate_report_failed',
          "Report server crashed: #{$!}\n#{stdErr.read}\n#{stdOut.read}")
  end
  # Disconnect the ReportServer
  begin
    @reportServer.disconnect(@rs_authKey)
  rescue
    error('rs_io_disconnect_failed', "Can't disconnect IO: #{$!}")
  end
  # And send a termination request.
  begin
    @reportServer.terminate(@rs_authKey)
  rescue
    error('report_server_term_failed',
          "Report server termination failed: #{$!}")
  end
  @reportServer = nil
  broker.disconnect

  @res['content-type'] = 'text/html'
  if retVal
    # To read the $stdout of the ReportServer we need to rewind the buffer
    # and then read the full text.
    stdOut.rewind
    @res.body = stdOut.read
  else
    stdErr.rewind
    error('get_req_stderr',
          "Error while parsing attribute definition:\n-8<-\n" +
          "#{attributes}\n->8-\n#{stdErr.read}")
  end
end
generateWelcomePage(projectId) click to toggle source
# File lib/taskjuggler/daemon/ReportServlet.rb, line 163
def generateWelcomePage(projectId)
  broker = connectToBroker

  begin
    projects = broker.getProjectList
  rescue
    error('cannot_get_project_list',
          "Cannot get project list from daemon: #{$!}")
  end

  text = "== Welcome to the TaskJuggler Project Server ==\n----\n"
  projects.each do |id|
    if id == projectId
      # Show the list of reports for this project.
      text << "* [/taskjuggler #{getProjectName(id)}]\n"
      reports = getReportList(id)
      if reports.empty?
        text << "** This project has no reports defined.\n"
      else
        reports.each do |reportId, reportName|
          text << "** [/taskjuggler?project=#{id};report=#{reportId} " +
                  "#{reportName}]\n"
        end
      end
    else
      # Just show a link to open the report list.
      text << "* [/taskjuggler?project=#{id} #{getProjectName(id)}]\n"
    end
  end

  # We no longer need the broker.
  broker.disconnect

  rt = RichText.new(text)
  rti = rt.generateIntermediateFormat
  rti.sectionNumbers = false
  page = HTMLDocument.new
  page.generateHead("The TaskJuggler Project Server")
  page.html << rti.to_html
  @res['content-type'] = 'text/html'
  @res.body = page.to_s
end
getProjectName(id) click to toggle source
# File lib/taskjuggler/daemon/ReportServlet.rb, line 206
def getProjectName(id)
  broker = connectToBroker

  uri, authKey = broker.getProject(id)
  return nil unless uri
  projectServer = DRbObject.new(nil, uri)
  return nil unless projectServer
  res = projectServer.getProjectName(authKey)

  broker.disconnect

  res
end
getReportList(id) click to toggle source
# File lib/taskjuggler/daemon/ReportServlet.rb, line 220
def getReportList(id)
  broker = connectToBroker

  uri, authKey = broker.getProject(id)
  return [] unless uri
  projectServer = DRbObject.new(nil, uri)
  return [] unless projectServer
  res = projectServer.getReportList(authKey)

  broker.disconnect

  res
end