how to filter specific class logger in logback.xml? - logback

we try to filter logs generated from one class:
com.websudos.phantom
for two goals:
all logs from app saved in the file except log from this calss
all log from this file transferred to graylog.
we have filter those log by regex with this filter:
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<matcher>
<Name>parameter</Name>
<regex>Executing query</regex>
</matcher>
<expression>parameter.matches(formattedMessage)</expression>
</evaluator>
<OnMismatch>DENY</OnMismatch>
<OnMatch>ACCEPT</OnMatch>
</filter>
the Executing query regex for this class : com.websudos.phantom
and we do not accept to set a level of this class to OFF because we need this log to transfer to graylog and not saving in the file!
whats a solution?

this is a solution:
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
<expression>logger.equals("com.websudos.phantom")</expression>
</evaluator>
<OnMismatch>NEUTRAL</OnMismatch>
<OnMatch>DENY</OnMatch>
</filter>
by add this filter to any appander, logs from class com.websudos.phantom ignored

XML:
<filter class="com.websudos.loggers.ClassNameFilter">
<className>com.websudos.phantom</className>
<onMatch>ACCEPT</onMatch>
</filter>
Java:
package com.websudos.loggers;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.AbstractMatcherFilter;
import ch.qos.logback.core.spi.FilterReply;
public class ClassNameFilter extends AbstractMatcherFilter<ILoggingEvent> {
String loggerName;
#Override
public FilterReply decide(ILoggingEvent event) {
if (!isStarted()) {
return FilterReply.NEUTRAL;
}
if (event.getLoggerName().equals(loggerName)) {
return onMatch;
} else {
return onMismatch;
}
}
public void setClassName(String className) {
this.loggerName = className;
}
#Override
public void start() {
if (this.loggerName != null) {
super.start();
}
}
}
You can easily enough modify this to check logging level as well. See ch.qos.logback.classic.filter.LevelFilter for an example.

Related

Configuring Castle Windsor using xml/app.config

I am currently building a sample application using Castle Windsor. The motto is to use xml/app.config to switch method interception on/off. I had used the Fluent API earlier and it worked as a charm. As the next step, I am trying to replace the fluent API with my xml.
The gist of the code is as follows:
A class called RandomOperations with two virtual methods.
A LoggingAspect class which implements IInterceptor.
A MyInterceptorsSelector class which implements IModelInterceptorsSelector
A Program.cs which had the fluent api syntax earlier and is now uses to only make calls to methods of RandomOperations class.
An app.config with a section called which has the xml syntax of registering components.
When I use the fluent api, I am able to intercept the method calls but I am unable to do it using the xml/app.config registration. Could someone please throw some light on what is being missed?
The classes are as follows:
RandomOperations.cs
public class RandomOperations
{
public virtual int MyRandomMethod(int x)
{
return x * x;
}
public virtual void Writer(string x)
{
Console.WriteLine(x);
}
}
LoggingAspect.cs
public class LoggingAspect : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("Intercepted the call to " + invocation.Method.Name);
invocation.Proceed();
Console.WriteLine("After the method call, the return value is " + invocation.ReturnValue);
}
}
MyInterceptorsSelector.cs
public class MyInterceptorsSelector : IModelInterceptorsSelector
{
public bool HasInterceptors(ComponentModel model)
{
return typeof(LoggingAspect) != model.Implementation &&
model.Implementation.Namespace.StartsWith("ConsoleApplication1") ;
}
public InterceptorReference[] SelectInterceptors(ComponentModel model, Castle.Core.InterceptorReference[] obj)
{
var interceptors = new List<InterceptorReference>(model.Interceptors.Count + 1);
foreach (InterceptorReference inter in model.Interceptors)
{
interceptors.Add(inter);
}
return interceptors.ToArray();
}
}
Main in Program.cs
static void Main(string[] args)
{
var container = new WindsorContainer();
//container.Register(Component.For<RandomOperations>().Interceptors(typeof(LoggingAspect)));
//container.Register(Component.For<LoggingAspect>());
//container.Kernel.ProxyFactory.AddInterceptorSelector(new MyInterceptorsSelector());
var service = container.Resolve<RandomOperations>();
service.MyRandomMethod(4);
service.Writer("Hello, World");
}
Removing the commented out fluent api syntax makes the application work correctly.
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
</configSections>
<castle>
<components>
<component id="MyInterceptorsSelector" type="MyInterceptorsSelector"/>
<component
id="LoggingAspect"
type="ConsoleApplication1.LoggingAspect, ConsoleApplication1">
</component>
<component
type="ConsoleApplication1.RandomOperations, ConsoleApplication1">
<interceptors selector="${MyInterceptorsSelector}">
<interceptor>${LoggingAspect}</interceptor>
</interceptors>
</component>
</components>
</castle>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
Thanks in advance.
You need to pass an IConfigurationInterpreter to your Windsor constructor. Change:
var container = new WindsorContainer();
To:
var container = new WindsorContainer(new XmlInterpreter());
The XmlInterpreter (with no parameters) will pull configuration from your app.config/web.config.
For more options on using IConfigurationInterpreter, see the docs.

how to integrate progress indicator for each dynamic item of longlistselector in windows phone 8?

Actually LonglistSelector bind with a dynamic list of image uri. i need to display loading indicator for each image until image loaded. If anybody knows how to make it done please help me Immediately. Thanks in advance.
You will have to provide a list of objects that know the following:
Image URL
Loading Indicator
The LonglistSelector Item Template would then display the Loading animation as long as LoadingIndicator is true. As soon as it becomes false, it would display the image.
This can be achieved using the Visibility property and a BooleanToVisibilityConverter class.
The model stated on top must implement INotifyPropertyChanged for the loading indicator to get the automatic UI refresh for each item as soon as it got changed.
Your model would look like this. The refered class BindableBase is a class from Microsoft which can be found here: http://msdn.microsoft.com/en-US/en-en/library/windows/apps/xx130657.aspx
public class ImageViewModel : BindableBase
{
/// <summary>
/// No need to use SetProperty here, because this parameter is not bound against any UI control
/// which would need a refresh
/// </summary>
public string ImageUrl { get; set; }
private bool isLoading;
/// <summary>
/// Reflects loading status of the underlying tile.
/// </summary>
public bool IsLoading
{
get { return isLoading; }
set { SetProperty(ref isLoading, value); }
}
private ImageSource imageContent;
/// <summary>
/// Reflects image which shall be displayed.
/// </summary>
public ImageSource ImageContent
{
get { return imageContent; }
set { SetProperty(ref imageContent, value); }
}
}
Your UI would bind against a list of those model entities and display the loading animation when isLoading is true, else the image. For this, you can use the BooleanToVisibilityConverter class which is most of the time created together with new projects. But you may search for the class online.
Now when your page is loaded, you iterate through your list of items and start downloading the image. Once you have downloaded it, replace the "ImageContent" with the actual image and set IsLoading to false. Your UI will then display the image and hide the loading animation because both properties are notitied to the UI.
Your data template would look something like this:
<DataTemplate x:Key="itemTemplateLongListSelector">
<StackPanel Width="230" Height="290" Margin="4,4">
<Image Width="230" Height="290" Stretch="UniformToFill" Visibility="{Binding IsLoading, Converter={StaticResource BooleanNegationToVisibilityConverter}}">
<Image.Source>
<BitmapImage UriSource="{Binding ImageSource}"/>
</Image.Source>
</Image>
<TextBlock Text="Loading..." Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</DataTemplate>
The converter classes needed:
public sealed class BooleanNegationToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value is bool && (bool)value) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value is Visibility && (Visibility)value == Visibility.Collapsed;
}
}
public sealed class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value is bool && (bool)value) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value is Visibility && (Visibility)value == Visibility.Visible;
}
}
Both need to be declared in your App.xaml resource dictionary section:
<common:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<common:BooleanNegationToVisibilityConverter x:Key="BooleanNegationToVisibilityConverter"/>
"common" is defined in App.xaml on top:
<Application
...
xmlns:common="clr-namespace:YourProject.Path.To.Converter.Classes"
...>

How can I set a pattern in a DBAppender?

I using logback and put a pettern into a dbappender, but it doesn´t work.
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource
class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>net.sourceforge.jtds.jdbc.Driver</driverClass>
<url>jdbc:jtds:sqlserver://xxx.xxx.xxx.xx:1433/granica</url>
<user>java</user>
<password>java</password>
</connectionSource>
<encoder>
<pattern>%d{HH:mm:ss.SSS} - %msg%n</pattern>
</encoder>
</appender>
someone know how to fix that?
thanks in advance!
You can't put a pattern in a DBAppender:
"The DBAppender inserts logging events into three database tables in a format independent of the Java programming language.
*These three tables are logging_event, logging_event_property and logging_event_exception. They must exist before DBAppender can be used. Logback ships with SQL scripts that will create the tables."*
Alternative solution: If you realy need this pattern, then you should create an extra dataBbase table/view/sql that generaties that output for you, based on the existing tables. This can be done with a simple SQL, View or Triggers. Use standard SQL to create the output you need.
Good luck!
I managed to customize the formatted message easily enough by defining a couple of custom classes, one extending the DBAppender and the other implementing the ILoggingEvent interface:
package com.mypackage;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Map;
import org.slf4j.Marker;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.db.DBAppender;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.LoggerContextVO;
/**
*
* #author PJ_Finnegan
*
* To override the default DBAppender with a custom FORMATTED_MESSAGE
*
*/
public class MdcDbAppender extends DBAppender {
#Override
protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement)
throws Throwable {
// use a special event with custom formatted message
MdcLoggingEvent customEvent = new MdcLoggingEvent(event);
super.subAppend(customEvent, connection, insertStatement);
}
}
/**
*
* #author PJ_Finnegan
*
* To override the getFormattedMessage() method prepending MDC info
*
*/
class MdcLoggingEvent implements ILoggingEvent {
private ILoggingEvent mOrigEvent;
public MdcLoggingEvent(ILoggingEvent origEvent) {
mOrigEvent = origEvent;
}
#Override
public String getThreadName() {
return mOrigEvent.getThreadName();
}
#Override
public Level getLevel() {
return mOrigEvent.getLevel();
}
#Override
public String getMessage() {
return mOrigEvent.getMessage();
}
#Override
public Object[] getArgumentArray() {
return mOrigEvent.getArgumentArray();
}
#Override
public String getFormattedMessage() {
// add my MDC info as a prefix
String mdcVals[] = new String[] { "NA", "NA", "NA" };
Map<String, String> mpm = mOrigEvent.getMDCPropertyMap();
if (mpm != null) {
int i = 0;
for (String key : new String[] { "jobName", "jobInstanceId", "jobExecutionId" }) {
if (mpm.containsKey(key)) {
mdcVals[i] = mpm.get(key);
}
i++;
}
}
return String.format("[%s-%s-%s] ", mdcVals[0], mdcVals[1], mdcVals[2]) + mOrigEvent.getFormattedMessage();
}
#Override
public String getLoggerName() {
return mOrigEvent.getLoggerName();
}
#Override
public LoggerContextVO getLoggerContextVO() {
return mOrigEvent.getLoggerContextVO();
}
#Override
public IThrowableProxy getThrowableProxy() {
return mOrigEvent.getThrowableProxy();
}
#Override
public StackTraceElement[] getCallerData() {
return mOrigEvent.getCallerData();
}
#Override
public boolean hasCallerData() {
return mOrigEvent.hasCallerData();
}
#Override
public Marker getMarker() {
return mOrigEvent.getMarker();
}
#Override
public Map<String, String> getMDCPropertyMap() {
return mOrigEvent.getMDCPropertyMap();
}
#Override
public Map<String, String> getMdc() {
return mOrigEvent.getMdc();
}
#Override
public long getTimeStamp() {
return mOrigEvent.getTimeStamp();
}
#Override
public void prepareForDeferredProcessing() {
mOrigEvent.prepareForDeferredProcessing();
}
}
This way the LOGGING_EVENT.FORMATTED_MESSAGE column values on the DB will have the string [jobName-jobInstanceId-jobExecutionId] (whose value I had previously put in MDC) prepended:
TIMESTMP
FORMATTED_MESSAGE
1635255763784
[test-2760-3980] HikariPool-1 - Shutdown completed.
Of course you want to use your custom class in your logback.xml file instead of the stock DBAppender:
<!-- DB appender -->
<!-- use our custom class to override the FORMATTED_MESSAGE default pattern -->
<!-- <appender name="ORADB" class="ch.qos.logback.classic.db.DBAppender"> -->
<appender name="ORADB" class="com.mypackage.MdcDbAppender">
<connectionSource
...
Nothing prevents from overriding the ILoggingEvent.getFormattedMessage() method with more elaborated manipulations.

how to work with event in Flex?

import flash.events.Event;
public class RequestEvent extends Event
{
public static const REQUEST:String = "request";
private var Results:Boolean;
public function get Results():Boolean
{
return _Results;
}
public function RequestEvent(Results:Boolean=false)
{
super(REQUEST);
Results = Results;
}
override public function clone():Event
{
return new RequestEvent(Results);
}
}
}
hi can some body explain why we are doing overridding of function clone and calling super(request), new in flex ........so don't mind.
One needs to implement the clone method just so that Flex could re-clone the event in the case when an event handler wishes to dispatch the same event again. Flex does provide a default implementation but one may override the method to clone the event differently, if need be.
As for calling the super method, you must call the super becasue you are extending the Event class. The type (in your case, REQUEST) must be a unique string that would uniquely identify the event to Flex platform.
Hope it helps
Regards.
The question about the overriding of the clone method in custom events is very popular and it seems to be one of the Flex strange things. You can read about it here.
So you have to override this method and only in this method you can define values of custom properties. The method is not usual, so if you try to debug it you will never get the debugger in its body.
If you try to define the value of your custom property in the constructor, the value will be ignorred.
It can be unpractical to use a constant string as the event's type value. In this case all instances of your RequestEvent are of the same type and you could not tell them appart using in different situations. As you can see in the example below, this string is used in action listener to map the listener function.
In this example I have three buttons with different events - normal Event, my version of RequestEvent and your version of it. Have a look at it, I hope it can help to understand the case.
//Application
<fx:Script>
<![CDATA[
import fld02.com.customevent.RequestEvent;
import fld02.com.customevent.RequestEvent2;
import mx.controls.Alert;
private function onCustomGroupBtn2Clicked(evt:RequestEvent):void
{
Alert.show('Btn2Clicked: results = ' + evt.results.toString(), 'This is RequestEvent');
}
private function onCustomGroupBtn3Clicked(evt:RequestEvent2):void
{
Alert.show('Btn3Clicked: Results = ' + evt.Results.toString(), 'This is your RequestEvent');
}
]]>
</fx:Script>
<customevent:CustomGroup
BUTTON1_CLICKED="{Alert.show('Btn1Clicked', 'This is Event')}"
BUTTON2_CLICKED="onCustomGroupBtn2Clicked(event)"
request="onCustomGroupBtn3Clicked(event)"/>
</s:Application>
//CustomGroup
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="346" height="144">
<fx:Metadata>
[Event(name="BUTTON1_CLICKED", type="flash.events.Event")]
[Event(name="BUTTON2_CLICKED", type="fld02.com.customevent.RequestEvent")]
[Event(name="request", type="fld02.com.customevent.RequestEvent2")]
</fx:Metadata>
<fx:Script>
<![CDATA[
private function onBtn1Click():void
{
this.dispatchEvent(new Event("BUTTON1_CLICKED"));
}
private function onBtn2Click():void
{
var requestEvent:RequestEvent = new RequestEvent("BUTTON2_CLICKED");
requestEvent.results = true;
this.dispatchEvent(requestEvent);
}
]]>
</fx:Script>
<s:Button x="43" y="31" width="183" label="Generate Event" click="onBtn1Click()"/>
<s:Button x="43" y="62" width="183" label="Generate RequestEvent" click="onBtn2Click()"/>
<s:Button x="43" y="93" width="183" label="Generate Your RequestEvent" click="{this.dispatchEvent(new RequestEvent2(true))}"/>
</s:Group>
//My RequestEvent
package fld02.com.customevent
{
import flash.events.Event;
public class RequestEvent extends Event
{
private var _results:Boolean;
public function RequestEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
override public function clone():Event
{
var requestEvent:RequestEvent = new RequestEvent(this.type);
requestEvent.results = this.results;
return requestEvent;
}
public function get results():Boolean
{
return _results;
}
public function set results(value:Boolean):void
{
_results = value;
}
}
}
//Your RequestEvent
package fld02.com.customevent
{
import flash.events.Event;
public class RequestEvent2 extends Event
{
public static const REQUEST:String = "request";
public function RequestEvent2(Results:Boolean=false)
{
super(REQUEST);
Results = Results;
}
private var _Results:Boolean;
public function get Results():Boolean
{
return _Results;
}
override public function clone():Event
{
return new RequestEvent2(Results);
}
}
}

using getDefinitionByName correctly

here is my code line :
new (getDefinitionByName(String( "mypackage.MyDynamicClass" )) as Class) ;
This generates an error : mypackage.MyDynamicClass is not defined.
I googled and found a solution : Use the name of the class in the import statement.
So,
import mypackage.MyDynamicClass
new (getDefinitionByName(String( "mypackage.MyDynamicClass" )) as Class) ;
It worked!!!
but, I am not satisfied with this solution, as it really violates the benefit of a dynamic class. If i know the name of the class ( in import ), then why i would be using it as a string ?
Any alternatives to make dynamic loading of classes work ?
Vishwas
Take look at this article: http://blogs.adobe.com/cantrell/archives/2010/09/loading-classes-dynamically-in-actionscript-3.html
You cannot avoid including the classes you need at the runtime. You can always use:
import mypackage.*
Be aware at the fact that including all the classes in the package might increase the code size.
It definitely not violating dynamic loading of Class because of our compiler detect unused class/import statement in our project then get rid of those class file for packed as swc or swf so the advantage of our final output file size can reduce.
Unless few times unable reduce file size.
Probably you know what are the class you are trying to load via getDefinitionByName() before make sure that class available in IncludeClass.as
For better way, you can import statement for your project
package
{
public class IncludeClasses
{
import com.abc.db.Database; Database;
import com.abc.RemoteLogTarget; RemoteLogTarget;
import com.abc.LocalLogTarget; LocalLogTarget;
import com.abc.exception.GlobalExceptionHandler; GlobalExceptionHandler;
import com.abc.utils.NetConnectionMonitor; NetConnectionMonitor;
}
}
Stil you want to use your way better you can try with compiler options.
I suggest that you use a abstract factory
http://en.wikipedia.org/wiki/Abstract_factory_pattern
Its a very flexible way to create objects without knowing wich class you are going to instantiate.
Is in this abstract factory class in wich you will need to import all the clases you might create objects from, this will not require you to import all clases in your main class but you will still need to import the abstract factory and the interface to communicate with your new objects.
Here is a quick example:
/*
The interface you will use to communicate with your objects
*/
InterfaceForAllMyClasses.as
public interface InterfaceForAllMyClasses
{
public function callMe();
public function callMe2();
}
/*
Your Classes wich implement the interface
*/
Class1.as
import mypackage.InterfaceForAllMyClasses;
public class Class1 implements InterfaceForAllMyClasses
{
public function callMe() { trace("called Class1"); }
public function callMe2() { trace("called Class1 too"); }
}
Class2.as
import mypackage.InterfaceForAllMyClasses;
public class Class1 implements InterfaceForAllMyClasses
{
public function callMe() { trace("called Class2"); }
public function callMe2() { trace("called Class2 too"); }
}
/*
The Abstract Factory
*/
AbstractFactory.as
import mypackage.InterfaceForAllMyClasses;
public class AbstractFactory
{
public function giveMeObject(classNumber:Number):InterfaceForAllMyClasses
{
switch(classNumber)
{
case 0: return(new Class1()); break;
case 1: return(new Class2()); break;
// for any new class that you add you must add a case entry here
}
}
}
/*
Your Program
*/
import mypackage.InterfaceForAllMyClasses;
import mypackage.AbstractFactory;
MyProgram.as
public class MyProgram
{
var abstractFactory:AbstractFactory = new AbstractFactory();
public function main()
{
var x:InterfaceForAllMyClasses=AbstractFactory.giveMeObject(0);
var y:InterfaceForAllMyClasses=AbstractFactory.giveMeObject(1);
x.callMe();
x.callMe2();
y.callMe();
y.callMe2();
}
}
If you cant import the classes from your main application because they are declared in external modules (swfs), then you can make each module as an Abstract Factory, here is an example:
Interfaces/IModuleInterface.as
package Interfaces
{
public interface IModuleInterface
{
function giveMeObject(classNumber:Number):IObjectInterface;
}
}
Interfaces/IObjectInterface.as
package Interfaces
{
public interface IObjectInterface
{
function callMe():void;
function callMeToo():void;
}
}
Modules/ModuleOne.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Module xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300" implements="Interfaces.IModuleInterface">
<fx:Script>
<![CDATA[
import Interfaces.IObjectInterface;
public function giveMeObject(classNumber:Number):IObjectInterface
{
switch(classNumber)
{
case 1:
trace("ModuleOne: Instantiating 1");
return(new ModuleOneClassOne());
break;
case 2:
trace("ModuleOne: Instantiating 2");
return(new ModuleOneClassTwo());
break;
}
return(null);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Label x="10" y="10" text="Module One Loaded"/>
</s:Module>
Modules/ModuleOneClassOne.as
package Modules
{
import Interfaces.IObjectInterface;
public class ModuleOneClassOne implements IObjectInterface
{
public function ModuleOneClassOne()
{
trace("ModuleOneClassOne: Instantiated");
}
public function callMe():void
{
trace("ModuleOneClassOne: called callMe()");
}
public function callMeToo():void
{
trace("ModuleOneClassOne: called callMeToo()");
}
}
}
Modules/ModuleOneClassTwo.as
package Modules
{
import Interfaces.IObjectInterface;
public class ModuleOneClassTwo implements IObjectInterface
{
public function ModuleOneClassTwo()
{
trace("ModuleOneClassTwo: Instantiated");
}
public function callMe():void
{
trace("ModuleOneClassTwo: called callMe()");
}
public function callMeToo():void
{
trace("ModuleOneClassTwo: called callMeToo()");
}
}
}
AbstractFactoryInModules.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="345" height="200">
<fx:Script>
<![CDATA[
import Interfaces.IModuleInterface;
import Interfaces.IObjectInterface;
import mx.events.ModuleEvent;
protected var module:IModuleInterface;
protected var object:IObjectInterface;
protected function ButtonLoadSwf_clickHandler(event:MouseEvent):void
{
loader.unloadModule();
loader.url=moduleUrl.text;
}
protected function loader_readyHandler(event:ModuleEvent):void
{
this.module = (loader.child) as IModuleInterface;
}
protected function ButtonCreateObject_clickHandler(event:MouseEvent):void
{
this.object = this.module.giveMeObject(Number(TClassNumber.text));
}
protected function BCallMe_clickHandler(event:MouseEvent):void
{
this.object.callMe();
}
protected function BCallMeToo_clickHandler(event:MouseEvent):void
{
this.object.callMeToo();
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Button id="ButtonLoadSwf" x="236" y="10" width="99" label="Load SWF"
click="ButtonLoadSwf_clickHandler(event)"/>
<s:Button id="ButtonCreateObject" x="150" y="108" label="Create Object"
click="ButtonCreateObject_clickHandler(event)"/>
<s:TextInput id="TClassNumber" x="96" y="107" width="46" text="1"/>
<s:ModuleLoader x="10" y="39" width="325" height="60" id="loader" ready="loader_readyHandler(event)">
</s:ModuleLoader>
<s:TextInput id="moduleUrl" x="10" y="10" width="218" text="Modules/ModuleOne.swf"/>
<s:Button id="BCallMe" x="96" y="137" width="150" label="callMe"
click="BCallMe_clickHandler(event)"/>
<s:Button id="BCallMeToo" x="96" y="166" width="150" label="callMeToo"
click="BCallMeToo_clickHandler(event)"/>
</s:WindowedApplication>