Archiv der Kategorie: Programmierung

Flex 3 und Google Maps API

Aufgabenstellung

In eine Flex-Anwendung soll Google Maps integriert werden.

Ansatz

1.) Aufruf der Seite http://code.google.com/intl/de/apis/maps/documentation/flash/
2.) Runterladen der Flash-Bibliothek (SWC-Datei) und einfügen in das LIBS-Verzeichnis der Flex-Anwendung
3.) Anfordern des Schlüssels unter „Wie beginne ich…“ und eintragen in den Code unten.

Lösung

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
	<!&#91;CDATA&#91;
		import com.google.maps.overlays.Marker;
		import com.google.maps.controls.ZoomControl;
		import com.google.maps.geom.Attitude;
		import mx.controls.Alert;
		import com.google.maps.*;
		import com.google.maps.geom.*;
		import com.google.maps.services.GeocodingEvent;
		import com.google.maps.services.ClientGeocoder;
		import com.google.maps.controls.NavigationControl;
		import com.google.maps.controls.MapTypeControl;
		import com.google.maps.MapEvent;
		
		public function onMapReady(event:MapEvent):void
		{
			// Die Bedienelemente der Karte hinzufügen
			map.addControl(new MapTypeControl());
			map.addControl(new NavigationControl());
			this.geocodeLocation('Fulda, Am Rinnweg 15');
		}
		
		public function onMapPreinitialize(event:MapEvent):void
		{
			var myMapOptions:MapOptions = new MapOptions();
			myMapOptions.viewMode = View.VIEWMODE_2D;
			myMapOptions.mapType=MapType.NORMAL_MAP_TYPE;
			//myMapOptions.zoom=0;
			map.setInitOptions(myMapOptions);
		}
		
		private function geocodeLocation(location:String=''):void
		{
			var geocoder:ClientGeocoder = new ClientGeocoder();
			geocoder.addEventListener(GeocodingEvent.GEOCODING_SUCCESS,
			geoCodeSuccess);
			geocoder.addEventListener(GeocodingEvent.GEOCODING_FAILURE, 
			geoCodeFailure);

			if(location!='')
			{
				geocoder.geocode(location);
			}
			else
			{
				geocoder.geocode(search.text);
			}
		}
		
		private function geoCodeSuccess(event:GeocodingEvent):void
		{
			var placemarks:Array = event.response.placemarks;
			map.flyTo(placemarks&#91;0&#93;.point, 18, new Attitude(35,60,35), 
			3);
			var marker:Marker = new Marker(placemarks&#91;0&#93;.point); 
			map.addOverlay(marker); 
		}
		
		private function geoCodeFailure(event:GeocodingEvent):void
		{
			Alert.show('Also wissense, das find ich einfach ned....',
                                       'Ähm...');			
		}		
	&#93;&#93;>
</mx:Script>
	<mx:TitleWindow layout="vertical" width="100%" height="100%" 
              title="Finde versteckte Schätze... 
             (c) die 1st-level-caches Schludi&amp;Mephi ">
		<maps:Map3D xmlns:maps="com.google.maps.*" 
			  id="map" 
			  mapevent_mapready="onMapReady(event)" 
			  mapevent_mappreinitialize="onMapPreinitialize(event)"
			  width="100%" 
			  height="100%"
    		  key="HIER KOMMT DER BEANTRAGTE GOOGLE KEY REIN"/>
    	<mx:HBox horizontalAlign="center" width="100%" height="30">
			<mx:Label x="70" y="524" text="Da will ich hin:" 
                                        width="120" fontWeight="bold"/>
			<mx:Button x="421" y="520" label="Suchen ..." 
                                        click="{geocodeLocation()}"/>
			<mx:TextInput id="search" x="167" y="520" 
                                       width="246"/>
    	</mx:HBox>
	</mx:TitleWindow>
</mx:Application>

Beispiel

http://baugebiete.fulda.de

FluorineFX: Standard Authentifizierung über die Session

Aufgabenstellung

Im Internet sollen sich Benutzer an einer Anmeldemaske einer Flex-Anwendung anmelden. Die Berechtigungen sollen anhand des Benutzernamens festgelegt werden.

Ansatz

1.) FLEX: Erstellen der Anmeldemaske mit User/Passwort
2.) C#: Überprüfung ob die User/Passwort-Kombination in der Datenbank vorhanden ist
3.) C#: Falls JA: Benutzer in die sessionvariable USERNAME schreiben / Falls NEIN: Tue nichts
4.) C#: Überprüfung der Backend-Services: Ist USERNAME leer?
NEIN: Darf der User die Daten aufrufen?? (Dem Benutzer kann eine ROLLE zugewiesen sein, siehe Artikel „FluorineFX: Windows Authentifierung… Single Sign On“, und anhand der Rolle kann ermittelt werden ob sie berechtigt ist die Daten abzurufen).
JA: Keine Daten ausliefern!!!
5.) FLEX: Falls user authentifziert: Maskenaufbau in der Flexanwendung entsprechend Rolle

Lösung

Hier die wichtigsten Auszüge aus meiner Lösung:

FLEX:

...
<mx:Script>
public function getAuthenticationHandler(event:ResultEvent):void
{
   if( (event.result as Boolean) )
   {
     // Schalte ViewStack um (keine Loginmaske mehr)
     loginStack.selectedChild=tarzanScreen;

     // Speichere Username in Modellocator (evtl. auch Rolle holen)
     ModelLocator.getInstance().myUser=txtUsername.text;
   }
   else
   {
      Alert.show('Wrong username or password!');
    }						
}
</mx:Script>
...
<mx:TextInput id="txtUsername" />
<mx:TextInput id="txtPasswort" displayAsPassword="true"/>
...
<mx:RemoteObject id="authenticationService" destination="GenericDestination"  
                                          source="TarzanServices.AuthenticationService" 
                                          showBusyCursor="true" 
                                          fault="faultHandler(event)" >
    <mx:method name="getAuthentication" result="getAuthenticationHandler(event)"/>
</mx:RemoteObject>

C#: Benutzername ermitteln: Falls „none“ einfach nix in Maske/Service machen:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Data.SqlClient;
using System.Web;
using FluorineFx;
using System.Security.Principal;
...
public string getSessionUser()
{
    if (FluorineFx.Context.FluorineContext.Current.Session["username"]==null) 
    return "none";

    return FluorineFx.Context.FluorineContext.Current.Session["username"].ToString();
}

C#: Überprüfe ob User/Kennwort-Kombination in der Datenbank vorhanden ist, falls JA, schreibe USERNAME in Sesseion und liefere TRUE zurück, damit Flex weiß das es von der Login-Maske weg darf.. (REAKTION IN FLEX).

public bool getAuthentication(string username, string password)
{
   SqlConnection conn = new SqlConnection(MyConfigurationManager.msSqlServerString);

   try
   {
      conn.Open();
      SqlCommand comm = new SqlCommand();
      comm.Connection = conn;
      comm.CommandText = "SELECT * FROM [users] WHERE [username]=@username "+
                                     "AND [password]=@passwort";
      comm.Parameters.AddWithValue("username", username);
      comm.Parameters.AddWithValue("passwort", password);

      int i = 0;
      SqlDataReader reader = comm.ExecuteReader();

       while (reader.Read())
       {
              i++;
       }

       if (i > 0)
       {
           // Schreibe USER in die Datenbank
           FluorineFx.Context.FluorineContext.
           Current.Session["username"] = username;
       }

       return i > 0;
   }
   catch (Exception e)
   {
        return false;
   }

   return false;
}

FluorineFX: Single-Sign-On per Windows Authentifizierung

Aufgabenstellung

In einem Windows Netzwerk mit Windows Servern sollen beim Aufruf einer Intranet-Web-Anwendung der angemeldete Benutzer automatisch ermittelt werden, so dass keine Benutzername/Kennwort-Maske zusätzlich erforderlich ist. Anhand des Benutzernamens können Berechtigungen festgemacht werden, die z.B. über ein Benutzerrollen-Konzept zugewiesen werden können.

Ansatz

Es wird die FluorineFX-API verwendet um den Windows User zu ermitteln. Dies kann im Preloader der Flex/Flash-Anwendung geschehen. Die Masken werden nachträglich anhand der Rolle aufgebaut bzw. angepasst.

Definition des Benutzerrollenkonzeptes

Eine Rolle ist eine Aufgabe, die eine Person in einem Unternehmen hat. In der Datenbank würde man auf relationaler Ebene 3 Tabellen benötigen, um ein Benutzerrollen-Konzept zu realisieren. Ein Benutzer hat keine, eine oder mehrere Rollen, wobei eine Rolle keinem, einem oder mehreren Benutzern zugewiesen werden kann. Man benötigt also eine Tabelle USER und eine Tabelle ROLE. Da es sich hierbei um eine M:N-Beziehung handelt, ensteht eine Zusatztabelle USERROLE, die die Foreign Keys der Tabelle USER und ROLE speichert.

Vorraussetzungen

In der Firma wird vorzugsweise der Internet-Explorer verwendet. Die Verwendung von Firefox ist möglich, benötigt aber eine Spezialkonfiguration für Windows-Authentifizierung.

Im IE:

Unter Extras / Internetoptionen / Erweitert / Integrierte Windows Authentifzierung aktivieren

Im Firefox:

  • in der Adressleiste „about:config“ eingeben
  • im Fenster rechte Maustaste, dann Neu -> String
  • Eigenschaftsnamen „network.automatic-ntlm-auth.trusted-uris“ eingeben
  • String-Wert: Name des Portalrechners. Wenn auf das Portal beispielsweise

über http://musterfirma/teamworks zugegriffen wird ist der korrekte Wert „musterfirma“ eingetragen

Im IIS Manager sollte mit der rechten Maustaste auf der Web-Anwendung / Eigenschaften der Haken „Windows Authentication“ gesetzt sein, sonst liefert der IIS nichts zurück.

Lösung

1.) Anlegen der 3 Tabellen USER, USERROLE und ROLE.
2.) Der Windows-User kann in einem ordinären Backendservice mit folgendem Code ermittelt werden:

System.Security.Principal.WindowsPrincipal p =
System.Threading.Thread.CurrentPrincipal as System.Security.Principal.WindowsPrincipal;
string strName = p.Identity.Name;
string userWithDeletedDomainPrefix = strName.Substring(strName.IndexOf("\\") + 1).Trim();

3.) Reaktion in der Flex-Anwendung auf die zugewiesenen Rollen (Maskenaufbau)
4.) Schützen der Backendservices anhand der zugewiesenen Rollen (Auch ohne Flex keine sicherheitskritischen Daten aufrufbar)

ASP.NET und C#: Excel Dateien im Web darstellen

Aufgabenstellung

Eine Excel Datei, die in einer Firma ständig aktualisiert wird, soll auf Smart Phones (Blackberrys, iPhones, Windows Mobiles, HTCs …) abrufbar sein.

Vorraussetzungen

Vorweg solllte der Artikel „WebDAV mit IIS einrichten“ durchgearbeitet werden, um Zugriff zu bekommen.

Ansatz

Die Datei wird ortstransparent auf einem WebDAV-Laufwerk (siehe Artikel WebDAV einrichten) eines Microsoft IIS Server gespeichert, der nach dem Verfahren von Artikel „WebDAV einrichten“ konfiguriert ist.. Der Microsoft JET Treiber verfügt über die Fähigkeiten, Excel Dateien zu laden (nur XLS, nicht XLSX oder XLSM). Im IIS Server wird eine ASP.NET Anwendung angelegt. Das nachfolgende Beispiel ist so eingerichtet, das es versuchen wird aus dem Web Root der Anwendung eine Datei test.xls zu laden.

Lösung

Anlegen eine ASP.NET Seite:

<%@ Page Language="C#" AutoEventWireup="true"  
CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Unbenannte Seite</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView ID="GridView1" runat="server">
        </asp:GridView>
        &nbsp;</div>
        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
    </form>
</body>
</html>

… mit dem folgenden Code:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.OleDb;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {

        string appPath = HttpContext.Current.Request.ApplicationPath;
        string physicalPath = HttpContext.Current.Request.MapPath(appPath).
                                                         Replace("\\","\\\\");

        string ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; 
        Data Source="+physicalPath+"\\test.xls; Extended Properties="+
                                                              \"Excel 8.0;HDR=Yes\";";

        string CommandText = "select * from [Tabelle1$]";

        OleDbConnection myConnection = new OleDbConnection(ConnectionString);
        OleDbCommand myCommand = new OleDbCommand(CommandText, myConnection);

        myConnection.Open();

        GridView1.DataSource = myCommand.ExecuteReader(CommandBehavior.CloseConnection);
        GridView1.DataBind();

        myConnection.Close();

        Label1.Text = physicalPath;
    }
}

Beispiel

Diese Datei kann per WebDAV bearbeitet werden:
http://178.77.69.22/test.xls

und wird on-the-fly in eine HTML-Seite umgewandelt:
http://178.77.69.22/exceltoweb/Default.aspx

Baum von mx:AdvancedDataGrid in die Zwischenablage kopieren

Aufgabe

Der Baum eine mx:AdvancedDataGrid mit mx:SummaryRows soll in die Zwischenablage kopiert werden.

Problem

Der DataProvider des mx:AdvancedDataGrid ist eigentlich flach. Der erzeugte Baum/die Hierarchie soll in die Zwischenablage kopiert werden, so dass er in Excel per Copy&Paste mit Einrückungen und Summen eingefügt werden kann.

Lösung

Eine rekursive Funktion wird erstellt, die über den erzeugten Baum iteriert:

// Für das Einrücken in die Zwischenablage
public var anzahlGroupingFelder:Number=0; // Gesamttiefe der Felder
public var aktuelleTiefe:Number=0;		  // Tiefe der aktuellen Rekursion
public var rekursionsString:String='';
public var farbenFuerLevel:Object = new Object();
private function getCompleteADGRekursion(treeData:ArrayCollection):void
{
	var aktuelleGruppeFelder:String='';
		
	for (var i:Number=0; i<treeData.length; i++)
	{
		if(i==0) aktuelleTiefe++;
		var tabs:String='';
		var behindtabs:String='';
		
		for (var l:Number=0; l<=anzahlGroupingFelder-aktuelleTiefe+3; l++) 
		{
			behindtabs+='<td></td>';						
		}
					
				
		var item:Object=treeData[i];
		if(item.children)
		{
			for (var j:Number=1; j<aktuelleTiefe; j++)
			{
				tabs+='<td></td>';						
			}
						
			var properties:String='<td><i><b>'+item.actuallastyear+
			'</b></i></td><td><i><b>'+item.actualyeartillaugust+
			'</b></i></td><td><i><b>'+item.actualyearlinearprojection+
			'</b></i></td><td><i><b>'+item.nextyear+
			'</b></i></td><td><i><b>'+item.nnextyear+
			'</i></td><td><i><b>'+item.nnnextyear+
			'</b></i></td><td><i><b>'+item.nnnnextyear+
			'</b></i></td>';
						
			rekursionsString+='<tr style="background-color:'+
			farbenFuerLevel&#91;aktuelleTiefe&#93;+';">'+tabs+
			'<td>'+item.GroupLabel+behindtabs+'</td>'+
			properties+'</tr>';
			getCompleteADGRekursion(item.children);
		}
		else
		{
			for (var k:Number=0; k<anzahlGroupingFelder; k++)
			{
				tabs+='<td style="background-color:#EFEFEF"></td>';						
			}
						
			var properties2:String='<td>'+item.productcharacteristic+
			'</td><td>'+item.productcharacteristic2+
			'</td><td>'+item.productcharacteristic3+
			'</td><td>'+item.material+
			'</td><td>'+item.actuallastyear+
			'</td><td>'+item.actualyeartillaugust+
			'</td><td>'+item.actualyearlinearprojection+
			'</td><td>'+item.nextyear+
			'</td><td>'+item.nnextyear+
			'</td><td>'+item.nnnextyear+
			'</td><td>'+item.nnnnextyear+'</td>';												
			
			rekursionsString+='<tr>'+tabs+properties2+'</tr>';
		}
					
		// Beim ersten gehts eins tiefer
					
		// Beim letzten eins zurück
		if(i==treeData.length-1) aktuelleTiefe--;
	}
}

Aufruf der rekursiven Funktion:

private function copyToClipboard(dg:AdvancedDataGrid):void
{
// Farben je Interationstiefe;

farbenFuerLevel[1]=’#00B684′;
farbenFuerLevel[2]=’#31C39C‘;
farbenFuerLevel[3]=’#63D3B5′;
farbenFuerLevel[4]=’#CEF3E7′;
farbenFuerLevel[5]=’#BBBBBB‘;
farbenFuerLevel[6]=’#AAAAAA‘;
farbenFuerLevel[7]=’#999999′;
farbenFuerLevel[8]=’#888888′;

var wasIstImDataProviderDebug:Object=dg.dataProvider;
try
{
anzahlGroupingFelder=Number(wasIstImDataProviderDebug.source.
grouping.fields.length);
}
catch(e:Error)
{
anzahlGroupingFelder=0;
}

if(anzahlGroupingFelder>0)
{
rekursionsString=‘‚;
rekursionsString=‘

‚;
rekursionsString=‘

‚;

for each (var order:ReportingOrderVO in reportingOrder)
{
if(order.position!=0)
{
rekursionsString+=‘

‚;
}
}

rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚,
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
getCompleteADGRekursion(
ArrayCollection(dg.hierarchicalCollectionView.source
.getRoot())
);
rekursionsString+=‘

‚+order.description+
Detail 1 Detail 2 Detail 3 Detail 4 Actual last year‚;
rekursionsString+=‘
Actual year‘;
rekursionsString+=‘ till august
Actual year linear projection Next year NNext year NNNext year NNNNext year

‚;
System.setClipboard(rekursionsString);
}
else
{
var totalExport:String = new String();
var colList:Array = new Array();
for(var i:int = 0; i < dg.columnCount; i++) { colList.push(dg.columns[i].dataField); totalExport += String(dg.columns[i].headerText) .replace('\r',' ') + "\t"; } totalExport += "\r"; for(var yp:int = 0; yp < reporting.length; yp++) { for(var xp:int = 0; xp < colList.length; xp++) { var insertString:String=reporting .getItemAt(yp)[colList[xp]]; if(!insertString) insertString=''; totalExport += insertString .replace("\t","") + "\t"; } totalExport += "\r"; } System.setClipboard(totalExport); } Alert.show('Data has been copied to clipboard. Now you can paste it to '+ 'Microsoft Excel or another application of your choice.'); } [/actionscript3]

Von Microsoft Excel in den Microsoft SQL Server importieren

Dies ist ein VBA Skript um Dateien von einer Excel-Tabelle in den Microsoft SQL Server zu importieren. Bevor das Skript läuft muss im VBA-Editor unter Extras -> Verweise „Microsoft ActiveX Data Objects“ als Bibliothek ausgewählt werden, damit ADODB.* zur Verfügung steht.

Private Sub CommandButton1_Click()
    ' Die Datenbankverbindung
    Dim Cn As New ADODB.Connection
    Dim Rs As New ADODB.Recordset
    Dim letzteZeile As Integer
    Dim i As Integer
    
    ' Die beiden Felder material, prdha
    Dim materialnr As String
    Dim prdha As String
    
    ' Finde die letzte Zeile
    letzteZeile = Cells.Find("*", [A1], , , xlByRows, xlPrevious).Row + 1
    
    ' Provider OLEDB Provider
    Cn.Provider = "SQLOLEDB.1"
    
    ' Connectionstring
    Cn.ConnectionString = "Password=meinpasswort;" & _
    "Persist Security Info=True;" & _
    "User ID=meinuser;" & _
    "Initial Catalog=meinedatenbank;" & _
    "Data Source=SERERNAMEODERIPADRESSE"
    
    ' Verbindung öffnen
    Cn.Open
            
    For i = 1 To letzteZeile
        materialnr = Tabelle1.Cells(i, 1)
        prdha = Tabelle1.Cells(i, 2)
        Cn.Execute ("INSERT INTO prdha(material, prdha) " & _
                         "VALUES('" + materialnr + "','" + prdha + "')")
    Next i
    
    ' Verbindung schließen
    Cn.Close
    
End Sub

Security Event Log auslesen bei Microsoft Windows Server

Das nachfolgende Konsolenprogramm gibt eine Liste des Security Event Logs beim Windows Server aus. Hier sieht man welche Person zu welchem Zeitpunkt eine Windows-Authentifizierung über eine Windows/ASP.NET-Anwendung am IIS Server getan hat.

namespace APITester
{
class Program
{
static void Main(string[] args)
{
EventLog test = new EventLog();
test.Log = „Security“;

for (int i = 0; i < test.Entries.Count; i++) { try { Console.WriteLine(test.Entries[i].TimeGenerated + " " + test.Entries[i].UserName); } catch (Exception e) { } } Console.ReadLine(); } } }[/javascript]

Zugriff von VBA auf Microsoft SQL Server

Zunächst wird (z.B. in Excel) im VBA-Editor im Menü „Verweise“ der Verweis „Microsoft ActiveX data Object“ hinzugefügt, damit man Zugriff auf die ADO-Klassen bekommt.

Private Sub CommandButton1_Click()
    Dim Cn As New ADODB.Connection
    Dim Rs As New ADODB.Recordset
    
    Cn.Provider = "SQLOLEDB.1"
    
    Cn.ConnectionString = "Password=daspasswort;" & _
    "Persist Security Info=True;" & _
    "User ID=deruser;" & _
    "Initial Catalog=datenbankname;" & _
    "Data Source=rechnername"
    
    Cn.Open
    
    Rs.CursorType = adOpenKeyset
    Rs.LockType = adLockPessimistic
    Rs.Open "SELECT TOP 10 * FROM globe2010juni_converted", Cn, adOpenStatic
   
    Do Until Rs.EOF
      Debug.Print Rs.Fields("compcode").Value
      Rs.MoveNext
    Loop
   
    Cn.Close
End Sub

FluorineFX RTMP/Messaging konfigurieren

In einer Microsoft IIS Umgebung greift der Flex-Entwickler auf die kostenlose Middleware FluorineFX zurück. Hier gibt es 4 wichtige Dateien, die für die Konfiguration des Frameworks zuständig sind.

  • data-management-config.xml
  • messaging-config.xml
  • remoting-config.xml
  • services-config.xml

Dem Java-Entwickler sind diese Dateien ebenfalls aus der Java-Bibliothek BlazeDS bekannt.

Bei einem Kompiliervorgang wird die Datei services-config.xml in den Compilersettings angegeben.
[Rechtsklick auf Projekt in Workspace -> Properties -> Flex Compiler -> Additional compiler arguments]

-locale en_US -services services-config.xml -context-root /

Es gibt die Möglichkeit einen direkten Pfad mit einer Netzwerkfreigabe auf die services-config.xml des IIS-Servers anzugeben oder diese Datei lokal zu kopieren und die URL’s

  • RTMP – Real Time Messaging Protocol – was für Messaging benötigt wird und..
  • die normalen AMF Services (Actionscript Messaging Format – dient lediglich der RPC-Kommunikation wie GWT RPC oder SOAP Webservices, hierauf wird in diesem Artikel nicht eingegangen)

anzupassen. Hierbei kann man eine absolute Pfadangabe machen, wenn man den IIS nicht lokal auf dem Entwicklerrechner installiert hat (dies ist das Vorgehen in diesem Artikel). Auf dem Server, auf dem sich redundant (in der deployten FluorineFX-Anwendung im WEB-INF-Verzeichnis) diese Dateien befinden, werden allerdings entgegen den nachfolgenden Beispielen anstelle der absoluten Pfade die Platzhalter rtmp://{server.name}:2037 und http://{server.name}:{server.port}/{context.root}/Gateway.aspx verwendet.

services-config.xml:

<?xml version="1.0" encoding="utf-8" ?> 
<services-config>
    <services>
      <service-include file-path="remoting-config.xml" />
      <service-include file-path="messaging-config.xml" />
    </services>
       
    <channels>
        <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
	        <endpoint uri="http://rechner3j.asag.asap.com:4321/Gateway.aspx" 
                 class="flex.messaging.endpoints.AMFEndpoint"/> 
        </channel-definition>

        <channel-definition id="my-rtmp" class="mx.messaging.channels.RTMPChannel">
            <endpoint uri="rtmp://rechner3j.asag.asap.com:2037" 
             class="flex.messaging.endpoints.RTMPEndpoint"/>
            <properties>
                <idle-timeout-minutes>0</idle-timeout-minutes>
            </properties>
        </channel-definition>

    </channels>
</services-config>

remoting-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service" class="flex.messaging.services.RemotingService" 
 messageTypes="flex.messaging.messages.RemotingMessage">
    <adapters>
        <adapter-definition id="dotnet" class="FluorineFx.Remoting.RemotingAdapter" 
	default="true"/>
    </adapters>

    <0default-channels>
        <channel ref="my-amf"/>
    </default-channels>

  <destination id="GenericDestination">
    <properties>
      <source>*</source>
    </properties>
  </destination>  

  <destination id="fluorine">
    <properties>
       <source>*</source>
    </properties>      
  </destination>
</service>

messaging-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<service id="message-service" class="flex.messaging.services.MessageService" 
 messageTypes="flex.messaging.messages.AsyncMessage">
     <!-- DO NOT CHANGE <adapters> SECTION-->
    <adapters>
	<adapter-definition id="messagingAdapter" 
        class="FluorineFx.Messaging.Services.Messaging.MessagingAdapter" 
        default="true"/>
    </adapters>

    <destination id="chat">
		<adapter ref="messagingAdapter"/>
    	<properties>
	        <network>
            	<session-timeout>0</session-timeout>
        	</network>
    	</properties>
    	<channels>
         	<channel ref="my-rtmp"/>
    	</channels>
    </destination>
</service>

FluorineFX geht folgendermaßen vor:
Sobald die Server-Seite (also nicht die Dateien mit den absoluten Pfaden wie obenstehend) mit der Definition für einen RTMP-Channel versehen wurde, wird ein Treffer auf einer beliebigen ASPX-Seite innerhalb der FluorineFX-Webanwendung dazu führen, das ein Socket auf dem definierten Port gestartet wird (oben wäre dass dann Port 2037 für RTMP). Ob es funktioniert hat, lässt sich mit einem telnet-Login prüfen. Hierzu einfach mal in der Eingabeaufforderung (CMD)

telnet hostname 2037

eingeben. Sollte der RTMP-Server nicht laufen, bitte mal auf dem Server per Remotekonsole einloggen und in der Eingabeaufforderung mit

telnet 127.0.0.1 2037

den Login versuchen.
Der Login funktioniert, wenn der Bildschirm gelöscht wird und ein Cursor in der Ecke links oben steht. Dieser Modus kann meistens mit STRG+C abgebrochen werden, sollte dies nicht funktionieren kann man auch die Eingabeaufforderung schließen.

Wichtig!!! Damit sie die Flex-Anwendung überhaupt mit der Messaging-Funktionalität und den obengenannten Konfigurationsdateien kompilieren können benötigen sie 2 Dateien, die sie in der Installation der Adobe Livecycle Services (LSDS) finden.

  • fds_rb.swc
  • fds.swc

Sie müssen einen Account bei Adobe registrieren und die LCDS runterladen, anschließend kopieren sie die o.g. Dateien in das LIB-Verzeichnis ihrer Flex-Anwendung und es kompiliert!!! Glückwunsch. So nun ein kleines Beispiel für einen gut funktionierenden Chat:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application creationComplete="{creationComplete()}" 
    xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:Script>
	<![CDATA[
	    import mx.controls.Alert;
	    import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;
    	    import mx.messaging.*;
    	    import mx.messaging.messages.*;
    	    import mx.messaging.events.*;
    	    import mx.core.Application; 
    		
    	    public function creationComplete():void
    	    {
    		consumer.subscribe();
    	    }
   
            private function messageHandler(event:MessageEvent):void
            {
    		txtLog.text += event.message.body.toString()+'\r';
    	    }
   
            private function messagefaultHandler(event:MessageFaultEvent):void
    	    { 
        	Alert.show(event.faultString, "Error"); 
    	    }
   
            public function sendMessage():void
	    {
        	var message:AsyncMessage = new AsyncMessage();
       		message.body = txtOutput.text;
       		producer.send(message);
       		txtOutput.text="";
    	    }
	]]>
       </mx:Script>
       <mx:Consumer id="consumer" destination="chat" 
	   message="messageHandler(event)" fault="messagefaultHandler(event)"/>
       <mx:Producer id="producer" destination="chat" fault="messagefaultHandler(event)"/>
       <mx:VBox width="100%" height="100%">
    	
       <mx:HBox width="100%">
         <mx:TextInput width="100%" id="txtOutput"/>
         <mx:Button label="Skicka" click="sendMessage()"/>
       </mx:HBox>
    	<mx:TextArea height="100%" width="100%" editable="true" id="txtLog"/>
    </mx:VBox>
</mx:Application>