content: /*
   * Copyright (C) 2013 4th Line GmbH, Switzerland
   *
   * The contents of this file are subject to the terms of either the GNU
   * Lesser General Public License Version 2 or later ("LGPL") or the
   * Common Development and Distribution License Version 1 or later
   * ("CDDL") (collectively, the "License"). You may not use this file
   * except in compliance with the License. See LICENSE.txt for more
   * information.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   */
  
  package org.fourthline.cling.protocol;
  
  import org.fourthline.cling.UpnpService;
  import org.fourthline.cling.model.message.UpnpMessage;
  import org.fourthline.cling.model.message.header.UpnpHeader;
  import org.fourthline.cling.transport.RouterException;
  import org.seamless.util.Exceptions;
  
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  /**
   * Supertype for all asynchronously executing protocols, handling reception of UPnP messages.
   * <p>
   * After instantiation by the {@link ProtocolFactory}, this protocol <code>run()</code>s and
   * calls its own {@link #waitBeforeExecution()} method. By default, the protocol does not wait
   * before then proceeding with {@link #execute()}.
   * </p>
   *
   * @param <M> The type of UPnP message handled by this protocol.
   *
   * @author Christian Bauer
   */
  public abstract class ReceivingAsync<M extends UpnpMessage> implements Runnable {
  
      final private static Logger log = Logger.getLogger(UpnpService.class.getName());
  
      private final UpnpService upnpService;
  
      private M inputMessage;
  
      protected ReceivingAsync(UpnpService upnpService, M inputMessage) {
          this.upnpService = upnpService;
          this.inputMessage = inputMessage;
      }
  
      public UpnpService getUpnpService() {
          return upnpService;
      }
  
      public M getInputMessage() {
          return inputMessage;
      }
  
      public void run() {
          boolean proceed;
          try {
              proceed = waitBeforeExecution();
          } catch (InterruptedException ex) {
              log.info("Protocol wait before execution interrupted (on shutdown?): " + getClass().getSimpleName());
              proceed = false;
          }
  
          if (proceed) {
              try {
                  execute();
              } catch (Exception ex) {
                  Throwable cause = Exceptions.unwrap(ex);
                  if (cause instanceof InterruptedException) {
                      log.log(Level.INFO, "Interrupted protocol '" + getClass().getSimpleName() + "': " + ex, cause);
                  } else {
                      throw new RuntimeException(
                          "Fatal error while executing protocol '" + getClass().getSimpleName() + "': " + ex, ex
                      );
                  }
              }
          }
      }
  
      /**
       * Provides an opportunity to pause before executing the protocol.
       *
       * @return <code>true</code> (default) if execution should continue after waiting.
       *
       * @throws InterruptedException If waiting has been interrupted, which also stops execution.
       */
      protected boolean waitBeforeExecution() throws InterruptedException {
          // Don't wait by default
          return true;
      }
  
      protected abstract void execute() throws RouterException;
  
      protected <H extends UpnpHeader> H getFirstHeader(UpnpHeader.Type headerType, Class<H> subtype) {
          return getInputMessage().getHeaders().getFirstHeader(headerType, subtype);
      }
  
      @Override
      public String toString() {
          return "(" + getClass().getSimpleName() + ")";
      }
  
  }
  