Last active
December 11, 2021 18:28
-
-
Save hugithordarson/de78813f7db2d3e97f33a8a7afbdda07 to your computer and use it in GitHub Desktop.
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 strimillinn.xperimental.async; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
@Retention( RetentionPolicy.RUNTIME ) | |
public @interface KVCAsync {} |
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 strimillinn.xperimental.async; | |
import java.lang.reflect.Method; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Map; | |
import java.util.Set; | |
import java.util.concurrent.CompletableFuture; | |
import java.util.concurrent.ExecutionException; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import com.webobjects.appserver.WOContext; | |
import er.extensions.components.ERXComponent; | |
/** | |
* Components that inherit from this class can annotate their methods with @KVCAsync. | |
* Methods annotated with @KVCAsync will be run simultaneously, instead of synchronously. | |
* Useful if you have a component that contains a lot of heavy methods and some concurrency can help. | |
*/ | |
public abstract class SMAsyncComponent extends ERXComponent { | |
private static final Logger logger = LoggerFactory.getLogger( SMAsyncComponent.class ); | |
/** | |
* Stores a list of all keys annotated with @KVCAsync | |
*/ | |
private Set<String> _asyncKeys; | |
/** | |
* Stores the values of asyncKeys, once they've been calculated. | |
*/ | |
private Map<String, Object> _asyncValues; | |
public SMAsyncComponent( WOContext context ) { | |
super( context ); | |
} | |
/** | |
* Indicates if you want to enable async resolution of methods annotated with @KVCAsync | |
*/ | |
protected boolean useKVCAsync() { | |
return true; | |
} | |
/** | |
* Checks if the requested key is annotated with @KVCAsync. The first time such a method is requested, | |
* all keys annotated with @KVCAsync are calculated simultaneously and the results then cached. | |
*/ | |
@Override | |
public Object valueForKey( String key ) { | |
if( useKVCAsync() ) { | |
if( isAsync( key ) ) { | |
if( _asyncValues == null ) { | |
populateAsyncValues(); | |
} | |
return _asyncValues.get( key ); | |
} | |
} | |
return super.valueForKey( key ); | |
} | |
/** | |
* @return A list of names of methods annotated with @KVCAsync | |
*/ | |
private Set<String> asyncKeys() { | |
if( _asyncKeys == null ) { | |
_asyncKeys = new HashSet<>(); | |
for( Method method : getClass().getDeclaredMethods() ) { | |
if( method.isAnnotationPresent( KVCAsync.class ) ) { | |
_asyncKeys.add( method.getName() ); | |
} | |
} | |
} | |
return _asyncKeys; | |
} | |
/** | |
* @return true if the given key references a method annotated with @KVCAsync | |
*/ | |
private boolean isAsync( String key ) { | |
return asyncKeys().contains( key ); | |
} | |
/** | |
* Populates the map of cached asynchronous values | |
*/ | |
private void populateAsyncValues() { | |
_asyncValues = Collections.synchronizedMap( new HashMap<>() ); | |
logger.debug( "Entering populateAsyncValues()" ); | |
try { | |
CompletableFuture<?>[] futures = new CompletableFuture[asyncKeys().size()]; | |
int i = 0; | |
for( String key : asyncKeys() ) { | |
futures[i++] = CompletableFuture.runAsync( () -> { | |
Long time = System.currentTimeMillis(); | |
_asyncValues.put( key, super.valueForKey( key ) ); | |
logger.info( "Populated KVCAsyncValue : " + key + " in " + (System.currentTimeMillis() - time) + "ms" ); | |
} ); | |
} | |
CompletableFuture.allOf( futures ).get(); | |
} | |
catch( InterruptedException | ExecutionException e ) { | |
throw new RuntimeException( "Failed to populate async fields", e ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment