ALBIQ framework

Table of Contents

Why to use ALBIQ
Components of ALBIQ
MDD tool
Framework
System modules
Business modules
Deployment model
Best practices / Consultancy
Technical details
DSL sample
Generated parts
Service method implementation
Screen in Smartclient
Default screen
Summary editor
Formula editor
Screen customization possibilities
Rule language
Domain Specific Language definition
Rules using textual DSL
Rules using Excel sheet

Why to use ALBIQ

Components of ALBIQ

ALBIQ consist of following components:

MDD tool

This tool allow to generate from textual definition (DSL) particular parts of system like Entities, Repositories, Services and GUI. Responsibility of tool is to generate safe, well structured environment for developer. Than developer can write services, customize entities and repositories. Tool is using latest open source MDA tools like oAW, xText, xPand from Eclipse foundation. Code should be regenerated when it's necessary to change textual DSL. Properties of MDD tool:

Framework

Framework consist of well selected components and frameworks. For some parts of system you can choose from more options. Following picture is describing some possibilities:

Image 1: Butterfly mean preferred choice, Hours mean beta version.

Except this you have available other powerful engines. Rule engine is integrated to service tier. Rules should be invoked on every service call (even on service to service call). To ALBIQ also messaging is integrated. Services should be invoked by XML messages.

System modules

System services are necessary for correct ALBIQ processing. As basic services you can find:

All this services can used also by business logic or rule engine.

Business modules

Because we are using ALBIQ for some time we have build also some business services which can be reused. Services are build with re-usability in mind. Some of these modules are used now in more production systems. List of reusable business services:

Deployment model

Whole MDA is build around idea of maximum protection of your business code. Whole system around your business code is generated and you focus on writing pure business logic. That's also reason why MDA systems are so flexible in terms of deployment. You can generate surrounded code for specific environment without changing business code which remain same. Let's look on possible enterprise deployment:

Image 2: Deployment possibilities

As you can see from this basic deployment you can choose between many technologies. All of them should be changed without changing your business code and protect your investment to future when new technology appear. Your business code doesn't depend on communication technologies like EJBs remote call or messaging. You can change also persistent layer technology for example from SQL database to Object Oriented Database.

Best practices / Consultancy

We should offer you also some interesting consultancy services. We implemented and consulted many development projects and our specialists took part in many interesting projects. We should offer following business experience:

Technical details

DSL sample

This is sample of rates import service definition. It consist of Entity definition together with Repository and service definition.

Entity RatesImport {
String name
- @InputType inputType
String patternMatch length="50"
- @PatternMatchTo matchTo
String fundName length="4"
String currency length="4"
String isin length="4"
String fundClass length="4" nullable
String issueDate length="4"
String dateFormat length="25" nullable
String typeCapDis length="4" nullable
String nav length="4"
String totalValue length="4" nullable
String domicil length="4" nullable
String overallShares length="4" nullable
int minimumImportRows
int dataFromRow
Repository RatesImportRepository {
findById;
findAll;
save;
delete;
}
}

Service RatesImportService {
inject @SecuritiesService
inject @CurrencyService
inject @RatesService
inject @AuditLogService
inject @PropertyService

findAll => RatesImportRepository.findAll;
findById => RatesImportRepository.findById;
save => RatesImportRepository.save;
delete => RatesImportRepository.delete;
public void ratesImport();
public void exchangeRatesImport(int daysBack, boolean overwrite);
}

Generated parts

Artifact Files
src/generated/java Interfaces:
   domain/RatesImportRepository.java - repository interface
   serviceapi/RatesImportService.java - service interface
   repositoryimpl/RatesImportAccessFactory.java - access factory interface

Classes:
   domain/RatesImport.java - persistent entity class
   domain/RatesImportProperties.java - properties definition for findByCondition
   serviceimpl/RatesImportServiceImplBase.java implements serviceapi/RatesImportService.java

Exception:
   exception/RatesImportNotFoundException.java
src/main/java Classes:
   serviceimpl/RatesImportServiceImpl.java extends serviceimpl/RatesImportServiceImplBase.java
   accessimpl/RatesImportAccessFactoryImpl.java implements repositoryimpl/RatesImportAccessFactory.java
   repositoryimpl/RatesImportRepositoryImpl.java implements domain/RatesImportRepository.java
src/test/generated/java Classes:
   test/java/sk/f4s/pic/core/serviceapi/RatesImportServiceTest.java
   serviceapi/RatesImportServiceTestBase.java
src/test/generated/resources dbunit/RatesImportServiceTest.xml - XML for initial db load for test

Service method implementation

