uses pal.Perception
uses Service
uses pal.ProxyInfo
data ConfigOption {
Composition c
char cflat[]
}
data ActiveIntercept {
char cmp[]
IDC com
char reqIntf[]
}
//wiring graph of the current live system
data Interface {
//name of the required interface
char name[]
//path of the component to which this required interface is currently connected, if any
char currentWiring[]
//proxy info associated with this wiring, if any (sometimes necessary to differentiate two wiring targets that might otherwise look the same, if they use the same proxy component but with different proxy params)
char wiringProxyInfo[]
//list of interceptors active on this required interface (currently we only support one)
ActiveIntercept intercept
}
data Component {
char path[]
IDC com
int completeWirings
bool serviceStarted
//the set of required interfaces of this component
Interface interfaces[]
bool used
bool proxy
char proxyTag[]
char proxyParams[]
char proxyInterface[]
Component next
}
data Interceptor {
char intf[]
char cmp[]
char proIntf[]
char reqIntf[]
}
component provides Assembly requires io.Output out, data.StringUtil stringUtil, data.IntUtil iu, composition.OptionBuilder, Loader loader, NativeLoader nLoader, composition.RecursiveLoader rLoader, composition.Adapt adaptAPI, composition.Intercept interceptAPI, System system, os.SystemInfo systemInfo, data.query.Search search, io.FileSystem fileSystem, data.adt.List {
bool verbose
//the list of loaded components and their current wirings
Component components
Component mainComponent
Composition compositions[]
ConfigOption configs[]
int currentConfig
Mutex configLock = new Mutex()
IDC perception
App myApp
AppParam subParams[]
bool appStarted
bool appFinished
bool exitCode
OptionBuilder builder
String searchPaths[]
//the list of registered interceptors
Interceptor interceptors[]
//disabled exceptions
String exceptionsDisabled[]
Assembly:Assembly(char main[], store AppParam params[], store IDC perceptionModule, opt store String interfaceExcludeList[], store String componentExcludeList[])
{
searchPaths = getStandardSearchPaths(main)
builder = new OptionBuilder(main, searchPaths, new String[](new String("pal.Perception"), interfaceExcludeList), componentExcludeList)
Composition options[] = builder.getCompositions()
subParams = params
perception = perceptionModule
ConfigOption newConfigs[] = new ConfigOption[options.arrayLength]
configs = newConfigs
for (int i = 0; i < newConfigs.arrayLength; i++)
{
newConfigs[i] = new ConfigOption(options[i], flattenComposition(options[i]))
}
compositions = options
}
char[] normalisePath(char path[])
{
path = clone path
for (int i = 0; i < path.arrayLength; i++)
{
if (path[i] == "\\") path[i] = "/"
}
return path
}
String[] getStandardSearchPaths(char main[])
{
//local
String result[] = new String[](new String("./"))
main = normalisePath(main)
if (stringUtil.rfind(main, "/") != StringUtil.NOT_FOUND)
{
main = stringUtil.subString(main, 0, stringUtil.rfind(main, "/"))
result = new String[](result, new String(main))
}
//System search paths
result = new String[](result, system.getSearchPaths())
//stdlib
result = new String[](result, new String(new char[](system.getDanaHome(), "/components/")))
//pre-process strings into a common format
for (int i = 0; i < result.arrayLength; i++)
{
result[i] = new String(normalisePath(result[i].string))
if (result[i].string.arrayLength > 0 && result[i].string[result[i].string.arrayLength-1] != "/")
result[i].string = new char[](result[i].string, "/")
}
//remove duplicates
String cd[] = result
result = null
for (int i = 0; i < cd.arrayLength; i++)
{
if (result.findFirst(String.[string], cd[i]) == null)
{
result = new String[](result, cd[i])
}
}
return result
}
int findFirstIndex(Data list[], TypeField field, Data template)
{
for (int i = 0; i < list.arrayLength; i++)
{
if (list[i]:.field == template:.field)
{
return i
}
}
return INT_MAX
}
char[] flattenComposition(Composition c)
{
//here we flatten the given composition into a specific wiring diagram string, by resolving required interfaces and their implied destinations in the cache
//first build our list of components, with syntax for proxies; note a component may appear more than once in c.options, where it implements more than one interface
String comps[]
for (int i = 0; i < c.options.arrayLength; i++)
{
char compStr[] = c.options[i].comp
if (c.options[i].proxyTag != null)
{
compStr = "{$compStr&$(c.options[i].proxyTag)&$(c.options[i].proxyParams)}"
}
if (comps.findFirst(String.[string], new String(compStr)) == null)
{
comps = new String[](comps, new String(compStr))
}
}
//build the list of wirings, but ignore those that don't have anything to resolve against in comps (either they're native or an auto-bind)
String wirings[]
for (int i = 0; i < c.options.arrayLength; i++)
{
for (int j = 0; j < c.options[i].req.arrayLength; j++)
{
if (!c.options[i].req[j].isNative)
{
int fromIndex = findFirstIndex(comps, String.[string], new String(c.options[i].comp))
int toIndex = findFirstIndex(c.options, InterfaceActual.[intf], new InterfaceActual(intf = c.options[i].req[j].intf))
if (toIndex != INT_MAX)
{
//convert this to an index in comps, which might be different where we have repeated components entries in c.options
char compStr[] = c.options[toIndex].comp
if (c.options[toIndex].proxyTag != null)
{
compStr = "{$compStr&$(c.options[toIndex].proxyTag)&$(c.options[toIndex].proxyParams)}"
}
toIndex = findFirstIndex(comps, String.[string], new String(compStr))
wirings = new String[](wirings, new String("$fromIndex:$toIndex:$(c.options[i].req[j].intf)"))
}
}
}
}
return new char[](comps.implode(","), "|", wirings.implode(","))
}
bool isAnyOf(char c, char tokens[])
{
for (int i = 0; i < tokens.arrayLength; i ++)
{
if (c == tokens[i])
return true
}
return false
}
//this version of "explode" does not parse inside braces, allowing proxy param strings to be unrestricted in their character set
String[] explodeXB(char str[], char tokens[])
{
int depth = 0
List lst = new List()
String res[]
String th
for (int i = 0; i < str.arrayLength; i++)
{
if (depth == 0 && isAnyOf(str[i], tokens))
{
if (th != null) lst.add(th)
th = null
}
else
{
if (th == null)
th = new String()
th.string = new char[](th.string, str[i])
if (str[i] == "{")
depth ++
else if (str[i] == "}")
depth --
}
}
if (th != null) lst.add(th)
if (lst.getLength() != 0)
{
int i = 0
res = new String[lst.getLength()]
for (String s = lst.getFirst(); s != null; s = lst.getNext())
{
res[i] = s
i ++
}
}
return res
}
/*
{ "@description" : "Gets a list of all valid assemblies of the software system being managed, where each assembly is identified with a unique string." }
*/
String[] Assembly:getConfigs()
{
String cfg[] = new String[configs.arrayLength]
for (int i = 0; i < cfg.arrayLength; i++)
{
cfg[i] = new String(configs[i].cflat)
}
return cfg
}
Interface getInterface(Component c, char name[])
{
for (int i = 0; i < c.interfaces.arrayLength; i++)
{
if (c.interfaces[i].name == name) return c.interfaces[i]
}
return null
}
Component componentLoaded(Component list, char comp[])
{
//check if it's a proxy
bool proxy = false
char proxyTag[] = null
char proxyParams[] = null
if (comp[0] == "{")
{
//we use lsplit here to minimise the character constraints on the proxy param string
String parts[] = dana.sub(comp, 1, comp.arrayLength-2).lsplit("&")
proxy = true
comp = parts[0].string
parts = parts[1].string.lsplit("&")
proxyTag = parts[0].string
proxyParams = parts[1].string
}
//find the component
Component cw = list
while (cw != null)
{
if (cw.path == comp && cw.proxy == proxy && cw.proxyTag == proxyTag && cw.proxyParams == proxyParams)
{
return cw
}
cw = cw.next
}
return null
}
bool isAutoBinding(char package[])
{
String interfaces[] = system.getAutoBindings()
for (int i = 0; i < interfaces.arrayLength; i++)
{
if (interfaces[i].string == package) return true
}
if (package == "pal.Perception") return true
return false
}
ReqIntf[] getRequiredInterfaces(Composition forc, char comp[])
{
InterfaceActual iact = forc.options.findFirst(InterfaceActual.[comp], new InterfaceActual(comp = comp))
return iact.req
}
bool nativeExceptionsDisabled(char path[])
{
PlatformInfo pi = system.getPlatform()
for (int j = 0; j < exceptionsDisabled.arrayLength; j++)
{
if (exceptionsDisabled[j].string.find("resources-ext") != StringUtil.NOT_FOUND)
{
for (int i = 0; i < searchPaths.arrayLength; i++)
{
char testPath[] = "$(searchPaths[i].string)resources-ext/$path"
if (testPath == exceptionsDisabled[j].string)
return true
testPath = "$(searchPaths[i].string)resources-ext/$path[$(pi.osCode).$(pi.chipCode)].dnl"
if (testPath == exceptionsDisabled[j].string)
return true
}
}
}
return false
}
Component loadComponent(Composition forc, char comp[], ProxyLoaderInfo ploaders[])
{
//check if it's a proxy
bool proxy = false
char proxyTag[] = null
char proxyParams[] = null
if (comp[0] == "{")
{
//we use lsplit here to minimise the character constraints on the proxy param string
String parts[] = dana.sub(comp, 1, comp.arrayLength-2).lsplit("&")
proxy = true
comp = parts[0].string
parts = parts[1].string.lsplit("&")
proxyTag = parts[0].string
proxyParams = parts[1].string
}
Component c = new Component(comp)
c.proxy = proxy
c.proxyTag = proxyTag
c.proxyParams = proxyParams
if (proxy)
{
//this is a proxy - loading is fully delegated to a ProxyLoader from ploaders
for (int i = 0; i < ploaders.arrayLength; i++)
{
if (ploaders[i].tag == proxyTag)
{
ProxyInstance pinstance = ploaders[i].loader.loadProxy(comp, proxyTag, proxyParams)
if (pinstance != null)
{
c.com = pinstance.com
c.proxyInterface = pinstance.providedInterface
break
}
else
{
throw new Exception("failed to load proxy component '$comp'")
}
}
}
}
else
{
//not a proxy - load it using our standard load approach
c.com = loader.load(comp)
if (exceptionsDisabled.findFirst(String.[string], new String(comp)) != null)
{
//out.println("disable exceptions on $(comp)")
c.com.enableExceptions(false)
}
if (c.com == null)
{
throw new Exception("failed to load component '$comp'")
}
//c.lastModified = fileSystem.getInfo(comp).modified
ReqIntf ris[] = getRequiredInterfaces(forc, comp)
for (int i = 0; i < ris.arrayLength; i++)
{
Interface iq = new Interface(ris[i].intf)
c.interfaces = new Interface[](c.interfaces, iq)
if (isAutoBinding(ris[i].intf))
{
c.completeWirings ++
}
if (ris[i].isNative)
{
IDC ncom = nLoader.load(ris[i].intf)
if (nativeExceptionsDisabled(ris[i].intf))
{
//out.println("disable exceptions on $(ris[i].intf)")
ncom.enableExceptions(false)
}
c.com.wire(ris[i].intf, ncom, ris[i].intf)
c.completeWirings ++
}
if (ris[i].intf == "pal.Perception")
{
c.com.wire("pal.Perception", perception, "pal.Perception")
}
}
if (c.completeWirings == c.interfaces.arrayLength)
{
//service start?
if (c.com.hasProvides("Service"))
{
Service svc = new Service() from c.com
svc.start()
}
}
}
return c
}
//this function wires up a component's dependency, but makes sure that dependency itself is fully wired first
int forwardWire(String comps[], String wirings[], int index)
{
int wiringsChanged = 0
//out.println("fwd wire $(wirings[index].string)")
String parts[] = wirings[index].string.lsplit(":")
int fromIndex = parts[0].string.intFromString()
parts = parts[1].string.lsplit(":")
int toIndex = parts[0].string.intFromString()
char intfName[] = parts[1].string
wirings[index] = null
// - wire everything *ahead* of this first
for (int i = 0; i < wirings.arrayLength; i++)
{
if (wirings[i] != null)
{
parts = wirings[i].string.lsplit(":")
int fromIndexB = parts[0].string.intFromString()
parts = parts[1].string.lsplit(":")
int toIndexB = parts[0].string.intFromString()
char intfNameB[] = parts[1].string
if (fromIndexB == toIndex)
wiringsChanged += forwardWire(comps, wirings, i)
}
}
// - and now do this wiring
Component fromComponent = componentLoaded(components, comps[fromIndex].string)
Component toComponent = componentLoaded(components, comps[toIndex].string)
Interface iq = getInterface(fromComponent, intfName)
char toIntfName[] = intfName
if (toComponent.proxyInterface != null) toIntfName = toComponent.proxyInterface
if (iq == null)
{
out.println("null IQ: $(comps[fromIndex].string) / $intfName")
}
if (iq.currentWiring == null)
{
if (verbose) out.println(" -- wiring $(fromComponent.path) :: $intfName >> $(toComponent.path)")
//it's new, so just connect it
fromComponent.com.wire(intfName, toComponent.com, toIntfName)
fromComponent.completeWirings ++
//check if we need to inject a proxy from an intercept rule
Interceptor nic = interceptors.findFirst(Interceptor.[intf], new Interceptor(intfName))
if (nic != null)
{
ActiveIntercept prxInstance = loadIntercept(nic, fromComponent.com, toComponent.com, intfName, fromComponent.path)
iq.intercept = prxInstance
}
wiringsChanged ++
if (fromComponent.completeWirings == fromComponent.interfaces.arrayLength)
{
//service start?
if (fromComponent.com.hasProvides("Service"))
{
//out.println(" -- starting service on '$(fromComponent.path)'")
Service svc = new Service() from fromComponent.com
svc.start()
}
}
}
else if (iq.currentWiring != toComponent.path || iq.wiringProxyInfo != "$(toComponent.proxyTag):$(toComponent.proxyParams)")
{
//it's already wired to something (and something different), so adapt it
if (verbose) out.println(" -- adapting $(fromComponent.path) :: $intfName >> $(toComponent.path)")
//check if there's a proxy on this interface, and adapt the proxy's RI if so
if (iq.intercept == null)
adaptAPI.adaptRequiredInterface(fromComponent.com, intfName, toComponent.com, toIntfName)
else
adaptAPI.adaptRequiredInterface(iq.intercept.com, iq.intercept.reqIntf, toComponent.com, intfName)
wiringsChanged ++
}
iq.currentWiring = toComponent.path
iq.wiringProxyInfo = "$(toComponent.proxyTag):$(toComponent.proxyParams)"
return wiringsChanged
}
void removeUnusedComponents(ProxyLoaderInfo loaders[])
{
Component prev = null
Component cw = components
while (cw != null)
{
if (!cw.used)
{
Component rem = cw
if (prev == null)
{
components = cw.next
}
else
{
prev.next = cw.next
}
if (rem.proxy)
{
for (int i = 0; i < loaders.arrayLength; i++)
{
if (loaders[i].tag == rem.proxyTag)
{
loaders[i].loader.unloadProxy(rem.com, rem.path, rem.proxyTag, rem.proxyParams)
break
}
}
}
}
else
{
prev = cw
}
cw = cw.next
}
}
void setComponentUse(Component lst, bool val)
{
Component cw = lst
while (cw != null)
{
cw.used = val
cw = cw.next
}
}
bool Assembly:setConfig(char config[], opt ProxyLoaderInfo loaders[])
{
//check if app has exited
if (appFinished) throw new Exception("Hosted application has completed execution")
bool adaptOK = true
int foundConfig = 0
mutex(configLock)
{
//validate configuration
bool found
for (int i = 0; i < configs.arrayLength; i++)
{
if (config == configs[i].cflat)
{
found = true
foundConfig = i
break
}
}
if (!found) throw new Exception("Configuration not known")
//adapt to the configuration
//if (verbose) out.println(" [setting config to '$config']")
// - walk through all wirings of the new configuration and apply them (using forwardWire to satisfy forward dependencies first)
String els[] = stringUtil.rsplit(config, "|")
String comps[] = explodeXB(els[0].string, ",")
String wirings[] = null
if (els.arrayLength == 2)
wirings = clone stringUtil.explode(els[1].string, ",")
//mark all loaded components as not-used
setComponentUse(components, false)
//load all components that are not yet loaded
bool loadFail = false
Component newComponents = null
Component end = null
for (int j = 0; j < comps.arrayLength; j++)
{
Component ldc = null
if ((ldc = componentLoaded(components, comps[j].string)) == null)
{
if (componentLoaded(newComponents, comps[j].string) == null)
{
Component c = loadComponent(configs[foundConfig].c, comps[j].string, loaders)
if (c != null)
{
c.used = true
if (end == null) end = c
c.next = newComponents
newComponents = c
}
else
{
loadFail = true
break
}
}
}
else
{
ldc.used = true
}
}
if (mainComponent == null)
{
mainComponent = end
}
//if we experienced a failure during loading, mark all original components as used, and all new components as not-used
if (loadFail)
{
setComponentUse(components, true)
setComponentUse(newComponents, false)
adaptOK = false
}
//add new components into the in-use list
// (note, this list can be empty if we're setting to the same config)
if (end != null)
{
end.next = components
components = newComponents
}
//perform the adaptation, if everything loaded OK
if (!loadFail)
{
currentConfig = foundConfig
//update wirings
for (int i = 0; i < wirings.arrayLength; i++)
{
if (wirings[i] != null)
{
forwardWire(comps, wirings, i)
}
}
}
//remove all components that aren't used
removeUnusedComponents(loaders)
}
return adaptOK
}
char[] Assembly:getConfig()
{
return configs[currentConfig].cflat
}
bool updConfig(char newConfig[], ConfigOption coption, char updComponent[], opt ProxyLoaderInfo loaders[])
{
mutex(configLock)
{
//adapt to the configuration
//if (verbose) out.println(" [setting config to '$config']")
// - walk through all wirings of the new configuration and apply them (using forwardWire to satisfy forward dependencies first)
String els[] = stringUtil.rsplit(newConfig, "|")
String comps[] = explodeXB(els[0].string, ",")
String wirings[] = null
if (els.arrayLength == 2)
wirings = clone stringUtil.explode(els[1].string, ",")
//mark all loaded components as not-used
Component cw = components
while (cw != null)
{
cw.used = false
cw = cw.next
}
//load all components that are not yet loaded
Component newComponents = null
Component end = null
IDC oldVersion = null
for (int j = 0; j < comps.arrayLength; j++)
{
Component ldc = null
if ((ldc = componentLoaded(components, comps[j].string)) == null)
{
if (componentLoaded(newComponents, comps[j].string) == null)
{
Component c = loadComponent(coption.c, comps[j].string, loaders)
c.used = true
if (end == null) end = c
c.next = newComponents
newComponents = c
}
}
else
{
if (ldc.path == updComponent)
{
//load the new version, clear its wirings, and save the old version's IDC for later
oldVersion = ldc.com
Component ndc = loadComponent(coption.c, comps[j].string, loaders)
ldc.com = ndc.com
ldc.interfaces = ndc.interfaces
ldc.completeWirings = ndc.completeWirings
ldc.serviceStarted = ndc.serviceStarted
}
ldc.used = true
}
}
if (mainComponent == null)
{
mainComponent = end
}
//add new components into the in-use list
// (note, this list can be empty if we're setting to the same config)
if (end != null)
{
end.next = components
components = newComponents
}
//update wirings
// - at this point we need to be using a slightly different "comps" list, where updComponent has a re-loaded version
// - so we save the old version in a tmp variable above, so the comps list here has the new thing (but that thing has an empty set of wirings)
// - after this, we need to work through wirings to find anything that points at the original updComponent's IDC, and adapt those wirings to updComponent's new IDC
String adaptWirings[] = clone wirings
for (int i = 0; i < wirings.arrayLength; i++)
{
if (wirings[i] != null)
{
forwardWire(comps, wirings, i)
}
}
//now adapt all wirings that went into this component, since they'll still be pointing at the old one
for (int i = 0; i < adaptWirings.arrayLength; i++)
{
String parts[] = adaptWirings[i].string.lsplit(":")
int fromIndex = parts[0].string.intFromString()
parts = parts[1].string.lsplit(":")
int toIndex = parts[0].string.intFromString()
char intfName[] = parts[1].string
Component fromComponent = componentLoaded(components, comps[fromIndex].string)
Component toComponent = componentLoaded(components, comps[toIndex].string)
if (toComponent.path == updComponent)
{
//adapt this to our new version (could do an extra check to see if it's already pointing at our new version, but it doesn't really matter)
adaptAPI.adaptRequiredInterface(fromComponent.com, intfName, toComponent.com)
}
}
//remove all components that aren't used
removeUnusedComponents(loaders)
}
return true
}
void launchMain(IDC class, char launchPath[])
{
myApp = new App() from class
myApp.setSourcePath(launchPath)
exitCode = myApp.main(subParams)
appFinished = true
appStarted = false
}
/*
{ "@description" : "Launches the main method of the program, blocking until that function completes." }
*/
bool Assembly:runApp(char launchPath[])
{
mutex(configLock)
{
if (mainComponent == null)
{
throw new Exception("no main component loaded - use setConfig first")
}
if (!appStarted)
{
appStarted = true
}
else
{
throw new Exception("app is already running")
}
}
launchMain(mainComponent.com, launchPath)
return true
}
String[] addCompositions(Composition newOpt[])
{
ConfigOption newConfigs[] = new ConfigOption[newOpt.arrayLength]
String result[] = new String[newOpt.arrayLength]
for (int i = 0; i < newConfigs.arrayLength; i++)
{
newConfigs[i] = new ConfigOption(newOpt[i], flattenComposition(newOpt[i]))
result[i] = new String(newConfigs[i].cflat)
}
configs = new ConfigOption[](configs, newConfigs)
compositions = new Composition[](compositions, newOpt)
return result
}
String[] remCompositions(Composition toRemove[])
{
Composition newCompositions[] = new Composition[compositions.arrayLength - toRemove.arrayLength]
ConfigOption newConfigs[] = new ConfigOption[configs.arrayLength - toRemove.arrayLength]
String result[] = new String[toRemove.arrayLength]
int j = 0
int k = 0
for (int i = 0; i < configs.arrayLength; i++)
{
if (!isRefIn(configs[i].c, toRemove))
{
newConfigs[j] = configs[i]
newCompositions[j] = configs[i].c
j ++
}
else
{
result[k] = new String(flattenComposition(toRemove[k]))
k ++
}
}
configs = newConfigs
compositions = newCompositions
return result
}
String[] Assembly:addComponent(char path[])
{
Composition newOpt[] = builder.addComponent(path)
return addCompositions(newOpt)
}
ConfigOption getBestConfigMatch(char matchWith[], opt char withComponent[])
{
//find the best match for "matchWith" in "fromList"
// - if withComponent has been provided, the indicated component must appear within the matched config
int comDelta = 0
//int wiringDelta = 0
int bestMatchIndex = 0
String compsA[] = explodeXB(matchWith.rsplit("|")[0].string, ",")
ConfigOption fromList[] = configs
for (int i = 0; i < fromList.arrayLength; i++)
{
//count how many components don't match (how many wirings don't match??)
String compsB[] = explodeXB(fromList[i].cflat.rsplit("|")[0].string, ",")
int thisComDelta = 0
for (int j = 0; j < compsA.arrayLength; j++)
{
if (compsB.findFirst(String.[string], compsA[j]) == null)
{
thisComDelta ++
}
}
for (int j = 0; j < compsB.arrayLength; j++)
{
if (compsA.findFirst(String.[string], compsB[j]) == null)
{
thisComDelta ++
}
}
if (thisComDelta < comDelta)
{
comDelta = thisComDelta
bestMatchIndex = i
}
}
return fromList[bestMatchIndex]
}
UpdateInfo Assembly:updComponent(char path[], opt ProxyLoaderInfo loaders[])
{
//call updComponent in optionBuilder; this will tell us if there are any changes to the composition list
// - if there are no changes, check if our current composition uses this component, and "adapt" to it if so
// - otherwise, if changes, check if our current composition uses this component, and locate the nearest-matching new composition to adapt to
bool inUse = false
Composition c = configs[currentConfig].c
for (int i = 0; i < c.options.arrayLength; i++)
{
if (c.options[i].comp == path)
{
inUse = true
break
}
}
CompositionUpdate cupdate = builder.updComponent(compositions, path)
if (inUse)
{
//here we need to adapt our current in-use composition to use the new version of the component
// - we may ALSO need to adapt the entire composition to a closest-match if there's a structural change (we detect a structure change by scanning through cupdate.remOptions to check if you current composition is listed there)
//we use a custom function here, updConfig(), to handle the process of changing to the new config while also hot-swapping the existing component
// - it'll still do the "load components" step, but then with forward-wire anything not in the current config, and will load a copy of the component to be updated and forward-wire that
// - then we'll do the hot-swap for anything that wires into the component to be updated
// - then we switch that component IDC into its cache record, and update our current config...
char configNow[] = configs[currentConfig].cflat
String remList[] = remCompositions(cupdate.remOptions)
String addList[] = addCompositions(cupdate.addOptions)
char x[] = null
ConfigOption xConfig = null
if (cupdate.remOptions != null)
{
//our current config may no longer be available, so find the next-best match within addOptions
ConfigOption bestMatch = getBestConfigMatch(configNow, path)
x = bestMatch.cflat
xConfig = bestMatch
//update currentConfig by finding the index of cflat in our new list
for (int i = 0; i < configs.arrayLength; i++)
{
if (x == configs[i].cflat)
{
currentConfig = i
break
}
}
}
else
{
//our current config must still be available
x = configs[currentConfig].cflat
xConfig = configs[currentConfig]
}
updConfig(x, xConfig, path, loaders)
//return the two list deltas, plus report the composition we're now in
return new UpdateInfo(x, remList, addList)
}
else
{
//just update our composition list
String remList[] = remCompositions(cupdate.remOptions)
String addList[] = addCompositions(cupdate.addOptions)
//return the two list deltas, plus report that we're still in the same composition
return new UpdateInfo(configs[currentConfig].cflat, remList, addList)
}
return null
}
bool isRefIn(Data ref, Data array[])
{
for (int i = 0; i < array.arrayLength; i++)
{
if (array[i] === ref) return true
}
return false
}
String[] Assembly:remComponent(char path[])
{
//check if the currently-selected composition is using the component (and fail if so)
// - otherwise just remove it, and report the removed compositions
Composition c = configs[currentConfig].c
for (int i = 0; i < c.options.arrayLength; i++)
{
if (c.options[i].comp == path)
throw new Exception("attempt to remove a component that is currently in-use")
}
Composition toRemove[] = builder.remComponent(compositions, path)
return remCompositions(toRemove)
}
String[] Assembly:addProxy(char forInterface[], char proxyComponent[], char tag[], char parameters[])
{
Composition newOpt[] = builder.addProxy(forInterface, proxyComponent, tag, parameters)
return addCompositions(newOpt)
}
String[] Assembly:remProxy(char forInterface[], char proxyComponent[], char tag[], char parameters[])
{
//check if the currently-selected composition is using the proxy (and fail if so)
// - otherwise just remove it, and report the removed compositions
Composition c = configs[currentConfig].c
for (int i = 0; i < c.options.arrayLength; i++)
{
if (c.options[i].intf == forInterface && c.options[i].comp == proxyComponent && c.options[i].proxyTag == tag && c.options[i].proxyParams == parameters)
throw new Exception("attempt to remove a component that is currently in-use")
}
Composition toRemove[] = builder.remProxy(compositions, forInterface, proxyComponent, tag, parameters)
return remCompositions(toRemove)
}
ActiveIntercept loadIntercept(Interceptor nic, IDC wireFrom, IDC wireTo, char wireToIntf[], char site[])
{
IDC prx = rLoader.load(nic.cmp, new String[](new String("lang.Morph"), new String("pal.Perception"))).mainComponent
//IDC prx = loader.load(nic.cmp)
prx.wire(nic.reqIntf, wireTo, wireToIntf)
interceptAPI.insertIntercept(wireFrom, wireToIntf, prx, nic.proIntf, nic.reqIntf)
//check if the proxy component is expecting additional info, via the ProxyInfo interface
if (prx.hasProvides("pal.ProxyInfo"))
{
ProxyInfo pinfo = new ProxyInfo() from prx
pinfo.setInjectSite(site, wireToIntf)
}
//check if the proxy component is expected to be connected to Perception
if (prx.hasRequires("pal.Perception"))
{
prx.wire("pal.Perception", perception, "pal.Perception")
}
return new ActiveIntercept(nic.cmp, prx, nic.reqIntf)
}
bool Assembly:addIntercept(char intf[], char cmp[])
{
//load copies of the given interceptor cmp at each location required interface "intf" appears
// - if interceptors already exist at that location, this one is added to the end of the chain
mutex(configLock)
{
//this implementation currently supports only a single interceptor per interface
if (interceptors.findFirst(Interceptor.[intf], new Interceptor(intf, cmp)) != null)
throw new Exception("an interceptor on $intf is already registered")
if (!fileSystem.exists(cmp))
{
throw new Exception("intercept component not found at '$cmp'")
}
Interceptor nic = new Interceptor(intf, cmp)
//detect which interfaces the proxy has (generic lang.Proxy/Morph, or specific)
IDC prxCheck = loader.load(cmp)
if (prxCheck.hasProvides("lang.Proxy"))
nic.proIntf = "lang.Proxy"
else if (prxCheck.hasProvides(intf))
nic.proIntf = intf
else
throw new Exception("intercept component does not provide a proxy-able interface for '$intf'")
if (prxCheck.hasRequires("lang.Morph"))
nic.reqIntf = "lang.Morph"
else if (prxCheck.hasRequires(intf))
nic.reqIntf = intf
else
throw new Exception("intercept component does not require a proxy-able interface for '$intf'")
interceptors = new Interceptor[](interceptors, nic)
//add this intercept to any interfaces that match
for (Component cw = components; cw != null; cw = cw.next)
{
for (int i = 0; i < cw.interfaces.arrayLength; i++)
{
if (cw.interfaces[i].name == intf)
{
//load a copy of the interceptor cmp, and inject it
Component wireTo = componentLoaded(components, cw.interfaces[i].currentWiring)
ActiveIntercept prxInst = loadIntercept(nic, cw.com, wireTo.com, cw.interfaces[i].name, cw.path)
cw.interfaces[i].intercept = prxInst
}
}
}
}
return true
}
bool Assembly:remIntercept(char intf[], char cmp[])
{
//remove the given interceptor cmp at each location that the required interface "intf" appears
// - if multiple interceptors exist, the one closest to the end of the chain is removed
mutex(configLock)
{
//remove the intercept rule
Interceptor nic = interceptors.findFirst(Interceptor.[intf], new Interceptor(intf, cmp))
if (nic == null)
throw new Exception("interceptor $intf/$cmp is not registered")
Interceptor nArray[] = new Interceptor[interceptors.arrayLength-1]
int j = 0
for (int i = 0; i < interceptors.arrayLength; i++)
{
if (interceptors[i] !== nic)
{
nArray[j] = interceptors[i]
j ++
}
}
interceptors = nArray
//remove each specific intercept
for (Component cw = components; cw != null; cw = cw.next)
{
for (int i = 0; i < cw.interfaces.arrayLength; i++)
{
if (cw.interfaces[i].name == intf && cw.interfaces[i].intercept != null)
{
Component wireTo = componentLoaded(components, cw.interfaces[i].currentWiring)
interceptAPI.removeIntercept(cw.com, cw.interfaces[i].name, wireTo.com, intf, nic.reqIntf)
cw.interfaces[i].intercept = null
}
}
}
}
return false
}
InterceptInfo[] Assembly:getIntercepts()
{
InterceptInfo result[] = new InterceptInfo[interceptors.arrayLength]
for (int i = 0; i < result.arrayLength; i++)
{
result[i] = new InterceptInfo(interceptors[i].intf, interceptors[i].cmp)
}
return result
}
void Assembly:disableExceptions(store String paths[])
{
exceptionsDisabled = paths
}
}