A few year ago I created spring4oe primarily because I felt the need for a DI container. This project is based on the Spring 2.5 definitions. Although the sources suggest there's some AOP stuff as well this is not functional.
disclaimer: this is Work In Progress. Please notice the MIT license attached.
The Spring DI container is build around beans. There's nothing particular about a bean, other than it should a ABL class. To make sure your XML is valid you may want to enter the appropriate namespaces.
<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- the beans go here -->
</beans>
Spring is ibuild around the concept of mapping a logical to an actual class. The simplest form of defining a bean looks like:
<bean id="applogger" class="bfv.spring4oe.sample.AppLogger" />
using bfv.spring4oe.beans.factory.XmlBeanFactory from propath.
using bfv.spring4oe.beans.factory.IBeanFactory from propath.
using bfv.spring4oe.sample.ILogging from propath.
define variable factory as IBeanFactory no-undo.
define variable logger as ILogging no-undo.
factory = new XmlBeanFactory("./bfv/spring4oe/sample/beans.xml").
logger = cast(factory:GetBean("applogger"), "ILogging").
If you want to use DefaultFactory property you need to set default factory first:
using bfv.spring4oe.beans.factory.XmlBeanFactory from propath.
using bfv.spring4oe.beans.factory.IBeanFactory from propath.
using bfv.spring4oe.sample.ILogging from propath.
define variable factory as IBeanFactory no-undo.
define variable logger as ILogging no-undo.
factory = new XmlBeanFactory("./bfv/spring4oe/sample/beans.xml").
factory:SetAsDefault().
logger = cast(factory:GetBean("applogger"), "ILogging").
Now you can use DefaultFactory:
using bfv.spring4oe.beans.factory.XmlBeanFactory from propath.
using bfv.spring4oe.sample.ILogging from propath.
define variable logger as ILogging no-undo.
logger = cast(XmlBeanFactory:DefaultFactory:GetBean("applogger"), "ILogging").
It is also possible to set a (public) property of a class. Our AppLogger class for example has a LogFilename property which allows us to set the filename to which is logged. Setting this property look like this:
<bean id="applogger" class="bfv.spring4oe.sample.AppLogger">
<property name="LogFilename" value="./../../log/app.log"/>
</bean>
Not only is it possible to set a value, it is also possible to set a reference, which is a reference to a class. For example if you want to specify that your session manager needs to use the applogger defined ealier this is the way to go:
<bean id="sessman" class="bfv.spring4oe.sample.SessionManager">
<property name="Logger" ref="applogger" />
</bean>
The property Logger is set to what applogger point to, which is the bfv.spring4oe.sample.AppLogger class.
Just setting a property is most of the time not enough to accomplish the desired behavior. Setting the property and call a method which can act upon this property does the trick. In Spring this method can be specfied via the init-method
<bean id="applogger" class="bfv.spring4oe.sample.AppLogger" init-method="InitLogger">
<property name="LogFilename" value="./../../log/app.log"/>
</bean>
First the object is constructed, then the properties are set and finally the init-method is called. The 4GL part could look like:
class bfv.spring4oe.sample.AppLogger implements ILogging: define public property LogFilename as character no-undo get. set. define private stream logStream. method public void Log(messageText as character): put stream logStream unformatted iso-date(now) " " messageText skip. end method. method public void InitLogger(): output stream logStream to value(logFilename) unbuffered. end method. end class.
By default a bean is a singleton. The container returns the same reference every time. When it is necessary to return a new instance on every call to GetBean you can set the scope attribute of the bean to prototype.
<bean id="pablo" class="Progress.Lang.Object" scope="prototype" />
PS. why pablo? It stands for Plain ABL Object, which is an simpel object which doesn't need some complicated instantiation. It's the ABL version of a POJO.
Sometimes it necessary to control the lifespan of an object by logic in the class itself. Spring4oe gives this possibility via a so-called factory method. This factory method is a static method and can be set via the factory-method on the bean.
<bean id="factmeth" class="bfv.spring4oe.sample.FactoryMethodSample"
factory-method="GiveMeAnInstance" />
with the accompanying 4GL
class bfv.spring4oe.sample.FactoryMethodSample: method public static FactoryMethodSample GiveMeAnInstance(): return new FactoryMethodSample(). end method. end class.
Whenever it is handy to have two logical names for what is in essence the same, one can use an alias:
<bean id="pablo" class="Progress.Lang.Object" scope="prototype" />
<alias alias="juan" name="pablo" />