Files
fflib-apex-extensions/sfdx-source/apex-extensions/main/application/dynamic/classes/fflib_AppBindingResolver.cls
T
wim a0fc1bd10d Improve performance of Dynamic Application Factories
Also added a method overload to provide a Package Name.
Useful for DX based second generation packages. Now each package can have their own bindings.
2023-09-01 15:39:25 +01:00

323 lines
9.2 KiB
OpenEdge ABL

/**
* File Name: fflib_AppBindingResolver
* @description Resolver for returning the requested binding(s)
*
* @author architect ir. Wilhelmus G.J. Velzeboer
*
* Copyright (c), W.G.J. Velzeboer,
* 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 author 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 the author 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.
*/
public virtual class fflib_AppBindingResolver
implements fflib_IAppBindingResolver
{
// allow for dependency injection inside the resolver
protected InternalBindings internalBindings
{
get
{
if (null == internalBindings)
{
internalBindings = new InternalBindings();
}
return internalBindings;
}
private set;
}
protected fflib_IAppBinding bindingToResolve
{
get
{
if (bindingToResolve == null)
{
bindingToResolve = internalBindings.newAppBinding();
}
return bindingToResolve;
}
private set;
}
protected fflib_IAppBindings bindings;
private List<fflib_IAppBindingModule> modules;
public static fflib_IAppBindingResolver newInstance()
{
return new fflib_AppBindingResolver();
}
/**
* Constructors
*/
public fflib_AppBindingResolver()
{
}
/**
* Providing routes to the AppBindingResolver constructor provides dependency injection inside the resolver
*
* @param routes The routes
*/
public fflib_AppBindingResolver(fflib_IAppBindingRouter routes)
{
this(routes.getRoutes());
}
public fflib_AppBindingResolver(Map<System.Type, System.Type> internalBindings)
{
this.internalBindings = new InternalBindings(internalBindings);
}
public fflib_IAppBindingResolver addModule(fflib_IAppBindingModule bindingModule)
{
if (null == this.modules) this.modules = new List<fflib_IAppBindingModule>();
this.modules.add(bindingModule);
return this;
}
public fflib_IAppBindingResolver byObject(Object bindingObject)
{
this.bindingToResolve.setBindingObject(bindingObject);
return this;
}
public fflib_IAppBindingResolver bySequence(Integer sequence)
{
this.bindingToResolve.setSequence(sequence);
return this;
}
public fflib_IAppBindingResolver byType(fflib_AppBindingType bindingType)
{
this.bindingToResolve.setBindingType(bindingType);
return this;
}
public fflib_IAppBindingResolver byInterfaceType(Type interfaceType)
{
this.bindingToResolve.setInterfaceType(interfaceType);
return this;
}
public fflib_IAppBindingResolver bySharingMode(fflib_SharingMode sharingMode)
{
this.bindingToResolve.setSharingMode(sharingMode);
return this;
}
public fflib_IAppBindingResolver byNamespacePrefix(String namespacePrefix)
{
this.bindingToResolve.setNamespacePrefix(namespacePrefix);
return this;
}
public fflib_IAppBindingResolver byPackageName(String packageName)
{
this.bindingToResolve.setPackageName(packageName);
return this;
}
/**
* Handles lazy loading of modules, if no custom module is configured (via addModule) the default module is returned
*
* @return Returns the configured modules
*/
public List<fflib_IAppBindingModule> getModules()
{
if (null == this.modules)
{
// Lazy load the default Binding module is none other is set.
modules = new List<fflib_IAppBindingModule>
{
internalBindings.newAppBindingModule()
};
}
return modules;
}
public List<fflib_IAppBinding> resolve()
{
if (!hasBindingsLoaded()) loadBindings();
fflib_IAppBindings resolvedBindings =
this.bindings
.selectByNamespacePrefix(bindingToResolve.getNamespacePrefix())
.selectByType(bindingToResolve.getBindingType())
.selectByInterfaceType(bindingToResolve.getInterfaceType())
.selectByObject(bindingToResolve.getBindingObject())
.selectBySharingMode(bindingToResolve.getSharingMode())
.selectBySequence(bindingToResolve.getSequence());
this.bindingToResolve = internalBindings.newAppBinding();
return resolvedBindings.getBindings();
}
protected Boolean hasBindingsLoaded()
{
return (this.bindings != null);
}
/**
* Prepares a domain to store the bindings,
* retrieves the bindings from the modules
* and stores the bindings in this.bindings
*/
protected virtual void loadBindings()
{
this.bindings = internalBindings.newAppBindings();
List<fflib_IAppBindingModule> modules = getModules();
List<fflib_IAppBinding> appBindings = loadModules(modules);
this.bindings.addBindings(
appBindings
);
}
/**
* Initialises all the modules and retrieves their binding.
* Any child modules in all nested will also be initialised and processed
*
* @param modules The modules to load their binding
*
* @return Returns the bindings configured in the given modules
*/
protected virtual List<fflib_IAppBinding> loadModules(List<fflib_IAppBindingModule> modules)
{
List<fflib_IAppBinding> result = new List<fflib_IAppBinding>();
for (fflib_IAppBindingModule module : getModules())
{
module.init();
List<fflib_IAppBinding> bindings = module.getBindings();
List<fflib_IAppBindingModule> subModules = getModulesFromBindings(bindings);
if (subModules.isEmpty() == false)
{
result.addAll(
loadModules(subModules)
);
}
result.addAll(bindings);
}
return result;
}
/**
* Retrieves the bindings of the type module from the given list and returns their instance
*
* @param bindings The bindings to filter
*
* @return Returns the instances of Module bindings
*/
private List<fflib_IAppBindingModule> getModulesFromBindings(List<fflib_IAppBinding> bindings)
{
List<fflib_IAppBindingModule> result = new List<fflib_IAppBindingModule>();
for (fflib_IAppBinding binding : bindings)
{
if (binding.getBindingType() != fflib_AppBindingType.Module) continue;
Object instance = binding.newImplInstance();
if (!(instance instanceof fflib_IAppBindingModule))
throw new DeveloperException(
'Binding module should be an instance of fflib_IAppBindingModule, instead found: '
+ binding.getImplementationType().getName()
);
result.add(
(fflib_IAppBindingModule) instance
);
}
return result;
}
public class InternalBindings
{
private Map<System.Type, System.Type> bindings;
public InternalBindings()
{
}
public InternalBindings(Map<System.Type, System.Type> internalBindings)
{
this.bindings = internalBindings;
}
public fflib_IAppBinding newAppBinding()
{
System.Type interfaceType = fflib_IAppBinding.class;
if (hasInternalBinding(interfaceType))
{
Object instance = getInternalBinding(interfaceType);
if (!(instance instanceof fflib_IAppBinding))
throw new DeveloperException('Invalid internal binding for fflib_IAppBinding');
return (fflib_IAppBinding) instance;
}
return new fflib_AppBinding();
}
public fflib_IAppBindings newAppBindings()
{
System.Type interfaceType = fflib_IAppBindings.class;
if (hasInternalBinding(interfaceType))
{
Object instance = getInternalBinding(interfaceType);
if (!(instance instanceof fflib_IAppBinding))
throw new DeveloperException('Invalid internal binding for fflib_IAppBindings');
return (fflib_IAppBindings) instance;
}
return new fflib_AppBindings();
}
private Boolean hasInternalBinding(System.Type interfaceType)
{
return this.bindings != null && this.bindings.containsKey(interfaceType);
}
private Object getInternalBinding(System.Type interfaceType)
{
return this.bindings.get(interfaceType).newInstance();
}
private fflib_IAppBindingModule newAppBindingModule()
{
System.Type interfaceType = fflib_IAppBindingModule.class;
if (hasInternalBinding(interfaceType))
{
Object instance = getInternalBinding(interfaceType);
if (!(instance instanceof fflib_IAppBindingModule))
throw new DeveloperException('Invalid internal binding for fflib_IAppBindingModule');
return (fflib_IAppBindingModule) instance;
}
return new fflib_AppBindingMetaDataModule();
}
}
public class DeveloperException extends Exception {}
}