Zobrazují se příspěvky se štítkemJava. Zobrazit všechny příspěvky
Zobrazují se příspěvky se štítkemJava. Zobrazit všechny příspěvky

neděle 31. ledna 2016

Spring and OSGi

OSGi

We are using OSGi framework for ensuring dynamic modularity in our solution. OSGi is great tool for this purposes. Adding and replacing bundles (let's say plugins or jars) into running system works well. Also dependecy resolving for keep all stuff in consistent state makes us happy.

Simple OSGi runtime schema

OSGi is focused on "low-level" modularity (jars, classloaders, class dependecies and their versions, import/export classes, services...). For "high-level" modularity or, better to say "business-level" features like the Inversion of Control, you need add support from outside.

Spring 

Spring is great IoC container (and even more) for creating apps. It is well known, popular and extensible. Huge community ensures support, project is very active and still inovative. Therefore would like to add Spring as IoC to our OSGi based system.

Simple Spring IoC container runtime schema

IoC and OSGi

Spring developers tried to break through OSGi world with "Spring Dynamic Modules". Unfortunately this project is almost dead. As a replacement is recommended to use an OSGi extension called "Blueprint", which is specification of IoC for OSGi framework. There are two usable implementations:
  • Eclipse Gemini Blueprint
  • Apache Aries Bluperint
... but, they are not Spring:(

Only one think which was done (by Apache ServiceMix community) is Spring framework wrapped as bundles. It was a starting point for our integration.

Our simple solution for Spring-OSGi

Our solution works very simple way which is sufficient for almost usecases:
  • Every OSGi-bundle has it's own isolated application context
  • Application context is defined by java configuration only.
  • Public and listen to service with cardinality 1..1.
  • No service filtering.
Our solution Spring-OSGi

Annotations

We need to define annotations which define spring beans as public services and also external beans which are imported via services into context (from outside).

Annotation for public spring beans into OSGi world as a OSGi service:

  @Retention(RetentionPolicy.RUNTIME)
  public @interface ExposeAsService  {}

Annotation for obtain OSGi service into spring world:

  @Retention(RetentionPolicy.RUNTIME)
  public @interface ObtainAsService {}
 
Example of application context configuration via java class:

@Configuration 
public abstract class DatabaseServicesContextConfiguration {
  @Bean
  @ExposeAsService
  protected ISystemService systemService() {
    return new SystemService();
  }
  
  @Bean
  @ObtainAsService
  public abstract IAppPropertiesService appProperties();
}

New post-processor and application-context

We need "somehow" handle new declared annotations. Therefore bean-factory-post-processor and application context will be created.

OsgiServiceObtainerBeanFactoryPostProcessor
This bean-factory-post-processor uses simple "trick": find all bean-definitions in context annotated by @ObtainAsService. Those definitions are removed and replaced by singleton instance of class which was lookuped from OSGi as service.

public class OsgiServiceObtainerBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
     private final BundleContext bundleContext;


     public
OsgiServiceObtainerBeanFactoryPostProcessor(BundleContext bundleContext) {
         this.bundleContext = bundleContext;
      }
         /** 

          * Find beanDefinitios with annotation ObtainAsService, 
          * lookup this service and register this service as

     * singletons into spring context. 
     */ 
      @Override    
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        
        DefaultListableBeanFactory beanFactoryEx = ((DefaultListableBeanFactory)beanFactory);

        // all beanDefinitions 
        String[] beanDefinitionNames = beanFactoryEx.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
           BeanDefinition beanDefinition = beanFactoryEx.getBeanDefinition(beanDefinitionName);
           if (beanDefinition.getSource() instanceof StandardMethodMetadata) {
              StandardMethodMetadata metadata = (StandardMethodMetadata)(beanDefinition.getSource()); 
              if (metadata.isAnnotated(ObtainAsService.class.getCanonicalName())) {            
               
               // 1. remove abstract definition 
               beanFactoryEx.removeBeanDefinition(beanDefinitionName); 
               
               // 2. get service from osgi
               String serviceClassName = metadata.getIntrospectedMethod().getReturnType().getCanonicalName();
               serviceInstance = getServiceTracker(serviceClassName).waitForService(0);
               ...   
               
               // 3. register service as singleton
               ...                
               beanFactoryEx.registerSingleton(metadata.getMethodName(), serviceInstance);
               ...
             }
         }
      }
   }
   ...
}

OsgiApplicationContext
This context is used in bundle activator and adds support for OSGi via "BundleContext" instance which is obrained in constructor. In constructor is also added "osgi bean-postprocessor" for service obtaining. Method "exposeService" ensures publicing annotated services into OSGi.

public class OsgiApplicationContext extends AnnotationConfigApplicationContext {

  private BundleContext bundleContext;

  // all exposed services.
  private List<ServiceRegistration> serviceRegistrations = new ArrayList<ServiceRegistration>();

  public OsgiApplicationContext(BundleContext bundleContext) {
    if (bundleContext!=null) {
      this.addBeanFactoryPostProcessor(new OsgiServiceObtainerBeanFactoryPostProcessor(bundleContext));
      this.bundleContext = bundleContext;
    } 
  }
  
  /**
   * Expose beans annotated as "ExposeAsService" to OSGi as services 
   */
  public void exposeServices() {
   // get beanDefinition of current bean
   for (String name : this.getBeanDefinitionNames()) {
      BeanDefinition beanDefinition = this.getBeanDefinition(name);
      if (beanDefinition.getSource() instanceof StandardMethodMetadata) {
         StandardMethodMetadata metadata = (StandardMethodMetadata)(beanDefinition.getSource()); 
         if (metadata.isAnnotated(ExposeAsService.class.getCanonicalName())) {
            String className = metadata.getIntrospectedMethod().getReturnType().getCanonicalName();
            // expose as service
            Object bean = this.getBean(name);
            LOGGER.info("Exposing bean "+name+" "+bean.toString() + " as a service "+className);
            ServiceRegistration serviceRegistration = this.bundleContext.registerService(className, bean, null);
            this.serviceRegistrations.add(serviceRegistration);
         }
      }
    }
  }
  @Override
  public void close() {
   for (ServiceRegistration registration : this.serviceRegistrations) {
      registration.unregister();
   }
   super.close();
  } 
} 


Bundle activator example

Usage of these classes is quite simple:
public class DatabaseServiceActivator implements BundleActivator {
  @Override
  public void start(final BundleContext bundleContext) throws Exception {
    springContext = new OsgiApplicationContext(bundleContext);
    springContext.setClassLoader(this.getClass().getClassLoader());
    springContext.register(DatabaseServicesContextConfiguration.class); 
    springContext.refresh();  
    springContext.exposeServices();
  } 
}

When bundle is activated, spring context will be created and obtains some beans as services from outside. After context activation (refresh call), some beans will be exposed as services. Important is to set classloader, because spring runs under it's own classloader and he cannot see your bean classes.

Future

Great news are comming from Apache Aries community! Blueprint will be implemented as "bridge" for Spring IoC. First release 0.1.0 is on download page at Apache Aries web.

When it will be more stable, we can replace our simple solution with more featured implementation.

http://aries.apache.org/modules/blueprint.html

pátek 15. května 2015

Hibernate 4.3.7 patch for schema name customization

The JPA standard allows to hardly define a concrete schema for tables/entites via annotations (for example @Table). For my project was necessary to modify these shema names according to configuration on the realtime, just before EntityManagerFactory is created. Generally, the Hibernate version 4.3.7 provides for a db-to-java name mapping special interface org.hibernate.cfg.naming.NamingStrategyDelegate.

This interface provides lots of nice method for define a mapping between JPA objects names and db objects names, but one thing is missing - schema names customization.

Extending interface

For assurance of backward compatibility, I decided to create new interface as extended existing interface:

package org.hibernate.cfg.naming;
public interface ExtendNamingStrategyDelegate extends NamingStrategyDelegate {
  public String determineSchemaName(String schemaName);
}

Modification of internal behavior of hibernate was not easy. All names modifications is present in very extensive class org.hibernate.cfg.AnnotationBinder. This class contains cca 2800 lines of code and really needs to be refactored :). I added this code fragment on multiple places:

NamingStrategyDelegate n =
  mappings.getNamingStrategyDelegator().getNamingStrategyDelegate(false);

if (n instanceof ExtendNamingStrategyDelegate) {
  schema = ((ExtendNamingStrategyDelegate) n).determineSchemaName(schema);
  if (schema == null) {
    schema = "";
  }
}
If you want to see whole class, please download sourcecode.

Patching of original hibernate

The Hibernate framework is opensource and for building purposes is used the Gradle system. Unfortunately, I haven't any knowledges about Gradle and also our processes in company are hardly maven-oriented. I've decided to do it "dirty" way with 2 maven projects.
  1. hibernate-core-patch (version 4.3.7.1) - contains my created class ExtendNamingStrategyDelegate and modified AnnotationBinder only.  Project version 4.3.7.1 defines that patch is based on hibernate 4.3.7.
  2. hibernate-core (version 4.3.7.1-Patched)- Combination of original hibernate binaries (hibernate-core version 4.3.7) and forced injection of my modifications (hibernate-core-patch 4.3.7.1). This project does not contain any java code and it is defined as a integrator for binaries via maven-dependency-plugin.
Download sourcecode here.

Using of patch

After building and installing patched hibernate-core project, Use in your project patched version of hibernate-core instead of original (replace version 4.3.7 with 4.3.7.1-Patched).

For using NamingStrategyDelegate, please read original documentation

https://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch03.html#configuration-namingstrategy

With difference that you will use ExtendedNamingStrategyDelegate interface.

Summary

I'm not a fan of "raping" of frameworks, tools and standard procedures as shows this article:). But sometimes it can be very usefull and it can save lots of time before you find better way to solve a problem. In this time, Hibernate version 5 is comming and according to release notes, it should be refactored and also support for schema name mapping should be added.


středa 11. března 2015

Práce s časem v Oracle databázi

Ve třetí části série článků o relačních databázích si ukážeme základy práce s časem.
 Databáze poskytuje mnoho funkcí pro práci s časem, v této kapitole se však budeme zabývat jen tím, jak čas a datum získat. Protože problémy způsobují časové zóny, rovněž se zmíním i o této problematice.

V Oracle databázi máme dva časové zdroje: systémový čas a databázový čas.

Sytémový čas


Máme dotaz:

select sysdate, systimestamp, SYS_EXTRACT_UTC(SYSTIMESTAMP) utc from dual;

Výše uvedený dotaz vrací čas operačního systému:

SYSDATE             SYSTIMESTAMP                        UTC                       
------------------- ----------------------------------- ----------------------------
03.03.15 13:26:41   03.03.15 13:26:41,365645000 +01:00  03.03.15 12:26:41,365645000 



Rozdíl mezi sysdate a systimestamp není jen v přesnosti. Důležité je, že sysdate neobsahuje informaci o časové zóně. V okamžiku volání sysdate se aktuální zóna "aplikuje" do hodnoty a ta se pak vrátí v dotazu. Takto zýskaný a ukládány čas může způsobit obrovské problémy:

Příklad
Představte si, že Vaše aplikace vkládá do sloupečku hodnotu SYSDATE pro zaznamání vzniku řádku a db server běží v Praze. Pokud db server přesune do Austrálie - nově vzniklé hodnoty budou ukládány s aplikovaným časovým posunem +11 ale staré hodnoty byly uloženy např. s posunem +2. Pokud tedy vznikly dva řádky v rozmezí 1 minuty ale v jiných zónách (+11 a +2), výpočty nad těmito časy (např. doba mezi vložením dvou záznamů) nebudou odpovídat realitě.

Jak tedy správně ukládat čas (a datum) v databázi? Existují 2 způsoby:
  • Záznam bude uložen včetně časové zóny a pro zjištění se užije SYSTIMESTAMP
  • Záznam bude uložen v UTC (bez časového posunu, +0h).
Právě druhý zminovaný způsob je šetrný na diskový prostor a často i na výkon. K získání času v UTC je ukázáno v třetím sloupečku předchozího příkladu, tedy SYS_EXTRACT_UTC(SYSTIMESTAMP). Nyní je jedno, ve které časové zóně se server nachází, všechny časy budou v UTC.

Pro práci s časem se může hodit časová zóna operačního systému:

select to_char(SYSTIMESTAMP,'TZR') timezone from dual;

Výslekek je pak například

 TIMEZONE                     
--------------------------------
+01:00     


Databázový čas 


S databázovým časem  se pracuje podobně jako se systémovým časem. Hlavní rozdíl je v možnosti nastavení časových zón platných v rámci databáze (tedy ne pro celý operační systém) nebo pro jedno sezení (session).

Analogický dotaz k systémovému času

select CURRENT_DATE,CURRENT_TIMESTAMP,SYS_EXTRACT_UTC(CURRENT_TIMESTAMP) utc from dual;

Výsledek je pro tentokrát zcela totožný se systémovým časem:

 CURRENT_DATE        CURRENT_TIMESTAMP                   UTC                      
------------------- ----------------------------------- ----------------------------
03.03.15 14:10:41   03.03.15 14:10:41,411721000 +01:00  03.03.15 13:10:41,411721000  


Stejne tak zjištění časové zony databáze (aktuálního sezení)

select SESSIONTIMEZONE timezone from dual;

Oproti systémovému času si můžeme nyní časovou zónu upravit podle potřeby:

ALTER SESSION SET TIME_ZONE = '+11:0'; 

Nastavíme si časový posun +11 hod a okamžitě zkusíme opět dotaz k získání času databáze. Můžeme porovnat výsledky:

CURRENT_DATE        CURRENT_TIMESTAMP                   UTC                       
------------------- ----------------------------------- ----------------------------
04.03.15 00:15:23   04.03.15 00:15:23,064275000 +11:00  03.03.15 13:15:23,064275000  


Zatímco UTC čas se nezměnil, hodnota CURRENT_DATE a CURRENT_TIMESTAMP se změnila. U hodnoty CURRENT_TIMESTAMP nám to ovšem nevadí, časová zóna je je součástí hodnoty. Zato však CURRENT_DATE nám bude vracet jiné hodnoty, které (vysvětleno výše na příkladu) nesmí být smýchány s hodnotami před změnou časové zóny.

Závěr

V databázi existuje pochopitelně nepřeberné množství funkcí pro práci s časem. Účelem tohoto článku bylo jen naťuknout tuto problematiku a poukázat na nejpalčivější problém týkající se časových zón. Nyní je již vše na samotném čtenáři, jak se tohoto tématu chopí:) 
Času zdar.



úterý 3. března 2015

Tabulka dual

Z mých zkušeností zná tuto tabulku každý databázový vývojář a v podstatě žádný Java/ORM vývojář, což není překvapující - pokud programujete přímo v databázi, některé konstrukce nejsou bez této tabulky zbytečně obtížné, ne-li nemožné. Pro programátora užívající ORM pak "ztácí" tabulka význam. Slovo "ztrácí" je v uvozovkách oprávněně: Na běžnou práci v ORM skutečně tabulka není potřeba. Pokud chceme databázi používat efektivně a nebo jsme nuceni volat některé databázové funkce přímo, tabulka se nám náramě hodí. 

Tabulka dual je tabulka vlastní všem Oracle databázím a obsahuje typicky  jeden sloupec jménem DUMMY a jeden řádek s hodnotou 'X' typu VARCHAR2(1).
select * from dual;

Proč je tedy tak důležitá? Pomocí této tabulky lze libovolnou hodnotu, literál či výstup funkce převést do relace - t.j. tabulky.

na příklad:

select 'hello world' from dual;
select 1+1 from dual;
select sysdate from dual;

Všechny tyto příklady mají jednu věc společnou - vytvářejí z "ničeho" tabulku. Velmi často se tabulka hodí pro volání databázových funkcí. 

select 1+9/4, trunc(1+9/4),DBMS_RANDOM.RANDOM from dual;

Dotaz ukazuje volání funkce "trunc", jež slouží oříznutí desetinné části z čísla a zároveň volá funkci pro generování náhodného čísla.

Výsledek dotazů je pak třísloupcová tabulka:

1+9/4 TRUNC(1+9/4) RANDOM
----- ------------ ------
 3,25            3 610212023


Z předchozích příkladů je patrný význam tabulky dual: slouží nám jako jakýsi můstek mezi konvenčním procedurálním a relačním přístupem. Článek ukázal nejzákladnější použití tabulky. V dalších dílech bude tabulka využita v mnohem větší míře a její účel bude pak jasnější. Tak zase příště.


pátek 9. září 2011

Započala práce na Mainframe Gateway

Během září se zúčastňuji jako sofrawrový architekt a vývojář na realizaci projektu "Mainframe Gateway" pro společnost ČSA.

Cílem je vytvořit integrační platformu pro systémy v prostředí ČSA a systémy typu mainframe, které mají velmi specifické a "těžkopádné" rozhraní. Mezi další funkčnosti aplikace bude využití mechanizmu mezipaměti, logování, administrátorské rozhraní a dalších.




V první fázi projektu půjde o získání údajů o elektronické letence z mainframe SITA podle identifkikačního čísla letenky.

Celý projekt bude postaven na technologii J5EE, jako aplikační server se použije GlassFish v2.