Last active
August 29, 2015 14:25
-
-
Save markzyu/8533bbdabd565ca8aaf7 to your computer and use it in GitHub Desktop.
Guess how you can utilize this on Android! (Please use Java 7 and retrolambda for a nice lambda support. Java 8's lambda support might malfunction)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package zyu19.prototypes.paperplane.connect; | |
import java.util.ArrayList; | |
import zyu19.prototypes.paperplane.connect.config.*; | |
public class ActionChain<ThisType extends ActionChain> implements ErrorHolder, FakePromise<ThisType> { | |
//------------- public functions (ErrorHolder Interface) ---------------- | |
private Exception mCause; | |
@Override | |
public final Exception getCause() { | |
return mCause; | |
} | |
@Override | |
public final void retry() { | |
iterate(); | |
} | |
//------------- public functions (FakePromise Interface) ---------------- | |
@Override | |
public final ThisType start() { | |
clearIterationState(); | |
iterate(); | |
return (ThisType)this; | |
} | |
@Override | |
public final ThisType fail(Consumer<ErrorHolder> onFailure) { | |
mCurrentOnFailure = onFailure; | |
return (ThisType)this; | |
} | |
@Override | |
public <In, Out> ThisType then(boolean runOnWorkerThread, PureAction<In, Out> action) { | |
mActionSequence.add(new ActionConfig(action, mCurrentOnFailure, runOnWorkerThread)); | |
return (ThisType)this; | |
} | |
@Override | |
public final <In, Out> ThisType netThen(PureAction<In, Out> action) { | |
mActionSequence.add(new ActionConfig(action, mCurrentOnFailure, true)); | |
return (ThisType)this; | |
} | |
@Override | |
public final <In, Out> ThisType uiThen(PureAction<In, Out> action) { | |
mActionSequence.add(new ActionConfig(action, mCurrentOnFailure, false)); | |
return (ThisType)this; | |
} | |
//------------- Constructors ---------------- | |
private final Consumer mOnSuccess; | |
private Consumer<ErrorHolder> mCurrentOnFailure; | |
private ArrayList<ActionConfig> mActionSequence = new ArrayList<>(); | |
protected final ThreadPolicy mThreadPolicy; | |
protected ActionChain(ThreadPolicy threadPolicy, Consumer<?> onSuccess) { | |
mThreadPolicy = threadPolicy; | |
mOnSuccess = onSuccess; | |
} | |
protected ActionChain(ThreadPolicy threadPolicy, Consumer<?> onSuccess, Consumer<ErrorHolder> onFailure) { | |
mThreadPolicy = threadPolicy; | |
mOnSuccess = onSuccess; | |
mCurrentOnFailure = onFailure; | |
} | |
//------------------------ [Private] Iteration State ------------------------------- | |
private int mNextAction; | |
private Object mLastActionOutput; | |
private boolean isOnSuccessCalled; | |
private final void clearIterationState() { | |
mNextAction = 0; | |
mLastActionOutput = null; | |
isOnSuccessCalled = false; | |
} | |
private final boolean isIterationOver() { | |
if (isOnSuccessCalled) | |
return true; | |
else if (mNextAction >= mActionSequence.size()) { | |
mThreadPolicy.switchAndRun(mOnSuccess, mLastActionOutput); | |
isOnSuccessCalled = true; | |
return true; | |
} else return false; | |
} | |
//------------------------ [Private] iterate and iterator --------------------------- | |
private final Consumer<ThreadPolicy> mIterator = (threadPolicy) -> { | |
synchronized (ActionChain.this) { | |
if (isIterationOver()) | |
return; | |
ActionConfig action = mActionSequence.get(mNextAction); | |
try { | |
mLastActionOutput = action.pureAction.process(mLastActionOutput); | |
} catch (Exception err) { | |
mCause = err; | |
threadPolicy.switchAndRun(action.errorHandler, this); | |
return; | |
} | |
mNextAction++; | |
iterate(); | |
} | |
}; | |
private final void iterate() { | |
synchronized (this) { | |
if (isIterationOver()) | |
return; | |
ActionConfig action = mActionSequence.get(mNextAction); | |
workOnProperThread(() -> mIterator.consume(mThreadPolicy), action); | |
} | |
} | |
//------------------------ policy ---------------------------- | |
/** | |
* This function decides which thread a task defined by actionConfig should run on. <br> | |
* Note: onSuccess and onFailure will always run on the thread selected by ThreadChanger, regardless | |
* of this function's configuration. | |
*/ | |
protected void workOnProperThread(Runnable task, ActionConfig actionConfig) { | |
if (actionConfig.runOnWorkerThread) | |
mThreadPolicy.runWorker(task); | |
else mThreadPolicy.switchAndRun(task); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package zyu19.prototypes.paperplane.connect.config; | |
import zyu19.prototypes.paperplane.connect.ActionChain; | |
/** | |
* @author Zhongzhi Yu | |
* Created on 7/21/2015. | |
*/ | |
public class ActionConfig<In, Out> { | |
final public PureAction<In, Out> pureAction; | |
final public Consumer<ActionChain> errorHandler; | |
final public boolean runOnWorkerThread; | |
public ActionConfig(PureAction<In, Out> pureAction, Consumer<ActionChain> errorHandler, boolean runOnWorkerThread) { | |
this.pureAction = pureAction; | |
this.errorHandler = errorHandler; | |
this.runOnWorkerThread = runOnWorkerThread; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package zyu19.prototypes.paperplane.connect.config; | |
/** | |
* For use with retro-lambda and Java 7. | |
* Not annotated, so it probably cannot be instantiated with lambda in Java 8. | |
* @author Zhongzhi Yu | |
* Created on 6/28/2015. | |
*/ | |
public interface Consumer<ArgType> { | |
void consume(ArgType arg); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package zyu19.prototypes.paperplane.connect.config; | |
/** | |
* ErrorHolder Interface. | |
* <p> | |
* Basically, instances of this class is used like Exception. But the following instructions | |
* are very important. | |
* <p> | |
* Instances of this type will be passed to the user when an error occurred, | |
* to inform the user to decide whether the error is recoverable. | |
* <p> | |
* If possibly recoverable, the user should check the type of that error and | |
* decide whether to use this ErrorHolder to recover the halted process. | |
* <p> | |
* If not recoverable at all, the user can record the error. | |
* <p> | |
* NOTE: The user must retry through this ErrorHolder within the 'Consumer' callback. | |
* Calling the function again, from the outside, will NOT succeed. | |
* <p> | |
* NOTE: ThreadPolicy will ensure that this class runs on the thread specified | |
* by ThreadChanger. | |
* <p> | |
* WARNING: It should be avoided to pass ErrorHolder to threads other than | |
* the thread of the 'Consumer' callback. | |
* | |
* @author Zhongzhi Yu | |
* Created on 7/2/2015. | |
* Modified on 7/21/2015. | |
* Isolated as Interface on 7/24/2015. | |
*/ | |
public interface ErrorHolder { | |
Exception getCause(); | |
void retry(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package zyu19.prototypes.paperplane.connect.config; | |
/** | |
* Behaviors of this interface does NOT follow the standard of Promise-then. | |
* @author Zhongzhi Yu | |
* Created on 7/24/2015. | |
*/ | |
public interface FakePromise <ThisType extends FakePromise> { | |
// start running this promise. Once started, the promise should NOT be started again. | |
ThisType start(); | |
// The following function must not be declared. | |
// Because onSuccess handler should be a final field in implementations. | |
// <AnsType> ThisType done(Consumer<AnsType> onSuccess); | |
// set the onFailure callback for the PureAction callbacks added afterwards. | |
ThisType fail(Consumer<ErrorHolder> onFailure); | |
// then().then().then() | |
<In, Out> ThisType then(boolean runOnWorkerThread, PureAction<In, Out> action); | |
<In, Out> ThisType netThen(PureAction<In, Out> action); | |
<In, Out> ThisType uiThen(PureAction<In, Out> action); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Copyright (c) 2015, Zhongzhi Yu | |
All rights reserved. | |
Redistribution and use in source and binary forms, with or without | |
modification, are permitted provided that the following conditions are met: | |
* Redistributions of source code must retain the above copyright notice, this | |
list of conditions and the following disclaimer. | |
* Redistributions in binary form must reproduce the above copyright notice, | |
this list of conditions and the following disclaimer in the documentation | |
and/or other materials provided with the distribution. | |
* Neither the name of Zhongzhi Yu nor the names of its contributors | |
may be used to endorse or promote products derived from this software | |
without specific prior written permission. | |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package zyu19.prototypes.paperplane.connect.config; | |
/** | |
* @author Zhongzhi Yu | |
* Created on 7/20/2015. | |
*/ | |
public interface PureAction<In, Out> { | |
Out process(In input) throws Exception; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package zyu19.prototypes.paperplane.connect.config; | |
/** | |
* @author Zhongzhi Yu | |
* Created on 5/15/2015. | |
*/ | |
public interface ThreadChanger { | |
<ArgType> void runCallbackOnWantedThread(Runnable runnable); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package zyu19.prototypes.paperplane.connect.config; | |
import com.google.common.util.concurrent.ListeningExecutorService; | |
import com.google.common.util.concurrent.MoreExecutors; | |
import java.util.concurrent.ExecutorService; | |
/** | |
* This class encapsulates Guava's ListeningExecutorService to simply the process to | |
* create an asynchronous call. Full lambda support for use with Retro-lambda. | |
* | |
* @author ZhongzhiYu | |
* Created on 5/15/2015. | |
* Separated as a single file by Zhongzhi Yu on 5/17/2015, | |
* Largely modified on 6/28/2015. | |
*/ | |
public class ThreadPolicy { | |
private final ThreadChanger mThreadChanger; | |
private final ListeningExecutorService mListeningExecutorService; | |
public ThreadPolicy(ThreadChanger threadChanger, ExecutorService multiThreadConfig) { | |
mListeningExecutorService = MoreExecutors.listeningDecorator(multiThreadConfig); | |
mThreadChanger = threadChanger; | |
} | |
public ThreadPolicy(ExecutorService multiThreadConfig) { | |
mListeningExecutorService = MoreExecutors.listeningDecorator(multiThreadConfig); | |
mThreadChanger = null; | |
} | |
public void runWorker(Runnable operation) { | |
mListeningExecutorService.submit(operation); | |
} | |
public <ArgType> void switchAndRun(Consumer<ArgType> consumer, ArgType arg) { | |
if (consumer == null) | |
return; | |
if (mThreadChanger == null) | |
consumer.consume(arg); | |
else { | |
mThreadChanger.runCallbackOnWantedThread(() -> { | |
consumer.consume(arg); | |
}); | |
} | |
} | |
public void switchAndRun(Runnable runnable) { | |
if (runnable == null) | |
return; | |
if (mThreadChanger == null) | |
runnable.run(); | |
else { | |
mThreadChanger.runCallbackOnWantedThread(runnable); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The following picture is the illustration of how this class (is supposed to) run different PureAction code blocks on "W" (Worker) thread and "User" (UI/Main) thread.
© 2015 Zhongzhi Yu. All rights reserved.