Package com.esri.gpt.control.webharvest.engine

Source Code of com.esri.gpt.control.webharvest.engine.Harvester$HarvesterListenerArray

/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You under the Apache 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://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.esri.gpt.control.webharvest.engine;

import com.esri.gpt.catalog.harvest.jobs.HjResetRunningRequest;
import com.esri.gpt.catalog.harvest.repository.HrRecord;
import com.esri.gpt.catalog.management.MmdEnums.ApprovalStatus;
import com.esri.gpt.control.rest.writer.ResponseWriter;
import com.esri.gpt.control.webharvest.common.CommonCriteria;
import com.esri.gpt.control.webharvest.protocol.ProtocolInvoker;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.jsf.MessageBroker;
import com.esri.gpt.framework.resource.api.SourceUri;
import java.lang.management.ManagementFactory;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.ObjectName;

/**
* Harvesting engine.
*/
public class Harvester implements HarvesterMBean {

  /** logger */
  private static final Logger LOGGER = Logger.getLogger(Harvester.class.getCanonicalName());
  /** listeners */
  private HarvesterListenerArray listenerArray = new HarvesterListenerArray();
  /** message broker */
  private MessageBroker messageBroker;
  /** harvester configuration */
  private HarvesterConfiguration cfg;
  /** task queue */
  private TaskQueue taskQueue;
  /** pool of threads */
  private Pool pool;
  /** resource autoSelector */
  private AutoSelector autoSelector;
  /** watch-dog */
  private WatchDog watchDog;
  /** MBean name */
  private ObjectName name = null;

  /**
   * Creates instance of the engine.
   * @param messageBroker message broker
   * @param cfg harvest configuration
   */
  public Harvester(MessageBroker messageBroker, HarvesterConfiguration cfg) {
    if (messageBroker == null) {
      throw new IllegalArgumentException("No message broker provided.");
    }
    if (cfg == null) {
      throw new IllegalArgumentException("No configuration provided.");
    }
    this.messageBroker = messageBroker;
    this.cfg = cfg;

    try {
      MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
      name = new ObjectName("com.esri.gpt:type=Synchronizer");
      if (mbs.isRegistered(name)) {
        mbs.unregisterMBean(name);
      }
      mbs.registerMBean(this, name);
    } catch (Exception ex) {
      LOGGER.log(Level.SEVERE, "Error creating managed bean for Synchronizer.", ex);
    }
  }

  @Override
  protected void finalize() throws Throwable {
    if (name != null) {
      try {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        if (mbs.isRegistered(name)) {
          mbs.unregisterMBean(name);
        }
      } catch (Exception ex) {
        LOGGER.log(Level.SEVERE, "Error destroying managed bean for Synchronizer.", ex);
      }
    }
    super.finalize();
  }

  @Override
  public void startup() {
    if (!getRunning()) {
      init();
    } else {
      LOGGER.info("[SYNCHRONIZER] Synchronizer is running already.");
    }
  }

  /**
   * Initializes engine.
   */
  public void init() {
    if (cfg.getQueueEnabled()) {
      LOGGER.info("[SYNCHRONIZER] Initializing synchronizer queue.");
      this.taskQueue = new TaskQueue();
    }
    if (cfg.getActive()) {
      LOGGER.info("[SYNCHRONIZER] Initializing synchronizer engine.");
      this.taskQueue = new TaskQueue();
      this.pool = new Pool(
        new DataProcessorDispatcher(createDataProcessors(messageBroker, cfg.getBaseContextPath(), listenerArray)),
        taskQueue, cfg.getPoolSize());

      if (cfg.getAutoSelectFrequency() > 0) {
        this.autoSelector = new AutoSelector(cfg.getAutoSelectFrequency()) {

          @Override
          protected void onSelect(HrRecord resource) {
            if (ProtocolInvoker.getUpdateContent(resource.getProtocol())) {
              RequestContext context = RequestContext.extract(null);
              try {
                submit(context, resource, null, resource.getLastSyncDate()==null || HarvestPolicy.getInstance().getForceFullHarvest(resource)? null: resource.getLastSyncDate());
              } finally {
                context.onExecutionPhaseCompleted();
              }
            }
          }
        };
        Thread thread = new Thread(autoSelector, "Auto-selector");
        thread.start();
      }

      if (cfg.getWatchDogFrequency() > 0) {
        this.watchDog = new WatchDog(cfg.getWatchDogFrequency()) {

          @Override
          protected String[] getCurrentlyHarvesterResourceUuids() {
            ArrayList<String> uuids = new ArrayList<String>();
            for (ExecutionUnit u : pool.getAllExecutedUnits()) {
              uuids.add(u.getRepository().getUuid());
            }
            return uuids.toArray(new String[uuids.size()]);
          }

          @Override
          protected void cancelByResourceUuids(String[] uuids) {
            for (String uuid : uuids) {
              pool.drop(uuid);
            }
          }
        };
        Thread thread = new Thread(watchDog, "Watch-dog");
        thread.start();
      }

      // resetRunning from the last fail/shutdown
      resetRunning();
    }
  }

