Skip to content

Commit

Permalink
[New] Added a ExecutorPool which is accessible in the runtime for asy…
Browse files Browse the repository at this point in the history
…nc operations

Now the input listeners don't create their own executors and/or threads anymore, but use Future tasks to manage the input listener threads.

Further the pulseAsync and blinkAsync methods now simply call context.submitTask() with a lambda which calls the relevant non-async blink or pulse methods.
  • Loading branch information
eitch committed Feb 19, 2024
1 parent bcb829c commit 6eeb129
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 259 deletions.
133 changes: 70 additions & 63 deletions pi4j-core/src/main/java/com/pi4j/context/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import com.pi4j.registry.Registry;
import com.pi4j.util.PropertiesUtil;
import com.pi4j.util.StringUtil;

import java.util.Map;
import java.util.concurrent.Future;

Expand All @@ -60,50 +61,60 @@
* @author Robert Savage (<a href="http://www.savagehomeautomation.com">http://www.savagehomeautomation.com</a>)
* @version $Id: $Id
*/
public interface Context extends Describable,
IOCreator,
ProviderProvider,
InitializedEventProducer<Context>,
ShutdownEventProducer<Context> {
public interface Context extends Describable, IOCreator, ProviderProvider, InitializedEventProducer<Context>,
ShutdownEventProducer<Context> {

/**
* <p>config.</p>
*
* @return a {@link com.pi4j.context.ContextConfig} object.
*/
ContextConfig config();

/**
* <p>properties.</p>
*
* @return a {@link com.pi4j.context.ContextProperties} object.
*/
ContextProperties properties();

/**
* <p>providers.</p>
*
* @return a {@link com.pi4j.provider.Providers} object.
*/
Providers providers();

/**
* <p>registry.</p>
*
* @return a {@link com.pi4j.registry.Registry} object.
*/
Registry registry();

/**
* <p>platforms.</p>
*
* @return a {@link com.pi4j.platform.Platforms} object.
*/
Platforms platforms();

/**
* Submits the given task for async execution
*
* @param task the task to execute asynchronously
*
* @return the task to cancel later
*/
Future<?> submitTask(Runnable task);

/**
* <p>shutdown.</p>
*
* @return a {@link com.pi4j.context.Context} object.
* @throws com.pi4j.exception.ShutdownException if an error occurs during shutdown.
*/
Context shutdown() throws ShutdownException;
Context shutdown() throws ShutdownException;

/**
*
Expand All @@ -127,7 +138,7 @@ public interface Context extends Describable,
* @param <P> a P object.
* @return a P object.
*/
default <P extends Platform> P platform(){
default <P extends Platform> P platform() {
return platforms().getDefault();
}

Expand All @@ -137,7 +148,7 @@ default <P extends Platform> P platform(){
* @param <P> a P object.
* @return a P object.
*/
default <P extends Platform> P getPlatform(){
default <P extends Platform> P getPlatform() {
return this.platform();
}

Expand All @@ -147,7 +158,7 @@ default <P extends Platform> P getPlatform(){
* @param <P> a P object.
* @return a P object.
*/
default <P extends Platform> P getDefaultPlatform(){
default <P extends Platform> P getDefaultPlatform() {
return this.platform();
}

Expand All @@ -157,26 +168,26 @@ default <P extends Platform> P getDefaultPlatform(){
* @param <P> a P object.
* @return a P object.
*/
default <P extends Platform> P defaultPlatform(){
default <P extends Platform> P defaultPlatform() {
return this.platform();
}

/**
* <p>platform.</p>
*
* @param id Id of the platform.
* @param id Id of the platform.
* @param <P> the platform type
* @return a P object.
* @throws PlatformNotFoundException if platform specified by {@code id} is not found.
*/
default <P extends Platform> P platform(String id) throws PlatformNotFoundException {
return (P)this.platforms().get(id);
return (P) this.platforms().get(id);
}

/**
* <p>platform.</p>
*
* @param id Id of the platform.
* @param id Id of the platform.
* @param <P> the platform type
* @return a P object.
* @throws PlatformNotFoundException if platform specified by {@code id} is not found.
Expand Down Expand Up @@ -237,20 +248,20 @@ default boolean hasPlatform(Class<? extends Platform> platformClass) throws Plat

/** {@inheritDoc} */
default <T extends Provider> T provider(String providerId) throws ProviderNotFoundException {
return (T)providers().get(providerId);
return (T) providers().get(providerId);
}

/** {@inheritDoc} */
default <T extends Provider> T provider(String providerId, Class<T> providerClass) throws ProviderNotFoundException {
return (T)providers().get(providerId);
default <T extends Provider> T provider(String providerId, Class<T> providerClass)
throws ProviderNotFoundException {
return (T) providers().get(providerId);
}

/** {@inheritDoc} */
default boolean hasProvider(String providerId) {
try {
return providers().exists(providerId);
}
catch (Exception e){
} catch (Exception e) {
return false;
}
}
Expand All @@ -266,13 +277,14 @@ default <T extends Provider> boolean hasProvider(Class<T> providerClass) {
}

/** {@inheritDoc} */
default <T extends Provider> T provider(Class<T> providerClass) throws ProviderNotFoundException, ProviderInterfaceException {
default <T extends Provider> T provider(Class<T> providerClass)
throws ProviderNotFoundException, ProviderInterfaceException {
// return the default provider for this type from the default platform
if(platform() != null && platform().hasProvider(providerClass))
if (platform() != null && platform().hasProvider(providerClass))
return platform().provider(providerClass);

// return the default provider for this type (outside of default platform)
if(providers().exists(providerClass))
if (providers().exists(providerClass))
return providers().get(providerClass);

// provider not found
Expand All @@ -282,85 +294,83 @@ default <T extends Provider> T provider(Class<T> providerClass) throws ProviderN
/** {@inheritDoc} */
default <T extends Provider> T provider(IOType ioType) throws ProviderNotFoundException {
// return the default provider for this type from the default platform
if(platform() != null && platform().hasProvider(ioType))
if (platform() != null && platform().hasProvider(ioType))
return platform().provider(ioType);

// return the default provider for this type (outside of default platform)
if(providers().exists(ioType))
if (providers().exists(ioType))
return providers().get(ioType);

// provider not found
throw new ProviderNotFoundException(ioType);
}


// ------------------------------------------------------------------------
// I/O INSTANCE ACCESSOR/CREATOR METHODS
// ------------------------------------------------------------------------

@Override
default <I extends IO>I create(IOConfig config, IOType ioType) {
default <I extends IO> I create(IOConfig config, IOType ioType) {

// create by explicitly configured IO <PLATFORM> from IO config
String platformId = config.platform();
if(StringUtil.isNotNullOrEmpty(platformId)) {
if (StringUtil.isNotNullOrEmpty(platformId)) {
// resolve the platform and use it to create the IO instance
Platform platform = this.platforms().get(platformId);
return platform.create(config, ioType);
}

// create by explicitly configured IO <PROVIDER> from IO config
String providerId = config.provider();
if(StringUtil.isNotNullOrEmpty(providerId)) {
if (StringUtil.isNotNullOrEmpty(providerId)) {
// resolve the provider and use it to create the IO instance
Provider provider = this.providers().get(providerId, ioType);
return (I)provider.create(config);
return (I) provider.create(config);
}

// get implicitly defined provider (defined by IO type)
// (this is the default or platform defined provider for this particular IO type)
if(ioType != null) {
if (ioType != null) {
// resolve the provider and use it to create the IO instance
Provider provider = this.provider(ioType);
return (I)provider.create(config);
return (I) provider.create(config);
}

// unable to resolve the IO type and thus unable to create I/O instance
throw new IOException("This IO instance [" + config.id() +
"] could not be created because it does not define one of the following: 'PLATFORM', 'PROVIDER', or 'I/O TYPE'.");
throw new IOException("This IO instance [" + config.id() + "] could not be created because it does not define one of the following: 'PLATFORM', 'PROVIDER', or 'I/O TYPE'.");
}

@Override
default <T extends IO>T create(String id) {
default <T extends IO> T create(String id) {
Provider provider = null;

// resolve inheritable properties from the context based on the provided 'id' for this IO instance
Map<String,String> inheritedProperties = PropertiesUtil.subProperties(this.properties().all(), id);
Map<String, String> inheritedProperties = PropertiesUtil.subProperties(this.properties().all(), id);

// create by explicitly configured IO <PLATFORM> from IO inheritable properties
if(inheritedProperties.containsKey("platform")){
if (inheritedProperties.containsKey("platform")) {
// resolve the platform and use it to create the IO instance
String platformId = inheritedProperties.get("platform");
Platform platform = this.platforms().get(platformId);
return platform.create(id);
}

// create by explicitly configured IO <PROVIDER> from IO config
if(inheritedProperties.containsKey("provider")){
if (inheritedProperties.containsKey("provider")) {
String providerId = inheritedProperties.get("provider");
// resolve the provider and use it to create the IO instance
provider = this.providers().get(providerId);
}

// create by IO TYPE
// (use platform provider if one if available for this IO type)
if(provider == null && inheritedProperties.containsKey("type")){
if (provider == null && inheritedProperties.containsKey("type")) {
IOType ioType = IOType.parse(inheritedProperties.get("type"));
provider = provider(ioType);
}

// validate resolved provider
if(provider == null) {
if (provider == null) {
// unable to resolve the IO type and thus unable to create I/O instance
throw new IOException("This IO instance [" + id +
"] could not be created because it does not define one of the following: 'PLATFORM', 'PROVIDER', or 'I/O TYPE'.");
Expand All @@ -370,32 +380,32 @@ default <T extends IO>T create(String id) {
ConfigBuilder builder = provider.type().newConfigBuilder(this);
builder.id(id);
builder.load(inheritedProperties);
return (T)provider.create((Config) builder.build());
return (T) provider.create((Config) builder.build());
}

@Override
default <T extends IO>T create(String id, IOType ioType) {
default <T extends IO> T create(String id, IOType ioType) {
Provider provider = null;

// resolve inheritable properties from the context based on the provided 'id' for this IO instance
Map<String,String> inheritedProperties = PropertiesUtil.subProperties(this.properties().all(), id);
Map<String, String> inheritedProperties = PropertiesUtil.subProperties(this.properties().all(), id);

// create by explicitly configured IO <PLATFORM> from IO inheritable properties
if(inheritedProperties.containsKey("platform")){
if (inheritedProperties.containsKey("platform")) {
// resolve the platform and use it to create the IO instance
String platformId = inheritedProperties.get("platform");
Platform platform = this.platforms().get(platformId);
return platform.create(id, ioType);
}

// create by explicitly configured IO <PROVIDER> from IO config
if(inheritedProperties.containsKey("provider")){
if (inheritedProperties.containsKey("provider")) {
String providerId = inheritedProperties.get("provider");
// resolve the provider and use it to create the IO instance
provider = this.providers().get(providerId, ioType);

// validate IO type from resolved provider
if(!ioType.isType(provider.type())){
if (!ioType.isType(provider.type())) {
throw new IOException("This IO instance [" + id +
"] could not be created because the resolved provider [" + providerId +
"] does not match the required I/O TYPE [" + ioType.name() + "]");
Expand All @@ -407,28 +417,28 @@ default <T extends IO>T create(String id, IOType ioType) {
provider = provider(ioType);

// validate resolved provider
if(provider == null) {
if (provider == null) {
throw new ProviderNotFoundException(ioType);
}

// create IO instance
ConfigBuilder builder = provider.type().newConfigBuilder(this);
builder.id(id);
builder.load(inheritedProperties);
return (T)provider.create((Config) builder.build());
}

/**
* shutdown and unregister a created IO.
*
* @param <T> the IO Type
* @param id the IO id
* @return the IO which was shutdown or null if it wasn't created/registered
* @throws IONotFoundException if the IO was not registered
* @throws IOInvalidIDException if the ID is invalid
* @throws IOShutdownException if an error occured while shuting down the IO
*/
<T extends IO> T shutdown(String id) throws IOInvalidIDException, IONotFoundException, IOShutdownException;
return (T) provider.create((Config) builder.build());
}

/**
* shutdown and unregister a created IO.
*
* @param <T> the IO Type
* @param id the IO id
* @return the IO which was shutdown or null if it wasn't created/registered
* @throws IONotFoundException if the IO was not registered
* @throws IOInvalidIDException if the ID is invalid
* @throws IOShutdownException if an error occured while shuting down the IO
*/
<T extends IO> T shutdown(String id) throws IOInvalidIDException, IONotFoundException, IOShutdownException;

// ------------------------------------------------------------------------
// I/O INSTANCE ACCESSORS
Expand Down Expand Up @@ -464,10 +474,7 @@ default <T extends IO> T getIO(String id, Class<T> ioClass) throws IOInvalidIDEx
* @return a {@link com.pi4j.common.Descriptor} object.
*/
default Descriptor describe() {
Descriptor descriptor = Descriptor.create()
.category("CONTEXT")
.name("Runtime Context")
.type(this.getClass());
Descriptor descriptor = Descriptor.create().category("CONTEXT").name("Runtime Context").type(this.getClass());

descriptor.add(registry().describe());
descriptor.add(platforms().describe());
Expand Down
Loading

0 comments on commit 6eeb129

Please sign in to comment.