Skip to content

Commit

Permalink
ability to register a boxlang object/function as a task
Browse files Browse the repository at this point in the history
  • Loading branch information
lmajano committed May 29, 2024
1 parent 62575fb commit 63b93ec
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 19 deletions.
4 changes: 2 additions & 2 deletions modules/bx-derby/models/DerbyDriver.bx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
* General:
* https://db.apache.org/derby/docs/10.17/devguide/rdevdvlp22102.html
*/
component{
class{
// @TODO: Custom Derby Driver implementation with helper utilities
}
}
88 changes: 73 additions & 15 deletions src/main/java/ortus/boxlang/runtime/async/tasks/ScheduledTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@
import ortus.boxlang.runtime.BoxRuntime;
import ortus.boxlang.runtime.async.executors.ExecutorRecord;
import ortus.boxlang.runtime.async.time.DateTimeHelper;
import ortus.boxlang.runtime.dynamic.IReferenceable;
import ortus.boxlang.runtime.events.BoxEvent;
import ortus.boxlang.runtime.interop.DynamicObject;
import ortus.boxlang.runtime.scopes.Key;
import ortus.boxlang.runtime.services.InterceptorService;
import ortus.boxlang.runtime.types.Function;
import ortus.boxlang.runtime.types.IStruct;
import ortus.boxlang.runtime.types.Struct;
import ortus.boxlang.runtime.util.StringUtil;
import ortus.boxlang.runtime.util.Timer;

/**
Expand Down Expand Up @@ -401,15 +404,31 @@ public void run( Boolean force ) {
}

// Execution by type
if ( task instanceof DynamicObject castedTask ) {
this.stats.put( "lastResult", Optional.ofNullable( castedTask.invoke( method ) ) );
} else if ( task instanceof Callable<?> castedTask ) {
this.stats.put( "lastResult", Optional.ofNullable( castedTask.call() ) );
} else if ( task instanceof Runnable castedTask ) {
castedTask.run();
this.stats.put( "lastResult", Optional.empty() );
} else {
throw new IllegalArgumentException( "Task is not a DynamicObject or a Callable or a Runnable" );
switch ( task ) {
case DynamicObject castedTask -> {
this.stats.put( "lastResult", Optional.ofNullable( castedTask.invoke( method ) ) );
}
case Callable<?> castedTask -> {
this.stats.put( "lastResult", Optional.ofNullable( castedTask.call() ) );
}
case Runnable castedTask -> {
castedTask.run();
this.stats.put( "lastResult", Optional.empty() );
}
case Function castedTask -> {
castedTask.invoke(
Function.generateFunctionContext(
castedTask, // the function
BoxRuntime.getInstance().getRuntimeContext(), // we use the runtime context
castedTask.getName(), // the function name
new Object[] {}, // no args
null // No class, lambda/closure
)
);
}
default -> {
throw new IllegalArgumentException( "Task is not a DynamicObject or a Callable or a Runnable" );
}
}

// Get the last result
Expand Down Expand Up @@ -636,7 +655,11 @@ public ScheduledTask onFailure( BiConsumer<ScheduledTask, Exception> target ) {
* --------------------------------------------------------------------------
* Task Registration Methods
* --------------------------------------------------------------------------
* A task can be represented either by a DynamicObject or a Java Lambda.
* A task can be represented either by many approved types:
* - DynamicObject
* - Java Lambda (Callable, Runnable)
* - BoxLang Function
* - BoxLang Object + method
* Here is where you can register the task to be executed.
*/

Expand Down Expand Up @@ -686,16 +709,38 @@ public ScheduledTask call( Runnable task ) {
}

/**
* This method is used to register the callable DynamicObject/Callable Lambda on this scheduled task.
* This method is used to register any executable as a scheduled task.
*
* @param task The DynamicObject/Callable Lambda to register
* @param method The method to execute in the DynamicObject/Callable Lambda, by default it is run()
* @param task The object to register as an executable task
*
* @return The ScheduledTask instance
*/
public ScheduledTask call( Object task ) {
return call( task, null );
}