  @Override
  public boolean getRunning() {
    return pool != null && taskQueue != null;
  }

  /**
   * Shuts down harvesting engine.
   */
  @Override
  public void shutdown() {
    if (getRunning()) {
      LOGGER.info("[SYNCHRONIZER] Shutting down synchronizer.");
      if (autoSelector != null)
        autoSelector.shutdown();
      autoSelector = null;
      if (watchDog != null)
        watchDog.shutdown();
      watchDog = null;
      if (pool != null)
        pool.shutdown();
      pool = null;
      taskQueue = null;
    } else {
      LOGGER.info("[SYNCHRONIZER] Synchronizer shutted down already.");
    }
  }

  @Override
  public void setPoolSize(int size) {
    if (pool != null)
      pool.resize(size);
  }

  @Override
  public int getPoolSize() {
    return pool != null ? pool.size() : 0;
  }

  /**
   * Reselects harvesting sites.
   */
  public void reselect() {
    LOGGER.finer("[SYNCHRONIZER] Reselects resources.");
    if (autoSelector != null)
      autoSelector.reselect();
  }

  /**
   * Checks if is executing locally.
   * @param uuid resource UUID
   * @return <code>true</code> if is executing locally
   */
  public boolean isExecutingLocally(String uuid) {
    return pool != null ? pool.isExecuting(uuid) : false;
  }

  /**
   * Gets statistics for given repository.
   * @param uuid repository uuid.
   * @return statistics or <code>null</code> if statistics unavailable
   */
  public Statistics getStatistics(String uuid) {
    ExecutionUnit unit = pool != null ? pool.getExecutionUnitFor(uuid) : null;
    ExecutionUnitHelper helper = new ExecutionUnitHelper(unit);
    return helper.getReportBuilder();
  }

