MultiCallback.java

/*
** Module   : MultiCallback.java
** Abstract : Multiple service callbacks.
**
** Copyright (c) 2017, Golden Code Development Corporation.
**
** -#- -I- --Date-- --------------------------------Description-----------------------------------
** 001 HC  20170612 Initial version.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** 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.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
**
**   0. Attribution Requirement.
**
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
**
**   1. No License To Use Trademarks.
**
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
**
**   2. No Misrepresentation of Affiliation.
**
**     You may not represent yourself as Golden Code Development Corporation or FWD.
**
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
**
**   3. No Misrepresentation of Source or Origin.
**
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/


package com.goldencode.p2j.admin.client;

import com.goldencode.p2j.admin.client.application.*;
import com.goldencode.p2j.admin.shared.*;

import java.util.*;
import java.util.logging.*;

/**
 * The class allows to issue multiple asynchronous service calls and handle results when all
 * calls finish.
 */
public class MultiCallback
{
   /** Logger */
   private Logger log = Logger.getLogger(MultiCallback.class.getSimpleName());

   /** Admin service reference */
   private AdminServiceAsync adm;

   /** Alarm service reference */
   private Alarm alarm;

   /* index 0 - Child, 1 - result, 2 - exception */
   private ArrayList<AdminCallback> callbacks = new ArrayList<>();

   /** The number of finished calls */
   private int callbacksDone;

   /** The handler called when all calls finish */
   private Runnable doneHandler;

   /**
    * Default ctor.
    */
   public MultiCallback()
   {
   }

   /**
    * Ctor.
    *
    * @param   adm
    *          Admin service reference
    *
    * @param   alarm
    *          Alarm service reference
    */
   public MultiCallback(AdminServiceAsync adm, Alarm alarm)
   {
      this.adm = adm;
      this.alarm = alarm;
   }

   /**
    * Creates new instance of {@link AdminCallback} that can be used in a service call.
    *
    * @param   <T>
    *          The result type.
    *
    * @return  {@link AdminCallback} reference
    */
   public <T> AdminCallback<T> newCallback()
   {
      AdminCallback<T> cb = new AdminCallback<T>(adm, null)
      {
         @Override
         public void onDone(T result)
         {
            childFinished(this);
         }
      };

      callbacks.add(cb);
      return cb;
   }

   /**
    * Takes a {@link Runnable} that will be called when all service calls finish.
    *
    * @param   run
    *          A {@link Runnable}.
    */
   public void onDone(Runnable run)
   {
      doneHandler = run;
   }

   /**
    * Returns <code>true</code> if all the service calls succeed.
    *
    * @return  success flag
    */
   public boolean isSuccess()
   {
      boolean isSuccess = true;
      for (AdminCallback c : callbacks)
      {
         isSuccess &= c.isSuccess();
      }
      return isSuccess;
   }

   /**
    * Returns all the server messages of all the failed callbacks.
    *
    * @return  list of server messages, an empty list if there are no messages
    */
   public List<String> getServerMessages()
   {
      List<String> messages = new ArrayList<>();
      for (AdminCallback c : callbacks)
      {
         messages.addAll(c.getServerMessages());
      }

      return messages;
   }

   /**
    * All the exceptions of all the failed service calls.
    *
    * @return  list of exceptions or an empty list if no exceptions
    */
   public List<Throwable> getExceptions()
   {
      ArrayList<Throwable> callbackExceptions = new ArrayList<>();
      for (AdminCallback c : callbacks)
      {
         Throwable t = c.getException();
         if (t != null)
         {
            callbackExceptions.add(t);
         }
      }

      return callbackExceptions;
   }

   /**
    * Called when all the child callbacks have finished.
    *
    * @param   doneHandler
    *          The runnable called when all the results are processed.
    */
   protected void handleAllFinished(Runnable doneHandler)
   {
      if (isSuccess())
      {
         // this is a success
         if (doneHandler != null)
         {
            doneHandler.run();
         }
         return;
      }

      List<String> messages = getServerMessages();
      List<Throwable> callbackExceptions = getExceptions();

      if (!callbackExceptions.isEmpty())
      {
         String msg = "Server call error";
         log.severe(msg);
         for (Throwable t : callbackExceptions)
         {
            log.log(Level.SEVERE, "", t);
         }
         messages.add(0, msg);
      }

      if (alarm != null && !messages.isEmpty())
      {
         alarm.ring(messages, doneHandler);
      }
      else
      {
         if (doneHandler != null)
         {
            doneHandler.run();
         }
      }
   }

   /**
    * A handler of single service result.
    *
    * @param   cb
    *          An {@link AdminCallback}.
    */
   private synchronized void childFinished(AdminCallback cb)
   {
      callbacksDone++;
      if (callbacksDone < callbacks.size())
      {
         //  not all callbacks have finished
         return;
      }

      handleAllFinished(doneHandler);
   }
}