/**
* This method is used to register any object that is either:
* - DynamicObject
* - IReferenceable
* - Callable
* - Runnable
* as a scheduled task.
*
* @param task The object to register as an executable task
* @param method The method to execute in the object, by default it is run()
*
* @return The ScheduledTask instance
*/
public ScheduledTask call( Object task, String method ) {
debugLog( "call" );

// If the task is an IReferenceable then wrap them in a DynamicObject
if ( task instanceof IReferenceable castedTask ) {
task = DynamicObject.of( castedTask );
}

// Store them up!
setTask( task );
setMethod( method == null ? "run" : method );

Expand Down Expand Up @@ -994,6 +1039,19 @@ public ScheduledTask withNoOverlaps() {
return this;
}

/**
* BoxLang proxy
*
* @param period The period of execution
* @param timeunit The time unit to use, available units are: days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default
*
* @return The ScheduledTask instance
*/
public ScheduledTask every( Double period, String timeUnit ) {
timeUnit = StringUtil.pluralize( timeUnit ).toUpperCase();
return every( period.longValue(), TimeUnit.valueOf( timeUnit ) );
}

/**
* Run the task every custom period of execution
*
Expand Down Expand Up @@ -1943,15 +2001,15 @@ public ScheduledTask setGroup( String group ) {
* @return the group
*/
public String getGroup() {
return group;
return this.group;
}

/**
* Get the task dynamic object that will be executed by the task.
* Must implement the run() method or use the {@code method} property.
*/
public Object getTask() {
return task;
return this.task;
}

/**
Expand Down
83 changes: 81 additions & 2 deletions src/test/bx/Scheduler.bx
Original file line number Diff line number Diff line change
@@ -1,7 +1,86 @@
/**
* A BoxLang scheduler is a BoxLang class that at runtime
* will be dynamically loaded, decorated and modified to
* work as a native Java BoxLang Scheduler.
*/
@BoxScheduler "My Custom Scheduler"
class {

function main( args = [] ){
println( "Running from my custom scheduler" );
/**
* The configure method is called by the BoxLang runtime
* to allow the scheduler to configure itself.
*
* This is where you define your tasks and setup global configuration.
*/
function configure(){

// Define a lambda task
task( "My Task" )
.call( () -> {
println( "I am a lambda task: #now()#" );
} )
.every( 1, "second" );
}

/**
* --------------------------------------------------------------------------
* Life - Cycle Callbacks
* --------------------------------------------------------------------------
*/

/**
* Called before the scheduler is going to be shutdown
*/
function onShutdown(){

}

/**
* Called after the scheduler has registered all schedules
*/
function onStartup(){
println( "I have started!" & getName() );
}

/**
* Called whenever ANY task fails
*
* @task The task that got executed
* @exception The exception object
*/
function onAnyTaskError( task, exception ){
println( "Any task [#task.getName()#] blew up " & exception.getMessage() );
}

/**
* Called whenever ANY task succeeds
*
* @task The task that got executed
* @result The result (if any) that the task produced as an Optional
*/
function onAnyTaskSuccess( task, result ){
println( "on any task success [#task.getName()#]" );
println( "results for task are: " & result.orElse( "No result" ) );
}

/**
* Called before ANY task runs
*
* @task The task about to be executed
*/
function beforeAnyTask( task ){
println( "before any task [#task.getName()#]" );
}

/**
* Called after ANY task runs
*
* @task The task that got executed
* @result The result (if any) that the task produced as an Optional
*/
function afterAnyTask( task, result ){
println( "after any task completed [#task.getName()#]" );
println( "results for task are: " & result.orElse( "No result" ) );
}

}

0 comments on commit 63b93ec

Please sign in to comment.