@GuiHints(detailBehavior=DetailBehavior.ON_ALL)
public void exchangeRatesImport(@Name("ctx") ServiceContext ctx, @Name("daysBack") int daysBack, @Name("overwrite") boolean overwrite) {
String url=getPropertyService().getPropertyValue(ctx, "month.report.url", MONTH_REPORT_XML);
String timeout=getPropertyService().getPropertyValue(ctx, "month.report.timeout", DEFAULT_TIMEOUT);
try {
Currency euroTicker = getCurrencyService().findByTicker(ctx, "EUR");
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
HashMap<String, Currency> realCurrencyCache=new HashMap<String, Currency>();
Calendar startDate=Calendar.getInstance();
startDate.clear(Calendar.HOUR); startDate.clear(Calendar.HOUR_OF_DAY); startDate.clear(Calendar.AM_PM);
startDate.clear(Calendar.MINUTE); startDate.clear(Calendar.SECOND); startDate.clear(Calendar.MILLISECOND);
startDate.add(Calendar.DAY_OF_YEAR, -daysBack);
Calendar now=Calendar.getInstance();
for (; !startDate.after(now); startDate.add(Calendar.MONTH, 1)) {
String result=MessageFormat.format(url, startDate.get(Calendar.MONTH) + 1, startDate.get(Calendar.YEAR));
log.info("Fetching exchange rates from: "+result);
URL service=new URL(result);
URLConnection conn = service.openConnection();
conn.setConnectTimeout(Integer.parseInt(timeout));
conn.setReadTimeout(Integer.parseInt(timeout));
Document doc = docBuilder.parse(conn.getInputStream());
NodeList cubes = doc.getElementsByTagName("Cube");
Date date=null;
for (int i=0; i < cubes.getLength(); i++) {
NamedNodeMap attrs = cubes.item(i).getAttributes();
Node dateN = attrs.getNamedItem("time");
Node tickerN = attrs.getNamedItem("currency");
Node rateN = attrs.getNamedItem("rate");
if (dateN != null) {
try {
date = DATE_FORMAT.parse(dateN.getTextContent());
if (date.before(startDate.getTime())) {
date=null;
}
} catch (ParseException pe) {
// Usually some statistics at end of report
date=null;
}
} else if (tickerN != null && rateN != null && date != null) {
try {
System.out.println("Currency: "+tickerN+", Rate: "+rateN+" Date: "+date);
Double rate=Double.parseDouble(rateN.getTextContent());
String ticker=tickerN.getTextContent();
Currency realTicker;
if (realCurrencyCache.containsKey(ticker)) {
realTicker = realCurrencyCache.get(ticker);
} else {
realTicker = getCurrencyService().findByTicker(ctx, ticker);
realCurrencyCache.put(ticker, realTicker);
}
importOneRate(ctx, euroTicker, realTicker, date, rate, overwrite);
} catch (NumberFormatException nfe) {
// Ignore error, just not imported
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

Screen in Smartclient

Default screen

Image 3: Screenshot od default generated application without any change after startup

Summary editor

allow to define column which aggregate more information together with special formatting

Image 4: Summary editor

Formula editor

allow to define new calculation like in spreadsheet (Excel, OpenOffice Calc, ...)

Image 5: Formula editor

Screen customization possibilities

Image 6: List customization possibilities

Image 7: Tabbed interface with Enterprise Blue skin and filter

Rule language

Domain Specific Language definition

[condition][]Ak sa vykonava pokyn pre=$req : RequestDescriptio...

[condition][]- menu "{currency}"=currency.ticker == "{currency}"

[condition][]- typ uctu je "{accType1}" alebo "{accType2}" alebo "{accType3}"=(account ...

[condition][]- typ uctu je "{accType1}" alebo "{accType2}"=(account.accountType.typeAccount matches ...

[condition][]- typ uctu je "{accType1}"=account.accountType.typeAccount matches "{accType1}.*"

[condition][]Ak sa vykonava pokyn=$req : RequestDescription(serviceName=="SecurityOrderTransactionService" ...

 

[condition][]Hotovostny vklad klienta=$req : RequestDescription(serviceName == "Accounting", methodName == "book") ...

[condition][]Hotovostny vyber klienta=$req : RequestDescription(serviceName == "Accounting", methodName == "book") ...

 

[condition][]Vklad cenneho papiera=$req : RequestDescription(serviceName == "Accounting", methodName == "book") ...

[condition][]Vyber cenneho papiera=$req : RequestDescription(serviceName == "Accounting", methodName == "book") ...

 

[consequence][]Odpocitaj poplatky=$order.setCalcAmount($fees.dischargeFees($order.getCalcAmount(), 2));

[consequence][]Log {message}=System.out.println({message});

[consequence][]LogError {message}=log.warn({message});

[consequence][]LogFatal {message}=log.error({message});

 

[consequence][]Audit {audit}=makeAuditRecord(serviceContext, servlet, {audit});

Rules using textual DSL

PRAVIDLO "Vklad cenneho papiera"

AK

Vklad cenneho papiera

POTOM

Log "Vklad cenneho papiera"

Priprav uctovnictvo

Najdi bezny ucet klienta $document.getAccount() v mene $document.getSecurity() a uloz do CREDIT_ACC

Najdi firemny ucet "KU" v mene $document.getSecurity() a uloz do DEBET_ACC

Zauctuj z uctu DEBET_ACC na ucet CREDIT_ACC v hodnote $document.getAmount() popis "SecDeposit"

KONIEC

 

PRAVIDLO "Validacia bezneho nakupneho pokynu - po spocitani poplatkov"

priorita -100

AK

Validacia bezneho nakupneho pokynu

Poplatky su priradene

POTOM

Vypis poplatky

Log "### Poplatky pre sumu "+$order.getInvestAmount().floatValue()+" su "+$fees.dischargeFees($order.getInvestAmount(), 2)

Odpocitaj poplatky

KONIEC

Rules using Excel sheet

You can define rules also with excel sheet. Sheet has to contain some additional definitions which are necessary for correct rules generation. However this definition should be hidden. In first screen you see pure rules with definition hidden and second screenshot with rules definition uncovered. On screenshot you can see price list implementation. It's important that this rules are processed directly by engine (no other hand touching or retyping).

Screen with definition hidden:

Image 8: XLS table with all definitions hidden (group level 1 collapsed)

Image 9: XLS where definition are shown