  /**
   * Submits new harvesting task for partial harvest.
   * @param context request context
   * @param resource resource to harvest
   * @param maxRecs maximum number of records to harvest (<code>null</code> for no maximum limit)
   * @param fromDate to harvest only from the specific date (<code>null</code> for no from date)
   * @return <code>true</code> if task has been submitted
   */
  public boolean submit(RequestContext context, HrRecord resource, Integer maxRecs, Date fromDate) {
    if (resource == null)
      throw new IllegalArgumentException("No resource to harvest provided.");
    // create instance of the task
    // add only if no similar task currently executing
    boolean submitted = false;
    if (ApprovalStatus.isPubliclyVisible(resource.getApprovalStatus().name()) && resource.getSynchronizable()) {
      CommonCriteria criteria = new CommonCriteria();
      criteria.setMaxRecords(maxRecs);
      if (fromDate != null) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(fromDate);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        fromDate = cal.getTime();
      }
      criteria.setFromDate(fromDate);
      submitted = !isExecutingLocally(resource.getUuid()) && (taskQueue != null ? taskQueue.add(context, resource, criteria) : false);
      LOGGER.log(Level.FINER, "[SYNCHRONIZER] Submitted resource: {0} ({1})", new Object[]{resource.getUuid(), resource.getName()});
    }
    return submitted;
  }

  /**
   * Spans new, separate thread exclusively for the resource.
   * @param context request context
   * @param resource resource to harvest
   * @param maxRecs maximum number of records to harvest (<code>null</code> for no maximum limit)
   * @param fromDate to harvest only from the specific date (<code>null</code> for no from date)
   * @return <code>true</code> if task has been sumbited
   */
  public boolean span(RequestContext context, HrRecord resource, Integer maxRecs, Date fromDate) {
    if (resource == null)
      throw new IllegalArgumentException("No resource to harvest provided.");
    // create instance of the task
    // add only if no similar task currently executing
    boolean submitted = false;
    if (resource.getApprovalStatus() == ApprovalStatus.approved && resource.getSynchronizable()) {
      CommonCriteria criteria = new CommonCriteria();
      criteria.setMaxRecords(maxRecs);
      criteria.setFromDate(fromDate);
      submitted = pool != null && taskQueue != null ? !pool.isExecuting(resource.getUuid()) && taskQueue.register(context, resource, criteria) : false;
      if (submitted)
        pool.span(resource, criteria);
      LOGGER.log(Level.FINER, "[SYNCHRONIZER] Submitted resource: {0} ({1})", new Object[]{resource.getUuid(), resource.getName()});
    }
    return submitted;
  }

  /**
   * Cancels pending tasks for given repository.
   * @param context request context
   * @param uuid repository uuid
   * @return <code>true</code> if has been canceled properly
   */
  public boolean cancel(RequestContext context, String uuid) {
    LOGGER.log(Level.FINER, "[SYNCHRONIZER] Canceled resource: {0}", uuid);
    // drop resource already being harvested
    boolean dropped = pool != null? pool.drop(uuid): false;
    // withdraw from the queue
    boolean canceled = taskQueue != null? taskQueue.cancel(context, uuid): false;
    // exit with status
    return canceled || dropped;
  }

  /**
   * Registers listener.
   * @param listener listener
   */
  public void registerListener(Listener listener) {
    this.listenerArray.add(listener);
  }

  /**
   * Removes listener.
   * @param listener listener
   */
  public void unregisterListener(Listener listener) {
    this.listenerArray.remove(listener);
  }

  /**
   * Removes all listeners.
   */
  public void removeAllListeners() {
    this.listenerArray.clear();
  }

  /**
   * Recovers records.
   */
  private void resetRunning() {
    RequestContext context = RequestContext.extract(null);
    try {
      HjResetRunningRequest recover = new HjResetRunningRequest(context);
      recover.execute();
      if (taskQueue!=null) {
        taskQueue.notifyChange();
      }
    } catch (SQLException ex) {
      LOGGER.log(Level.SEVERE, "[SYNCHRONIZER] Error recovering from the previous failout", ex);
    } finally {
      context.onExecutionPhaseCompleted();
    }
  }

  /**
   * Listener of events.
   */
  public static interface Listener {

    /**
     * Called on start of harvesting of the specific repository.
     * @param repository repository
     */
    void onHarvestStart(HrRecord repository);

    /**
     * Called on end of harvesting of the specific repository.
     * @param repository repository
     */
    void onHarvestEnd(HrRecord repository);

    /**
     * Called on harvest a single metadata from the repository.
     * @param repository repository
     * @param sourceUri metadata source URI
     * @param metadata metadata full text
     */
    void onHarvestMetadata(HrRecord repository, SourceUri sourceUri, String metadata);

    /**
     * Called on publish a single metadata from the repository.
     * @param repository repository
     * @param sourceUri metadata source URI
     * @param uuid metadata UUID
     * @param metadata metadata full text
     */
    void onPublishMetadata(HrRecord repository, SourceUri sourceUri, String uuid, String metadata);

    /**
     * Called on iteration exception.
     * @param repository repository
     * @param ex exception
     */
    void onIterationException(HrRecord repository, Exception ex);

    /**
     * Called on harvest exception.
     * @param repository repository
     * @param sourceUri metadata source URI
     * @param ex exception
     */
    void onHarvestException(HrRecord repository, SourceUri sourceUri, Exception ex);

    /**
     * Called on publish exception.
     * @param repository repository
     * @param sourceUri metadata source URI
     * @param metadata metadata full text
     * @param ex exception
     */
    void onPublishException(HrRecord repository, SourceUri sourceUri, String metadata, Exception ex);
  }

  /**
   * Array of listeners.
   */
  private class HarvesterListenerArray extends ArrayList<Harvester.Listener> implements Harvester.Listener {

    /**
     * Called on start of harvesting ofthe specific repository.
     * @param repository repository
     */
    @Override
    public void onHarvestStart(HrRecord repository) {
      for (Harvester.Listener l : this) {
        l.onHarvestStart(repository);
      }
    }

    /**
     * Called on end of harvesting ofthe specific repository.
     * @param repository repository
     */
    @Override
    public void onHarvestEnd(HrRecord repository) {
      for (Harvester.Listener l : this) {
        l.onHarvestEnd(repository);
      }
    }

    /**
     * Called on publish a single metadata from the repository.
     * @param repository repository
     * @param sourceUri metadata source URI
     * @param uuid metadata UUID
     * @param metadata metadata full text
     */
    @Override
    public void onHarvestMetadata(HrRecord repository, SourceUri sourceUri, String metadata) {
      for (Harvester.Listener l : this) {
        l.onHarvestMetadata(repository, sourceUri, metadata);
      }
    }

    /**
     * Called on publish a single metadata from the repository.
     * @param repository repository
     * @param sourceUri metadata source URI
     * @param uuid metadata UUID
     * @param metadata metadata full text
     */
    @Override
    public void onPublishMetadata(HrRecord repository, SourceUri sourceUri, String uuid, String metadata) {
      for (Harvester.Listener l : this) {
        l.onPublishMetadata(repository, sourceUri, uuid, metadata);
      }
    }

    /**
     * Called on iteration exception of a single metadata from the repository.
     * @param repository repository
     * @param sourceUri metadata source URI
     */
    @Override
    public void onIterationException(HrRecord repository, Exception ex) {
      for (Harvester.Listener l : this) {
        l.onIterationException(repository, ex);
      }
    }

    /**
     * Called on harvest exception of a single metadata from the repository.
     * @param repository repository
     * @param sourceUri metadata source URI
     */
    @Override
    public void onHarvestException(HrRecord repository, SourceUri sourceUri, Exception ex) {
      for (Harvester.Listener l : this) {
        l.onHarvestException(repository, sourceUri, ex);
      }
    }

    /**
     * Called on publish exception of a single metadata from the repository.
     * @param repository repository
     * @param sourceUri metadata source URI
     * @param metadata metadata full text
     */
    @Override
    public void onPublishException(HrRecord repository, SourceUri sourceUri, String metadata, Exception ex) {
      for (Harvester.Listener l : this) {
        l.onPublishException(repository, sourceUri, metadata, ex);
      }
    }
  }
 
  public void safeSuspend() {
    LOGGER.info("[SYNCHRONIZER] Suspending harvester");
    if (pool!=null) {
      pool.safeSuspend();
    }
    if (watchDog!=null) {
      watchDog.safeSuspend();
    }
    if (autoSelector!=null) {
      autoSelector.safeSuspend();
    }
  }
 
  public void safeResume() {
    LOGGER.info("[SYNCHRONIZER] Resuming harvester");
    if (autoSelector!=null) {
      autoSelector.safeResume();
    }
    if (watchDog!=null) {
      watchDog.safeResume();
    }
    if (pool!=null) {
      pool.safeResume();
    }
  }
 
  private List<DataProcessor> createDataProcessors(MessageBroker messageBroker, String baseContextPath, Harvester.Listener listener) {
    List<DataProcessor> processors = new ArrayList<DataProcessor>();
    for (DataProcessorFactory factory : cfg.getDataProcessorFactories()) {
      processors.add(factory.newProcessor(messageBroker, baseContextPath, listener));
    }
    return processors;
  }
 
  /**
   * Writes harvester engine statistics
   * @param writer the response writer
   * @param sb the response string builder
   * @throws Exception if exception occurs
   */
  public void writeStatistics(ResponseWriter writer,StringBuilder sb) throws Exception {
    HarvesterStatisticsCollector hsc = new HarvesterStatisticsCollector(pool, watchDog, taskQueue, messageBroker);
    hsc.writeStatistics(writer, sb);
  }
}
TOP

Related Classes of com.esri.gpt.control.webharvest.engine.Harvester$HarvesterListenerArray

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.