All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencastproject.serviceregistry.api.Incidents Maven / Gradle / Ivy

There is a newer version: 16.6
Show newest version
/*
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 *
 * The Apereo Foundation licenses this file to you under the Educational
 * Community License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License
 * at:
 *
 *   http://opensource.org/licenses/ecl2.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package org.opencastproject.serviceregistry.api;

import static org.opencastproject.util.data.Monadics.mlist;
import static org.opencastproject.util.data.Tuple.tuple;

import org.opencastproject.job.api.Incident;
import org.opencastproject.job.api.Incident.Severity;
import org.opencastproject.job.api.IncidentTree;
import org.opencastproject.job.api.Job;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.data.Function;
import org.opencastproject.util.data.Tuple;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

/** Create and record job incidents. Facade for {@link IncidentService}. */
public final class Incidents {

  private static final Logger log = LoggerFactory.getLogger(Incident.class);

  /**
   * System error codes
   */
  private static final String SYSTEM_UNHANDLED_EXCEPTION = "org.opencastproject.system.unhandled-exception";
  private static final String SYSTEM_JOB_CREATION_EXCEPTION = "org.opencastproject.system.job-creation-exception";

  public static final Map NO_PARAMS = Collections.emptyMap();
  public static final List> NO_DETAILS = Collections.emptyList();

  private final IncidentService is;
  private final ServiceRegistry sr;

  public Incidents(ServiceRegistry sr, IncidentService is) {
    this.is = is;
    this.sr = sr;
  }

  /**
   * Record an incident for a given job. This method is intended to record client incidents, i.e. incidents crafted by
   * the programmer.
   *
   * @param code
   *          A code number. This incident factory method enforces an incident code schema of job_type.code
   *          , e.g. org.opencastproject.service.1511 . So instead of aligning
   *          job.getJobType() and the incident's code prefix manually this is done automatically for you
   *          by this method. See {@link org.opencastproject.job.api.Incident#getCode()}.
   * @see org.opencastproject.job.api.Incident
   */
  public void record(Job job, Severity severity, int code, Map params,
          List> details) {
    try {
      is.storeIncident(job, new Date(), job.getJobType() + "." + code, severity, params, details);
    } catch (IncidentServiceException e) {
      logException(e);
    }
  }

  /**
   * Record an incident for a given job. This method is intended to record client incidents, i.e. incidents crafted by
   * the programmer.
   *
   * @see #record(org.opencastproject.job.api.Job, org.opencastproject.job.api.Incident.Severity, int, java.util.Map,
   *      java.util.List)
   * @see org.opencastproject.job.api.Incident
   */
  public void record(Job job, Severity severity, int code) {
    record(job, severity, code, NO_PARAMS, NO_DETAILS);
  }

  /**
   * Record a failure incident for a given job.
   *
   * @see #record(org.opencastproject.job.api.Job, org.opencastproject.job.api.Incident.Severity, int, java.util.Map,
   *      java.util.List)
   * @see org.opencastproject.job.api.Incident
   */
  public void recordFailure(Job job, int code, Map params) {
    record(job, Severity.FAILURE, code, params, NO_DETAILS);
  }

  /**
   * Record a failure incident for a given job.
   *
   * @see #record(org.opencastproject.job.api.Job, org.opencastproject.job.api.Incident.Severity, int, java.util.Map,
   *      java.util.List)
   * @see org.opencastproject.job.api.Incident
   */
  public void recordFailure(Job job, int code, Throwable t, Map params,
          List> details) {
    List> detailList = new ArrayList<>(details);
    detailList.add(tuple("stack-trace", ExceptionUtils.getStackTrace(t)));
    record(job, Severity.FAILURE, code, params, detailList);
  }

  public void recordJobCreationIncident(Job job, Throwable t) {
    unhandledException(job, SYSTEM_JOB_CREATION_EXCEPTION, Severity.FAILURE, t);
  }

  /**
   * Record an incident for a given job caused by an uncatched exception. This method is intended to record incidents by
   * the job system itself, e.g. the job dispatcher.
   */
  public void unhandledException(Job job, Severity severity, Throwable t) {
    unhandledException(job, SYSTEM_UNHANDLED_EXCEPTION, severity, t);
  }

  /**
   * Record an incident for a given job caused by an uncatched exception. This method is intended to record incidents by
   * the job system itself, e.g. the job dispatcher. Please note that an incident will only be recorded if none
   * of severity {@link org.opencastproject.job.api.Incident.Severity#FAILURE} has already been recorded by the job or
   * one of its child jobs. If no job with the given job id exists nothing happens.
   */
  public void unhandledException(long jobId, Severity severity, Throwable t) {
    try {
      unhandledException(sr.getJob(jobId), severity, t);
    } catch (NotFoundException ignore) {
    } catch (ServiceRegistryException e) {
      logException(e);
    }
  }

  /**
   * Record an incident for a given job caused by an uncatched exception. This method is intended to record incidents by
   * the job system itself, e.g. the job dispatcher.
   */
  private void unhandledException(Job job, String code, Severity severity, Throwable t) {
    if (!alreadyRecordedFailureIncident(job.getId())) {
      try {
        is.storeIncident(
                job,
                new Date(),
                code,
                severity,
                Collections.singletonMap("exception", ExceptionUtils.getMessage(t)),
                Arrays.asList(tuple("job-type", job.getJobType()), tuple("job-operation", job.getOperation()),
                        tuple("stack-trace", ExceptionUtils.getStackTrace(t))));
      } catch (IncidentServiceException e) {
        logException(e);
      }
    }
  }

  private void logException(Throwable t) {
    log.error("Error recording job incident. Log exception and move on.", t);
  }

  public boolean alreadyRecordedFailureIncident(long jobId) {
    try {
      return findFailure(is.getIncidentsOfJob(jobId, true));
    } catch (Exception e) {
      return false;
    }
  }

  static boolean findFailure(IncidentTree r) {
    return mlist(r.getIncidents()).exists(isFailure) || mlist(r.getDescendants()).exists(findFailureFn);
  }

  static final Function findFailureFn = new Function() {
    @Override
    public Boolean apply(IncidentTree r) {
      return findFailure(r);
    }
  };

  static final Function isFailure = new Function() {
    @Override
    public Boolean apply(Incident i) {
      return i.getSeverity() == Severity.FAILURE;
    }
  };
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy