O technologii i biznesie naszym zdaniem
Archive for luty, 2009
Mate Flex Framework
lutego 13th
Mate (wym. mate, nie mejt) jest Fleksowym frameworkiem, opartym na tagach (ang. tag-based) i sterowanym zdarzeniami (ang. event-driven). Używamy go w jednym z naszych projektów już od kilku miesięcy i z perspektywy tego czasu, chciałbym parę słów o nim napisać.
Celem Mate jest zaproponowanie prostego i skutecznego mechanizmu zorganizowania przepływu sterowania (ang. control flow) w stylu MVC, z wykorzystaniem eventów Fleksa. Jest to jedna z głównych zalet Mate: zostaje wykorzystany już istniejący, sprawdzony i naturalny dla Fleksa mechanizm komunikacji między warstwami aplikacji. Framework nie wprowadza własnych rozwiązań, jeśli nie ma takiej potrzeby, co wspiera niezależność tworzonych modułów programowych.
Mate jest całkowicie przezroczysty z punktu widzenia aplikacji. Nie ma żadnych odniesień do klas frameworka zarówno w komponentach widoku, jak również biznesowych. Naturalnie, warstwa kontrolerów (w sensie MVC), musi być już powiązana z konkretną biblioteką i tak też jest w tym przypadku. Zastosowano jednak dość ciekawe rozwiązanie, polegające na zapisywaniu logiki sterowania w postaci tzw. map zdarzeń (ang. event map) definiowanych za pomocą XML. Określają one sekwencje poleceń do wykonania w odpowiedzi na wystąpienie konkretnych eventów. Przykładowo, nasza klasa zdarzenia mogłaby wyglądać tak:
package pl.espeo
{
import flash.events.Event;
public class AuthEvent extends Event
{
public static const LOGIN:String = "loginAuthEvent";
public var username:String;
public var password:String;
public function AuthEvent(type:String, bubbles:Boolean = true, cancelable:Boolean = false)
{
super(type, bubbles, cancelable);
}
}
}
Blok przetwarzania dla tego zdarzenia mógłby wyglądać tak:
<EventMap ...>
<EventHandlers type="{AuthEvent.LOGIN}">
<RemoteObjectInvoker instance="{authService}"
method="loginRemoteMethod" arguments="{[event.username, event.password]}">
<resultHandlers>
<MethodInvoker generator="{AuthManager}"
method="loginLocalMethod" arguments="{resultObject}"/>
</resultHandlers>
</RemoteObjectInvoker>
</EventHandlers>
</EventMap>
Jeśli gdziekolwiek w naszej aplikacji zostanie “wyrzucony” event o typie AuthEvent.LOGIN, zostanie wykonany odpowiedni kod z mapy zdarzeń, który w tym przypadku wywołuje metodę loginRemoteMethod zdalnego obiektu authService, przekazując 2 argumenty: username oraz password. Po otrzymaniu wyniku tego wywołania, zostanie on następnie przekazany jako argument metody loginLocalMethod obiektu klasy AuthManager, która zrealizuje jakąś logikę związaną z obsługą wyniku logowania. Takie podejście często określa się mianem MVCS, gdzie to dodatkowe S oznacza Service. Mate promuje taką architekturę, oferując znaczniki obsługujące nie tylko zdalne obiekty, ale również WebService’y oraz dowolne inne usługi HTTP (np. w konwencji REST). Diagram przepływu sterowania w opisanym scenariuszu wygląda tak:

Jest to przypadek stosunkowo prosty. Możliwości Mate w zakresie kontroli przepływu sterowania są znacznie bardziej rozbudowane; jest np. możliwość zdefiniowania metody obsługi błędów zdalnych usług albo wywołanie odpowiednich metod logiki widoku w zależności od wyniku wykonania metod logiki biznesowej. Jeśli konieczne jest zaimplementowanie czegoś naprawdę skomplikowanego, na co tagi Mate mogą nie pozwolić, zawsze można napisać własną metodę w ActionScript i wydelegować przetwarzanie do niej. Możliwości są tutaj w zasadzie nieograniczone. Kolejny wielki plus Mate: bardzo duża elastyczność.
Oprócz realizacji architektury MVC(S), Mate zapewnia również implementację mechanizmu wstrzykiwania zależności (ang. dependency injection). Ponownie, nie ma tutaj ingerencji we właściwy kod logiki biznesowej czy widoku; zależności do wstrzyknięcia definiuje się w mapach zdarzeń. Mate oferuje tutaj jedynie sposób deklaratywnego opisu powiązań między obiektami. Właściwe wstrzykiwanie jest po stronie Fleksa i jego atrybutu [Bindable]. To kolejny przykład nieinwazyjności tego frameworka. Jeśli platforma programowa realizuje jakąś funkcjonalność dobrze, po co to powielać? Spójrzmy, jak łatwo w Mate wstrzykuje się obiekt bieżącej sesji (pole currentSession obiektu klasy SessionManager) do menedżera autoryzacji (pole session obiektu klasy AuthManager):
<EventMap ...>
<Injectors target="{AuthManager}">
<PropertyInjector source="{SessionManager}"
sourceKey="currentSession" targetKey="session" />
</Injectors>
</EventMap>
Podsumowując, Mate to naprawdę świetny pomysł na wprowadzenie do własnego projektu dobrej, spójnej architektury MVC. Promuje najlepsze praktyki rozwiązywania typowych problemów, występujących w każdej aplikacji, np. problemu zależności między komponentami z rozwiązaniem w postaci wstrzykiwania zależności. Zapewnia ogromną elastyczność, pozwalając w elegancki sposób wspomóc framework własnym kodem w ActionScript, jeżeli dostarczone tagi okażą się niewystarczające. Jest przy tym całkowicie nieinwazyjny, przez co komponenty widoku i logiki biznesowej mogą być ponownie wykorzystane.
Mimo młodego wieku, Mate oferuje naprawdę dużą funkcjonalność, która jest cały czas rozszerzana. Śledząc stronę projektu i jego forum, można przekonać się, że projekt żyje, jest rozwijany, a społeczność zadowolonych programistów-użytkowników Mate nieustannie się powiększa. Myślę, że warto się nim zainteresować. Już teraz jest bardzo poważnym konkurentem takich rozwiązań jak Cairngorm czy PureMVC. Jeśli projekt nie wytraci impetu to może już niedługo być najchętniej wybieranym frameworkiem dla nowopowstających projektów Fleksowych.
Gorąco zachęcam do odwiedzenia strony domowej projektu:
http://mate.asfusion.com/
Integracja Spring Flex
lutego 5th
Ostatnimi czasy dużą popularność przy tworzeniu bogatych aplikacji internetowych (RIA) zdobywa podejście, w którym warstwę widoku pisze się z użyciem Fleksa lub innej technologii ułatwiającej budowanie interfejsów, a warstwy logiki biznesowej i dostępu do danych implementuje się przy pomocy Javy. Dodatkowo Java jest bardzo często wspierana przez bardzo popularne frameworki – Spring oraz Hibernate.
Do łączenia aplikacji klienckiej napisanej we Fleksie z aplikacją serwerową napisaną w Javie zazwyczaj używa się darmowego BlazeDS lub komercyjnego LiveCycle ES, oba te narzędzia są autorstwa firmy Adobe i udostępniają wiele ciekawych możliwości. Chyba najszybszym i najlepszym sposobem połączenia Fleksa z Javą jest wykorzystanie tzw. remote objects. Jest to po prostu zdalne wywoływanie metod obiektów napisanych w Javie z poziomu Fleksa, cała komunikacja odbywa się przy użyciu bardzo wydajnego, binarnego protokołu AMF (Action Message Format).
Dotychczas, aby z poziomu Fleksa skorzystać z beanów tworzonych przez Springa z użyciem wstrzykiwania zależności, należało wykonać wiele niekoniecznie prostych i oczywistych operacji. Domyślnie sam BlazeDS tworzy skonfigurowane w jego plikach obiekty, co w tym przypadku było działaniem niepożądanym. Trzeba było skonfigurować odpowiednią fabrykę dla BlazeDS, tak aby pozwolił on zająć się tworzeniem odpowiednich obiektów kontenerowi Springa. Były to dość złożone operacje i potrafiły spowodować pojawienie się wielu siwych włosów nawet u doświadczonych developerów.
Sytuacja diametralnie zmieniła się pod koniec zeszłego roku, gdy SpringSource wypuściło na światło dzienne nowy projekt – Spring BlazeDS Integration. Na razie jest to jeszcze dość świeży projekt, jednak pozwala już na użycie beanów tworzonych przez Springa w aplikacjach napisanych we Fleksie przy stosunkowo niewielkim wysiłku. W najbliższych planach jest m.in. implementacja mechanizmu Spring Security, bardzo pożądanego przez developerów tworzących aplikacje Fleksowe.
Aktualnie, przy użyciu Spring BlazeDS Integration, udostępnienie obiektów tworzonych przez Springa sprowadza się do kilku prostych kroków:
1.Po stronie Javy:
a) ustawienie odpowiedniego listenera w pliku web.xml:
<listener> <listener-class>flex.messaging.HttpFlexSession</listener-class> </listener>
b) praktycznie standardowa konfiguracja springowego servletu w pliku web.xml:
<!-- The front controller of this Spring Web application --> <servlet> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/config/web-application-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all /spring requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <url-pattern>/spring/*</url-pattern> </servlet-mapping>
c) dodanie odpowiedniego MessageBrokera w pliku konfiguracyjnym podanym powyżej jako parametr (web-application-config.xml):
<!-- Bootstraps and exposes the BlazeDS MessageBroker --> <bean id="mySpringManagedMessageBroker" class="org.springframework.flex.messaging.MessageBrokerFactoryBean" />
Będzie on używał pliku konfiguracyjnego BlazeDS z domyślnego miejsca (WEB-INF/flex/services-config.xml). Aby zmienić to miejsce, należy dodać do powyższego beana następujący parametr:
<property name="servicesConfigPath" value="classpath*:services-config.xml" />
d) dodanie mapowania do MessageBrokera z BlazeDS (również w web-application-config.xml):
<!-- Maps request paths at /messagebroker to the BlazeDS MessageBroker -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/messagebroker/*=mySpringManagedMessageBroker
</value>
</property>
</bean>
e) dodanie kanału odpowiadającego powyższej konfiguracji w pliku services-config.xml:
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
</properties>
</channel-definition>
f) przykładowa definicja obiektu, który ma zostać wystawiony Fleksowi (znowu web-application-config.xml):
<!-- Implementation of HelloService --> <bean id="blazeHelloService" class="pl.espeo.blog.HelloServiceImpl" > </bean> <!-- Expose the blazeHelloService bean for BlazeDS remoting --> <bean id="helloService" class="org.springframework.flex.messaging.remoting.FlexRemotingServiceExporter"> <property name="messageBroker" ref="mySpringManagedMessageBroker"/> <property name="service" ref="blazeHelloService"/> </bean>
Po takim skonfigurowaniu z poziomu Fleksa powinien być dostępny obiekt o nazwie „helloService” jako remote object.
2.Po stronie Fleksa:
a) odpowiednia konfiguracja do współpracy z BlazeDS – ustawienie projektu do korzystania ze standardowego pliku services-config.xml
b) obiekt „helloService” użyty we Fleksie:
<mx:RemoteObject id="helloSrv" destination="helloService"/>
Po prawidłowym ustawieniu plików konfiguracyjnych w obu aplikacjach można bezproblemowo cieszyć się springowymi beanami dostępnymi z poziomu Fleksa. Dokumentację dotyczącą projektu Spring BlazeDS Integration można przeczytać na stronie projektu:
Actionscript i tworzenie bibliotek zasobów
lutego 4th
Nie trudno wyobrazić sobie sytuację, kiedy w naszej aplikacji Flash/Flex chcielibyśmy wyprowadzić pewne zasoby na zewnątrz programu w celu łatwej ich podmiany/update. Przykładem może być np. pole w naszej nowej super grze, lub sprite przeciwnika z tej samej mega produkcji. Taki element programu będzie opisywany zarówno przez jego wizualna reprezentację jak i przez zestaw parametrów.
W naszej aplikacji nie chcemy znać definicji tych obiektów, chcemy załadować kontener swf zawierający taki obiekt do głównej aplikacji i uzyskać dostęp do zasobów. Do dzieła!
Nasza prosta aplikacja będzie miała za zadanie wzywanie bohaterów którzy uratują świat przed nieuniknioną zagładą. Przygotujmy interface dla takiego bohatera:
package pl.espeo.hero
{
import flash.display.Bitmap;
public interface IHero
{
function getName():String;
function getImage():Bitmap;
}
}
Jak widać jedyne co nasz bohater będzie potrafił to powiedzieć jak się nazywa i dać nam swoją pamiątkową fotografię. Dodajmy klasę po której nasi poszczególni bohaterowie będą dziedziczyć:
package pl.espeo.hero
{
import flash.display.Bitmap;
import flash.display.Sprite;
public class SuperHero extends implements IHero
{
protected var heroName:String;
protected var heroImage:Bitmap;
public function SuperHero(heroName:String, heroImage:Bitmap)
{
super();
this.heroName = heroName;
this.heroImage = heroImage;
}
public function getName():String
{
return heroName;
}
public function getImage():Bitmap
{
return heroImage;
}
}
}
Czas przedstawić naszych zbawców. Pierwszy z nich to nikt inny jak Rambo:
package
{
import pl.espeo.hero.SuperHero;
public class Hero extends SuperHero
{
[Embed(source="assets/rambo.jpg")]
private var EmbedImage:Class;
public function Hero()
{
super('Rambo', new EmbedImage());
}
}
}
Tak przygotowanego bohatera kompilujemy do pliku swf. To samo robimy z naszym następnym bohaterem, Kapitanem Planetą:
package
{
import pl.espeo.hero.SuperHero;
public class Hero extends SuperHero
{
[Embed(source="assets/captain_planet.jpg")]
private var EmbedImage:Class;
public function Hero()
{
super('Captain Planet', new EmbedImage());
}
}
}
Mamy więc naszych bohaterów, czas przygotować dla nich podium oraz opracować sposób komunikacji z nimi. Aby nasza komunikacja przebiegała poprawnie, oraz aby nie przywołać zwykłego śmiertelnika w naszej aplikacji będziemy potrzebowali interfacu pl.espeo.hero.IHero. Czas przygotować formę komunikacji z naszym bohaterem:
package pl.espeo.hero
{
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLRequest;
public class HeroSummoner extends EventDispatcher
{
public static const CAPTAIN_PLANET:String = 'CaptainPlanet';
public static const RAMBO:String = 'Rambo';
private var loader:Loader = new Loader();
public function HeroSummoner()
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteEventHandler);
}
public function callHero(heroName:String):void
{
loader.load(new URLRequest('assets/' + heroName + '.swf'));
}
private function loaderCompleteEventHandler(event:Event):void
{
var Hero:Class = event.target.applicationDomain.getDefinition('Hero') as Class;
var heroEvent:HeroEvent = new HeroEvent(HeroEvent.SUMMONED);
heroEvent.hero = new Hero();
dispatchEvent(heroEvent);
}
}
}
dzięki tak przygotowanej klasie, zostaniemy poinformowani eventem HeroEvent że nasz bohater jest już na miejscu.
package pl.espeo.hero
{
import flash.events.Event;
public class HeroEvent extends Event
{
public static const SUMMONED:String = 'pl.espeo.hero.HeroEventSummoned';
public var hero:IHero;
public function HeroEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
Tak może to wyglądać w aplikacji flex:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()">
<mx:Script>
<![CDATA[
import pl.espeo.hero.HeroEvent;
import pl.espeo.hero.HeroSummoner;
import mx.collections.ArrayCollection;
import pl.espeo.hero.IHero;
public static const NO_HERO:uint = 0;
[Bindable]
private var heroes:ArrayCollection = new ArrayCollection([{label:'There is no hero!', data:NO_HERO},
{label:'First hero', data:HeroSummoner.CAPTAIN_PLANET},
{label:'Second hero', data:HeroSummoner.RAMBO}]);
private var heroSummoner:HeroSummoner = new HeroSummoner();
private function init():void
{
heroSummoner.addEventListener(HeroEvent.SUMMONED, welcomeMyHero);
}
private function summonHero(event:Event):void
{
var hero:* = ComboBox(event.target).selectedItem.data;
if (hero == NO_HERO)
{
return;
}
heroSummoner.callHero(hero);
}
private function welcomeMyHero(event:HeroEvent):void
{
heroPanel.status = event.hero.getName();
heroImage.source = event.hero.getImage();
}
]]>
</mx:Script>
<mx:HBox>
<mx: Panel id="heroPanel" x="10" y="10" width="400" height="400" layout="absolute">
<mx:Image id="heroImage" x="0" y="0"></mx:Image>
</mx: Panel>
<mx:ComboBox id="heroesList" dataProvider="{heroes}" change="{summonHero(event)}"/>
</mx:HBox>
</mx:Application>
Dzięki tak przygotowanym obiektom możemy zbudować elastyczny system z zasobami zamkniętymi w łatwych do podmiany plikach. Dodając do tego prosty program narzędziowy wykorzystujący kompilator mxmlc możemy stworzyć furtkę, dzięki której przygotowanie kolejnych elementów nie będzie wymagało ingerencji programisty.