Tag Archives: Weborb

firefox_icon

White screen when loading Flash in FireFox – GZIP problem

Last week at work we noticed a strange bug. When loading our Flex application in FireFox, we got a white screen. The swf was loaded in the browser, but no content was shown, just an empty white screen. Very annoying. Debugging wasn’t possible because no connection could be made between the swf file and Eclipse. In all other browsers we had no problem with the application and all content was shown.

Now, after searching for some hours, we found that the problem had to do with IIS and FluorineFx ecnrypting the swf with GZIP. When we tunred GZIP off for swf files, things worked again in FireFox. Our application can be configured to work with WebORB or with FluorineFx and with WebORB we didn’t have this problem (the app worked fine in FireFox with WebORB). Computer problems always have a logical explanation, one of the reasons why I like IT so much. IT and my wife balance out great!

What I don’t get so far is if this is a problem with IIS 7(.5) not ecrypting properly the swf for FireFox or if it’s a problem with FireFox not decrypting the file properly.

I think it has to do with FireFox because in Charles I do not see differences in the Requests and Responses from FireFox and Chrome.

Continue reading

Flash game Eggie (WebORB, PHP and MySql)

Together with a colleague Piet Santy (aka Ploem), who did the artwork, we created a Flash Game for a friend of ours. Elections are up in Belguim and Dominique Dehaene needs votes! One way to get some attention and publicity is off course the internet. It was great to work with CS5, but I did have some problems with code completion and Flash Builder. Also, in Flash Builder, lots of classes weren’t found. I probably haven’t searched enough to find out the solutions of these problems.
I really like creating once in a while such a small game. No design patterns are involved, no architecture, just programming and quickly seeing result! With WebORB for PHP together with MySql I created a TOP 10 highscores. When the game finishes, your score is stored in the database and top 10 is fetched and displayed. Setting up WebORB for PHP was very easy. I just had some problems with a function that I called Echo, which is a reserved word in PHP and off course not knowing the language PHP. But everything was created very fast.

The result can be found here.

Configure WebORB with Log4Net

This will be a quick one because time is not on my side these days… If you’ve never worked with Log4Net, I suggest to go and read some documentation on this an try to set it up.

A while ago I was searching how I could merge the logging from WebORB with my Log4Net configuration. As it happens, I had two log files, one from WebORB and one from Log4Net. Having an overview of the logging, was difficult like this. After some initial help from Mark Piller (WebORB), I managed to merge the two loggings. However, in the beginning, some logs were still written to the WebORB log file. After some weeks I got another email from Mark explaining how it could be done so that all logging would go to the Log4Net logging.

So here’s how you do it:

In the Weborb.config file, add next lines in the logging tag section:

1
2
3
4
5
6
<!--<currentPolicy>Date Based Logging</currentPolicy>-->
Log4NetPolicy
 
&nbsp;
 
Log4NetPolicy Edu3.Weborb.Logging.Log4NetPolicy

The Date Based Logging is the default policy used by WebORB which I commented out. We’ll implement a Log4NetPolicy (you can take another name if you wish so).

In the loggingPolicy tag you can map the policy to a className which we’ll implement. This class needs to implement the interface Weborb.Util.Logging.ILoggingPolicy, which is an interface in the weborb dll.

Important is to set all the log enables to true. It’s Log4Net that will handle levels. If you set, for instance, the WebORB INFO level to enable=false, then our custom Log4Net logger will not receive any INFO loggings from WebORB. Even if you then set INFO level to be logged in Log4Net, the WebORB INFO levels won’t be logged.

1
2
3
4
5
6
7
8
9
WEBORB INFO
WEBORB DEBUG
WEBORB ERROR
WEBORB SERIALIZATION
WEBORB EXCEPTION
WEBORB INSTRUMENTATION
WEBORB SECURITY
WEBORB MESSAGE SERVER
WEBORB THREADING

So here’s my Edu3.Weborb.Logging.Log4NetPolicy class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using Weborb.Util.Logging;
using System.Collections;
 
namespace Edu3.Weborb.Logging
{
class Log4NetPolicy : ILoggingPolicy
{
private readonly Hashtable _hashtable;
private const string PolicyName = "Log4NetPolicy";
 
public Log4NetPolicy(Hashtable hashTable)
{
_hashtable = hashTable;
}
 
public ILogger getLogger()
{
return new Log4NetLogger();
}
 
public string getPolicyName()
{
return PolicyName;
}
 
public Hashtable getPolicyParameters()
{
return _hashtable;
}
}
}

getLogger() should return the logger object which will be used by WebORB
getPolicyName() should return the name of the policy. Make sure it is the same as you specified in the config file
getPolicyParameters() should return the same hashtable which was passed into your policy constructor

The Log4NetLogger is also a custom written class that looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using System;
using log4net;
using Weborb.Util.Logging;
using log4net.Core;
 
namespace Edu3.Weborb.Logging
{
public class Log4NetLogger : AbstractLogger
{
private static readonly ILog Log = LogManager.GetLogger("WebORB");
 
public override void fireEvent(string category, object eventObject
, DateTime timestamp)
{
Level activityLevel;
switch (category)
{
case LoggingConstants.WEBORB_INFO:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_INFO"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
case LoggingConstants.WEBORB_DEBUG:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_DEBUG"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
case LoggingConstants.WEBORB_ERROR:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_ERROR"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
case LoggingConstants.WEBORB_SERIALIZATION:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_SERIALIZATION"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
case LoggingConstants.WEBORB_EXCEPTION:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_EXCEPTION"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
case LoggingConstants.WEBORB_INSTRUMENTATION:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_INSTRUMENTATION"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
case LoggingConstants.WEBORB_SECURITY:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_SECURITY"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
case LoggingConstants.WEBORB_MESSAGE_SERVER:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_MESSAGE_SERVER"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
case LoggingConstants.WEBORB_THREADING:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_THREADING"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
case LoggingConstants.WEBORB_MESSAGINGEXCEPTION:
activityLevel =
Log.Logger.Repository.LevelMap["WEBORB_MESSAGINGEXCEPTION"];
Log.Logger.Log(GetType(), activityLevel
, eventObject, null);
break;
default:
throw new Exception("All LoggingConstants should " +
"have been catched previously.");
}
}
}
}

As you’ll see, I defined some custom log levels in Log4Net. You really don’t need this, you could just use the existing Log4Net levels and map custom WebORB levels to one of the existing Log4Net levels.

Here’s how my Log4Net configuration section looks like if you want to work with these custom levels.

1
 

In this section you can configure not to log all levels. For more information on this see the Log4Net documentation.

Maybe that some of the Log Levels values (the integers) should be changed to meet your expectations. See Log4Net Levels for more information.

That’s about it. If I forgot something, I’ll update the post. If you have any question, please feel free to ask.

Peformance issues IIS7, WebORB, Network, …

Two weeks ago I had a lot of problems in a project of mine. Often some clients couldn’t connect to weborb.aspx (actually 0.01% of the times). After a lot of searching we weren’t really able to find the problem. It seemed to me that maybe it was a network issue. The customer had just changed a lot of servers and improved their network. What previously worked didn’t seem to work anymore now.

We changed some things after which everything started to work smoothly again:

- When an error is thrown from the server or there’s a network problem we invoke the server call again with Credentials. We use authentication, so if at a certain moment in time IIS decides to recycle, the next calls to the server will fail. So we implemented a fallback system. If a fault occurs we authenticate again and try the call a second time. If the second time would also fail, then we give the user an Alert with some information what to do.

- When profiling SQLServer2008 we saw that there were millions of logs being written to the database by WebORB. We had configured WebORB logging with Log4Net and set the Filter in Log4Net on INFO. Basically everything that was logged was written to the database. This meant a lot of overhead. After having put the Filter on Warning connections to database reduced by a factor 100.

Configuring WebORB Logging with Log4Net (in global.asax):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
     void Application_Start(object sender, EventArgs e) 
    {
        log4net.Config.XmlConfigurator.Configure();
        log4net.ILog log = log4net.LogManager.GetLogger("Global.asax");
        log.Info("Application_Start(" + sender.ToString() + ", " + e.ToString() + ")");
 
        //Configuring WebORB Logging with Log4Net
	try
	{
		Weborb.Util.Logging.Log.addLogger("log4NetLogger", new Edu3.Util.Logger());	
                Weborb.Util.Logging.Log.removeLogger("default");
	}
	catch (Exception exception)
	{
		log.Error("Application_Start", exception);
	}
    }

- We profiled IIS7 with some tools that were proposed on StackOverflow. These tools were very interesting, but still I thought to get much more information. Strange that there’s not a professional profiling tool for IIS7. We used Administration Pack for IIS 7.0, the stackoverflow question is at link. The strange thing was that in 0.01% of the requests a 404 was returned. Even after having enabled Failed Request Tracing in IIS7, I couldn’t find the reason for the 404.

- Recycling happens automatically @ 23 hours. (this was allready done earlier when I had problems in November).

After these tweaks everything is working fine again. Those 404′s remain a mystery…

Ciao, Lieven Cardoen

Stress Testing with WebORB

Yesterday and today I worked on a performance issue where 350 candidates had to wait more than a minute before their data was loaded. The 350 candidates start their exam simultaenously so the server is heavily loaded at that time. I had a couple of ideas in mind like caching certain things but how was I going to test this. I haven’t got 350 computers at my desk, I just have one laptop (and a development server).

A first tool I got from an answer on StackOverflow. AB is a tool for benchmarking apache server, but it can be used to benchmark any url(in my case local IIS). To do this I created an aspx page that loaded the same data that was loaded by one candidate. I then used AB to call that page multiple times. With this tool I already got a good idea how good my caching was working but I didn’t know if WebORB was also causing performance problems.

Since the last update we put on the development server of the customer, candidates had to wait much longer to get started. My guess was that it had to do with WebORB authentication. I now had to find a way to test my services by using the WebORB gateway. Asynchronous Unit Testing with FlexUnit had to be one of the answers. So I got started and two hours later I had my test. I did however had to try three different things.

In the first try I created a Test and added it a hundred times to the TestSuite. This wasn’t so usefull because the next Test starts only if the previous had ended. I needed simulteanously remote calls, parallel, not serial.

In the second try I created a Test that does a remote call a hundred times and is added only once to the TestSuite. This second try looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package {
	import flexunit.framework.Assert;
	import flexunit.framework.TestCase;
 
	import mx.rpc.AsyncToken;
	import mx.rpc.Fault;
	import mx.rpc.IResponder;
	import mx.rpc.Responder;
	import mx.rpc.remoting.mxml.RemoteObject;
 
	public class LoadExamSessionTest extends TestCase  {
 
		private var remoteObject:RemoteObject;
 
		override public function setUp():void {
			remoteObject = new RemoteObject("GenericDestination");
			remoteObject.source = "Edu3.ApplicationTestServices.LoadExamSessionBenchMarkTestService";
			remoteObject.endpoint = "http://localhost/edumatic3/local/weborb.aspx";
			// This is needed because the weborb authentication is enabled on the server side.
			remoteObject.setCredentials("credentials", "credentials");
 
		}
 
		public function testLoadExamSessionTest():void {
			for(var i:int ; i < 200 ; i++){			
				loadExamSession();
			}
		}
 
		public function loadExamSession():void{
			var token:AsyncToken = remoteObject.LoadTest();
			var responder:IResponder = new Responder
			(
				addAsync(onLoadExamSessionResult, 1000000), 
				null
			);
			token.addResponder(responder);
		}
 
		public function onLoadExamSessionResult(data:Object):void {
			assertNotNull(data.result);
		}
	}
}

This worked like a charm, but Authentication on the server side is only done once with the frist call because I’m using the same Remote Object every time. You should also check Charles to see what happens because it’s very interesting. Apparently the there are only two calls to weborb.aspx. The first call is the one that will trigger my custom authentication handler, the next call has packages 199 calls to the service… I won’t put a screenshot here because testing it is so much cooler. You can actually see in Charles that the server has started 199 threads and you can follow how much of the amf package is downloaded.

But this wasn’t what I needed because all 350 candidates are authenticated seperately. To simulate this I just create a new remote object for each call. Class now looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package {
	import flexunit.framework.Assert;
	import flexunit.framework.TestCase;
 
	import mx.rpc.AsyncToken;
	import mx.rpc.Fault;
	import mx.rpc.IResponder;
	import mx.rpc.Responder;
	import mx.rpc.remoting.mxml.RemoteObject;
 
	public class LoadExamSessionTest extends TestCase  {
 
		private var remoteObject:RemoteObject;
 
		override public function setUp():void {
		}
 
		public function testLoadExamSessionTest():void {
			for(var i:int ; i < 200 ; i++)
			{			
				loadExamSession();
			}
		}
 
		public function loadExamSession():void{
			remoteObject = new RemoteObject("GenericDestination");
			remoteObject.source = "Edu3.ApplicationTestServices.LoadExamSessionBenchMarkTestService";
			remoteObject.endpoint = "http://localhost/edumatic3/local/weborb.aspx";
			// This is needed because the weborb authentication is still enabled on the server side.
			remoteObject.setCredentials("credentials", "credentials");
 
			var token:AsyncToken = remoteObject.LoadTest();
			var responder:IResponder = new Responder
			(
				addAsync(onLoadExamSessionResult, 1000000), 
				null
			);
			token.addResponder(responder);
		}
 
		public function onLoadExamSessionResult(data:Object):void {
			assertNotNull(data.result);
		}
	}
}

Now you’ll see 200 calls to weborb.aspx in Charles and my custom WebORB authentication handler will be called 200 times. This is somewhat the customers case I wanted to simulate.

What did I find out, well, for 200 remote calls, the third scenario (200 times weborb authentication) takes double as long as the second scenario (once weborb authentication).

The Caching on the server times now speeds up the loading of the data five times!

ps: I do need to dig in a little deeper in the addAsync from FlexUnit because when I add another addAsync for my fault event in the Reponder, things get messy… Will keep you posted on this.

Ciao! Lieven Cardoen aka Johlero

WebORB Authentication Issue

Imagine you have a Flex Applicatin using WebORB Authentication. When the first Remote Call is done you supply the Remote Object with credentials. The server authenticates and from then on you don’t need to supply credentials anymore. The server side session will remembler who you are.

But if for some reason your server side session is ended (to to session timeout, applicatin pool recycling, …) the next Remote Call will get a Weborb Security Error.

We have solved this by catching this error in the OnFault event from the responder and retrying the remote call again with the credentials. However, this wasn’t as easy as we thought. Apparently the client side code remebers that it’s been authenticated and will not re-authenticate automatically (because the client doens’t know the session has ended). First we tried to call logout() on the remote object but that didn’t work (we got an error saying that logging out and setting the credentials couldn’t be done simultaenously). We then tried disconnect() and this worked like a charm. After the disconnect() on the remote object we resetted the credentials. If we then called a server side function, WebORB would re-authenticate again. Without the disconnect() WebORB would’nt re-authenticate again (apparently the remote object holds some flags which tell WebORB to authenticate or not). We do need to go and find out the difference between the logout() and the disconnect().

I will definetily come back to this because it isn’t a hundred procent clear to me.

Ciao, Lieven Cardoen aka Johlero

IIS Recycling

Last week I got a call from a big customer saying that the 100 of the 200 candidates had just received an error log window in our Flex Application. I knew that this could happen someday, but what had caused this error log…?

After a lot of searching in the logs of the two servers at the customer, I found out that at a certain time on one of the two servers all sessions and application pool were ended. Why? Well, after again a lot of searching I found out that IIS recycles an application pool every 29 hours… Man, why is this default enabled???

Apparently a lot of these recycle events are nog logged by default. Only the 29 hour recycle event apparently is logged by default. As for now I haven’t found this log yet, so I will have to enable all those logs to find the actual reason why the application pool recycled…

That’s done by this command on IIS6:

cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs Set w3svc/AppPools/LogEventOnRecycle 255

On IIS7 you can enable them in the Advanced Settings of the application pool.

The problem with our Flex Application was that it uses WebORB Authentication. This authentication is done by the first remote call after which the server side session remembers the credentials. After the recycle the sessions were gone and 100 candidates received a WebORB Security error… We have already solved this problem by re-authenticating when this happens but I’m also really hoping to find out why the application pool recycled in the first place… Any suggestions are welcome… (memory?, too mucht connections?, database problem?, …).

Ciao, Lieven Cardoen aka Johlero

WebORB Security Flex .NET Server Side PartI

I will not do a big introduction on WebORB security because I mainly want to show the security with some examples. It would however not be bad to read the following two things at http://www.themidnightcoders.com/doc30/ :

  • Flex Integration –> Flex Remoting –> Security
  • Server Infrastructure –> Security

Beside that read the comments in the Security section in the weborb.config file.

In my example I’ll work with the closed system.

From WebORB:

WebORB default authorization handler implements a powerful system for granting or denying access to the user classes, XML Web Services, CFCs or any other supported service type. The system supports 2 modes of operation: Open and Closed mode. The ‘Open System’ mode exposes all application classes and services to the rich client applications. Access can be restricted to individual methods, classes or packages/namespaces by creating specific access constraints. This mode is useful for development when infrastructure security has lower priority. The ‘Closed System’ mode is the opposite of the open mode. When running in the ‘Closed System’ mode, WebORB restricts access to all service types available in the application – all classes, xml services, EJBs and CFCs. Access to the individual methods, classes, packages/namespace can be granted by applying security access constraints.

The closed system is more secure and easier to manage, so I’ll work with that.

On the servers side I have one Class that needs security:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Authentication
{
    public class TestService
    {
        /**
         * No access constraints should be defined for this method. In a closed system,
         * this means that no one should have access to this method.
         */ 
        public void Ping() { }
 
        /**
         * Access should be granted to all IP adresses.
         */ 
        public void AllIpPing() { }
 
        /**
         * Access should be granted only to Admin roles.
         */
        public void AdminPing() { }
 
        /**
         * Access should be granted only to User roles.
         */
        public void UserPing() { }
 
        /**
         * Access should be granted to users having both
         * Admin and User role.
         */ 
        public void AdminAndUserPing() { }
 
        /**
         * Access should be granted to users having 
         * Admin or User role.
         */ 
        public void AdminOrUserPing() { }
    }
}

Default WebORB Authentication/Authorization

First we’ll check the default Authentication/Authorization from WebORB. In the weborb.config check the section on Security:

1
2
3
4
5
6
7
8
9
<security>
      <deploymentMode>closed</deploymentMode>
      <authenticationHandler>Weborb.Security.WebORBAuthenticationHandler</authenticationHandler>
      <!--authenticationHandler>Johlero.Security.Handler.AuthenticationHandler</authenticationHandler-->
      <authorizationHandler>Weborb.Security.WebORBAuthorizationHandler</authorizationHandler>
      <!--authorizationHandler>Johlero.Security.Handler.AuthorizationHandler</authorizationHandlers-->
      <rolesProvider>Weborb.Security.WebORBRolesProvider</rolesProvider>
      <!--rolesProvider>Johlero.Security.Provider.RolesProvider</rolesProvider-->
</security>

Ok, closed system is set. Now we need to define the users. This is done at the end of the Security section in the weborb.config and should be set like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<acl>
      <user>
        <name>admin</name>
        <password>adminpass</password>
        <role>admin</role>
      </user>
      <user>
        <name>user</name>
        <password>userpass</password>
        <role>user</role>
      </user>
      <user>
        <name>adminuser</name>
        <password>adminuserpass</password>
        <role>user</role>
        <role>admin</role>
      </user>
</acl>

We have defined three users each with their password and the roles they are given. The adminuser is given two roles, user and admin. This will become clear later on in the Flex Part.

Now we need to set the constraints. At this point, no function in the TestService Class is accessible because of the Closed system.

First we’ll create some access constraints. You’ll see that some constraints are allready defined. We’ll use also one of those predefined constraints:

1
2
3
4
5
6
7
8
9
10
11
12
13
<access-constraint action="grant">
          <name>Constraint.Grant.UserRole</name>
          <role>user</role>
        </access-constraint>
        <access-constraint action="grant">
          <name>Constraint.Grant.AdminRole</name>
          <role>admin</role>
        </access-constraint>
        <access-constraint action="grant">
          <name>Constraint.Grant.AdminAndUserRole</name>
          <role>admin</role>
          <role>user</role>
</access-constraint>

First constraint grants access to the User role. The name is just a name that will be used further on in the secure-resource tags.
Second constraint grants access to the Admin role.
Third constraint grants access to users having both Admin AND User role. Be aware, this access-contraint does not say: give access to users having the Admin OR User role.
Besides giving access to a certain role it’s also possible to give access to IP adresses, IP ranges and hostnames. (See comments in weborb.config).

We will also use a IP access contraint:

1
2
3
4
<access-constraint action="grant">
          <name>everyone</name>
          <IP>*.*.*.*</IP>
</access-constraint>

Ok, so these access-constraints are like predefined security configurations that can now be used to secure resources. In the secure-resources tag in weborb.config add these lines:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<secure-resource>
          <resource>Authentication.TestService.UserPing</resource>
          <constraint-name>Constraint.Grant.User</constraint-name>
        </secure-resource>
        <secure-resource>
          <resource>Authentication.TestService.AdminPing</resource>
          <constraint-name>Constraint.Grant.Admin</constraint-name>
        </secure-resource>
        <secure-resource>
          <resource>Authentication.TestService.AdminAndUserPing</resource>
          <constraint-name>Constraint.Grant.AdminAndUser</constraint-name>
        </secure-resource>
        <secure-resource>
          <resource>Authentication.TestService.AdminOrUserPing</resource>
          <constraint-name>Constraint.Grant.Admin</constraint-name>
        </secure-resource>
        <secure-resource>
          <resource>Authentication.TestService.AdminOrUserPing</resource>
          <constraint-name>Constraint.Grant.User</constraint-name>
        </secure-resource>
        <secure-resource> 
          <resource>Authentication.TestService.Ping</resource>
          <constraint-name>everyone</constraint-name>
</secure-resource>

First secure-resource secures the Authentication.TestService.UserPing function with the Constraint.Grant.User constraint. This means that access to the UserPing function is granted to users that have the user role. So when along the Flex side user credentials are set (see ACL), access will be granted. Because of the closed system, ONLY user roles will have access.

Second secure-resource is analog to previous secure-resource, but now for Admin.

Third secure-resource secures the AdminAndUserPing with the AdminAndUser constraint. Here only access will be given to users having both user and admin role!

To give access to a Function for user having user OR admin role, you need to define two secure resources (fourth and fifth secure-resource).

The last secure-resource secures the Ping function with the everyone constraint. So everyone will have access to this resource.

In next post will show you how you can configure certain things with the Weborb Console (weborbconsole.html).

Flex integration with Microsoft Message Queuing (MSMQ) using WebORB – Part II – Server Side

Introduction

For the Server Side I have made a new example in Flex based on the previous example in Part I article. Let me sketch the problem in our project again. On the client side a user can edit things and save them. If two clients were to edit the same thing, then the user that saves last will win. The other user will have saved his work, but will loose it because the other user overwrites it.

Ok, assume the Client loads in a tree. Leafs can be Apples, Potatoes or Bananas. If the Client A clicks on an Apple Leaf, then a message is sent saying that an Apple with Id X is locked by Client A. Other Clients get this message and lock the Apple leaf. If Client A navigates to another leaf, then the previous leaf is released (so a message is sent saying this Apple with Id X is no more locked) and a new leaf is locked (again a message).

Requirements: Please check  Part I.

Problems:

  1. A Client connects and leafs are allready locked. This means that the server needs to have a registry of locked Apples, locked Potatoes and locked Bananas. If a Client connects the server notifies the Client of already locked things.
  2. Client A and Client B click simultaneously on the same Apple. Two messages are sent locking the same Apple. In this case the server will have to let one message through and cancel to other message. If the message from Client B has been cancelled, the server will have to notify this Client B that locking the Apple did not work.
  3. When stress testing such cases, lot’s of things can go wrong due to asynchronous processes. When a Producer sends a message locking an Apple with IdX, then it has to wait for his Consumer getting the Message back saying the Apple with IdX is locked. But image clicking very fast… This means that by the time the Consumer gets the affirmation that the Apple with IdX has successfully been locked, the user may already have clicked another Apple with IdY (and a message locking this IdY has also been sent…). I will not go to deep into these problems for now, maybe I’ll talk about it in a part III or IV…

 For this part II I have created a second flex example. This example is a bit more complex than the one in part I.

 The server side will be a bit more work to explain. This is the source code. You’ll see there’s a Site, a DotNet and a Binaries folder. First create a site in IIS and link it to the Site folder (if you use johleromsmq as the name of your site, then you won’t have to change a lot, but you’re free to name it as you want). Then in the DotNet folder open the solution with Visual Studio 2008. Normally it won’t find my site, so you’ll have to remove johleromsmq site and then add your site to the solution. Then check the references.  The Messaging Class library should have a reference to the System.Messaging and weborb. The site should have a reference to the Messaging Class library and weborb.dll.

I made the target Framework 2.0 but 3.5 will also work.

If IIS is properly set then you should be able to build the solution. You also should be able to surf to weborbconsole.html and to diagnostics.aspx (don’t worry about the WebORB Permissons Summary being all red –> If you want WebORB to be able to write logs, then you should give the Network Service write permissions on your log folder).

 

Solution VS2008

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


  

 

Flow

Ok, let’s go.

First take a look at the constants

1
2
3
4
5
6
private const ENDPOINT:String = "http://localhost/johleromsmq/weborb.aspx";
private const URI:String = "rtmp://localhost:2037/MessagingService";
private const REMOTEDESTINATION:String = "GenericDestination";
private const MESSAGINGDESTINATION:String = "JohleroMsmqChannel"
private const NETCONNECTIONSUCCESS:String = "NetConnection.Connect.Success";
private const MANAGEMENTSERVICE:String = "Weborb.Management.ManagementService";

The ENDPOINT should point to your site. You will not find weborb.aspx as it is configured as a http handler in the web.config. Also be aware of the RTMPTHttpHandlers! Don’t forget to add them to your own project!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0"?>
<configuration>
	<system.web>
    <customErrors mode="Off" />
		<compilation debug="true">
		</compilation>
		<authentication mode="Windows"/>
		<httpHandlers>
			<add verb="*" path="weborb.aspx" type="Weborb.ORBHttpHandler"/>
			<add verb="*" path="codegen.aspx" type="Weborb.Management.CodeGen.CodegeneratorHttpHandler"/>
			<add verb="*" path="/open/*" type="Weborb.Messaging.Net.RTMPT.RTMPTHttpHandler"/>
			<add verb="*" path="/send/*" type="Weborb.Messaging.Net.RTMPT.RTMPTHttpHandler"/>
			<add verb="*" path="/idle/*" type="Weborb.Messaging.Net.RTMPT.RTMPTHttpHandler"/>
			<add verb="*" path="/close/*" type="Weborb.Messaging.Net.RTMPT.RTMPTHttpHandler"/>
			<add verb="*" path="/open/*/*" type="Weborb.Messaging.Net.RTMPT.RTMPTHttpHandler"/>
			<add verb="*" path="/send/*/*" type="Weborb.Messaging.Net.RTMPT.RTMPTHttpHandler"/>
			<add verb="*" path="/idle/*/*" type="Weborb.Messaging.Net.RTMPT.RTMPTHttpHandler"/>
			<add verb="*" path="/close/*/*" type="Weborb.Messaging.Net.RTMPT.RTMPTHttpHandler"/>
		</httpHandlers>
		<caching>
			<outputCache enableOutputCache="true"/>
		</caching>
	</system.web>
</configuration>

The URI should stay the same, as it is used by the Channel to connect to the MessagingService started on port 2037. The /MessagingService points to a folder in the Applications folder under the root of your site where you can find an app.config. Here a custom ApplicationHandler can be entered to track connections, disconnections, joins, leaves, …

The REMOTEDESTINATION points to a destination in the WEB-INF/flex/remoting-config.xml file on the root of your site. There you’ll find the GenericDestination and it can be used to configurate access to certain services, … Explaining this will however be for a next post. This destination will be used to by the RemoteObject.

The MESSAGINGDESTINATION points to a destination in the WEB-INF/flex/messaging-config.xml file on the root of your site. There you’ll find a destination with id JohleroMsmqChannel. The weborb-rtmp channel set in the channels attribute refers to a channel-definition n the WEB-INF/flex/weborb-services-config.xml file on the root of your site. For the rest if you read the comments in the file, everything should be clear. The
.\private$\weborb-[destinationName] in our case will be translated to weborb-JohleroMsmqChannel-subqueue. The subqueue will be configured later on in the Consumer. The Messaging.Selectors.CustomSelector will be explained later.

Now look at the onCreationComplete() handler in the Flex Code.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public function onCreationComplete(event:FlexEvent) : void
{
    this.addLogMessage("onCreationComplete");
 
    registerClassAlias("Messaging.Messages.LockedAppleMessage", LockedAppleMessage);
    registerClassAlias("Messaging.Messages.LockedPotatoeMessage", LockedPotatoeMessage);
    registerClassAlias("Messaging.Messages.LockedBananaMessage", LockedBananaMessage);
 
    this.createChannelSet();
}
 
private function createChannelSet() : void
{
    this.addLogMessage("createChannelSet()");
 
    cs = new ChannelSet();
    channel = new WeborbMessagingChannel("WebOrbMessagingChannel", URI);
    channel.addEventListener(MessageEvent.MESSAGE, onChannelMessage);
    channel.addEventListener(ChannelEvent.CONNECT, onChannelConnect);
    channel.addEventListener(ChannelEvent.DISCONNECT, onChannelDisconnect);
    channel.addEventListener(ChannelFaultEvent.FAULT, onChannelFault);
    cs.addChannel(channel);
}

Every function that is called writes a log to a TextArea, so you can follow on the screen what happens.

With the registerClassAlias Client side classes (the ones that will be used for messaging) are mapped to server side classes. After this a Channel Set is created with a channel connecting to the MessagingServer.

Pinging WebORB to ensure MessagingServer is started

Now start the Flex application and click on the Button Pinging WebORB to ensure MessagingServer is started. The following code in Flex is executed:

1
2
3
4
5
6
7
8
9
10
11
private function pingWeborb() : void
{
    this.addLogMessage("Pinging WebORB");
 
    var ro:RemoteObject = new RemoteObject(REMOTEDESTINATION);
    ro.source = MANAGEMENTSERVICE;
    ro.endpoint = ENDPOINT;
    ro.ping.addEventListener(ResultEvent.RESULT, onWebOrbPingResult);
    ro.ping.addEventListener(FaultEvent.FAULT, onWebOrbPingFault);
    ro.ping();
}

On the server side Application_Start(object sender, EventArgs e) in Global.asax is triggered (if not then first kill the w3wp.exe process). You can check this by marking some breakpoints in VS2008 and starting your site in debug mode.

 Breakpoint Application Start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Application_Start(object sender, EventArgs e)
{
    try
    {
        Weborb.Config.ORBConfig config = new Weborb.Config.ORBConfig();
 
        Weborb.Messaging.RTMPServer server = new Weborb.Messaging.RTMPServer( "default", 2037, 500, config );
        server.start();
        Application[ "weborbMessagingServer" ] = server;
 
    }
    catch( Exception )
    {
    }
}

As you can see the RTMPServer is started here. If you started for instance the weborbconsole.html in the weborb30 installation site, then this RTMPServer will already be running on port 2037. In this case weborb30 installation site will catch messages and not your site. It’s hard for me to explain this as I am not a IIS expert…

Next the Session_Start(object sender, EventArgs e) in Global.asax is triggered. Arter this the weborb.aspx handler will handle this ping request and the ping() function in the Weborb.Management.ManagementService will be triggered. If this is successfull then the onWebOrbPingResult should be triggered (on flex side), if not the onWebOrbPingFault will be triggered (on flex side).

If there’s a problem then start your site in debug mode and check the output. Here’s the output for a successfull ping

[code]
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:Received request for http://localhost/johleromsmq/weborb.aspx from ::1
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:Request is in session 4shaj0bj10sjmb55o1rhcrii
[Thread-4] WEBORB INSTRUMENTATION:3/07/2008 20:59:39:Request parsing time in ms - 3
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:Dispatching request processing to V3 Dispatcher
[Thread-4] WEBORB SECURITY:3/07/2008 20:59:39:access allowed. resource name - 'Weborb.Management.ManagementService#ping'. reason -
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:loading type: Weborb.Management.ManagementService
[Thread-4] WEBORB INSTRUMENTATION:3/07/2008 20:59:39:clr method invocation time (in ms) - 0 method - ping
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:Object Handler handler successfully invoked method ping on the service Weborb.Management.ManagementService
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:V3 Dispatcher successfully handled request processing
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:Beginning to write response
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:got headers 0
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:got bodies 1
[Thread-4] WEBORB INFO:3/07/2008 20:59:39:AMFMessageWriter.write - message version: 3 header length: 0
[Thread-4] WEBORB INSTRUMENTATION:3/07/2008 20:59:39:Response serialization time in ms - 9
[Thread-4] WEBORB INSTRUMENTATION:3/07/2008 20:59:39:Total request processing time in ms - 141
[/code]

Initializing connection to get unique ClientId

Click on the Button Initializing connection to get unique ClientId. Next Flex code is executed:

1
2
3
4
5
6
7
8
9
10
11
12
13
private function initConnection() : void
{
    this.addLogMessage("InitConnection");
 
    var uri:String = URI;
    wOrb_nc = new NetConnection();
 
    wOrb_nc.client = this;
    wOrb_nc.objectEncoding = ObjectEncoding.AMF3;
    wOrb_nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
    wOrb_nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
    wOrb_nc.connect( uri, "NetConnection");
}

This makes a connection with the localhost on port 2037. On the server side the IScopeHandler.connect(IConnection conn, IScope scope, object[] parms) function is triggered in the CustomApplicationAdapter. This CustomApplicationAdapter is configured in the app.config in the /Applications/MessagingService folder under root of the site. The “NetConnection” parameter will appear in the object[] parms paramter in C# function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
bool IScopeHandler.connect(IConnection conn, IScope scope, object[] parms)
{
    Log.startLogging(Weborb.Util.Logging.LoggingConstants.DEBUG);
    Log.log(Weborb.Util.Logging.LoggingConstants.DEBUG, "*** CustomApplicationAdapter:connect(" + conn.ToString() + ", " + scope.ToString() + ")");
 
    //Get the connections of the scope (MessagingService scope)
    IEnumerator connections = scope.getConnections();
 
    //Create an object that will be sent back to client
    Object[] args = new Object[1];
    args.SetValue(conn.getClient().getId(), 0);
    String inName = null;
    try
    {
        inName = (Convert.ToString(parms[0])); //Will throw an error if Consumer of Producer connects
        //Check if it is the NetConnection connecting (Consumer &amp; Producer will also be connecting)
        if (conn is IServiceCapableConnection &amp;&amp; inName == "String type. Value - NetConnection")
        {
            Log.log(Weborb.Util.Logging.LoggingConstants.DEBUG, "***     CustomApplicationAdapter:connect:NetConnection ClientId " + conn.getClient().getId());
 
            Log.log(Weborb.Util.Logging.LoggingConstants.DEBUG, "*** CustomApplicationAdapter:connect:NetConnection Invoking setClientID on Flex Client " + conn.getClient().getId());
            //Let Client now his ClientId
            ((IServiceCapableConnection)conn).invoke("setClientID", args);
 
            Log.log(Weborb.Util.Logging.LoggingConstants.DEBUG, "*** CustomApplicationAdapter:connect:NetConnection Invoking initLockedPotatoeMessages on Flex Client " + conn.getClient().getId());
            //Let Client now which Potatoes are already locked
            ((IServiceCapableConnection)conn).invoke("initLockedPotatoeMessages", new Object[] { LockedPotatoeRegistry.instance.GetAllLockedPotatoeMessages() });
 
            Log.log(Weborb.Util.Logging.LoggingConstants.DEBUG, "*** CustomApplicationAdapter:connect:NetConnection Invoking initLockedBananaMessages on Flex Client " + conn.getClient().getId());
            //Let Client now which Banana's are already locked
            ((IServiceCapableConnection)conn).invoke("initLockedBananaMessages", new Object[] { LockedBananaRegistry.instance.GetAllLockedBananaMessages() });
 
            Log.log(Weborb.Util.Logging.LoggingConstants.DEBUG, "*** CustomApplicationAdapter:connect:NetConnection Invoking initLockedAppleMessages on Flex Client " + conn.getClient().getId());
            //Let Client now which Apples are already locked
            ((IServiceCapableConnection)conn).invoke("initLockedAppleMessages", new Object[] { LockedAppleRegistry.instance.GetAllLockedAppleMessages() });
 
            Log.log(Weborb.Util.Logging.LoggingConstants.DEBUG, "*** CustomApplicationAdapter:connect:NetConnection Setting client connection uName attribute to " + inName);
            //Set an attribute on the connection so when disconnecting we can now if it is the NetConnection disconnecting
            conn.getClient().setAttribute("uName", inName);
        }
    }
    catch (Exception)
    {
        //Consumer or Producer trying to connect. Haven't found a way to make difference between the two.
        Log.log(Weborb.Util.Logging.LoggingConstants.DEBUG, "*** CustomApplicationAdapter:connect:WebORBConsumer or WebORBProducer connected with clientId " + conn.getClient().getId());
    }
 
    Log.stopLogging(Weborb.Util.Logging.LoggingConstants.DEBUG);
    return base.connect(conn, scope, parms);
}

After this the IScopeHandler.join(IClient client, IScope scope) is also triggered adding the client to the scope.

Please see the comments in the code to now what is happening. In Flex there are four five functions being triggered as you can see in the output panel:
[code]
SERVER CALL: setClientID(2)
SERVER CALL: initLockedPotatoeMessages(0)
SERVER CALL: initLockedBananaMessages(0)
SERVER CALL: initLockedAppleMessages(0)
netStatusHandler(NetConnection.Connect.Success)
[/code]

The clientId is 2 (that’s the id from the NetConnection) and there are not locked potatoes, banana’s or apples. Next there’s the return base.connect(conn, scope, parms); which triggers the netStatusHandler with the event.info.code being NetConnection.Connect.Success.

If you would change the port from 2037 to 2038 you would have to get this in the output panel:
[code]
netStatusHandler(NetConnection.Connect.Failed)
[/code]

If you kill the w3wp.exe process and try to initialize the netconnection (on port 2037) without pinging WebOrb you’ll get the same Failed Connection. This is because in Global.asax the Application_Start will not have been triggered and the MessagingServer not been started.

Connecting the Consumer

In Flex application click on Connect as Consumer to receive the messages. Next Flex code is executed:

1
2
3
4
5
6
7
8
9
10
11
12
private function connectAsConsumer() : void
{
this.addLogMessage("connectAsConsumer()");
 
consumer = new WeborbConsumer();
consumer.channelSet = cs;
consumer.destination = MESSAGINGDESTINATION;
consumer.subqueue = this.subQueueu.text;
consumer.addEventListener(MessageAckEvent.ACKNOWLEDGE, onConsumerMessageAck);
consumer.addEventListener(MessageFaultEvent.FAULT, onConsumerMessageFault);
consumer.subscribe(this.clientId);
}

The consumer uses the ChannelSet that was created in the onCreationComplete handler. The subqueue will be used to create the name of the queue (see MESSAGINGDESTINATION). The paramter this.clientId will be accessible in the CustomSelector.cs. We will be able to use this in the CustomSelector.cs to now which client will receive a message.

On the server side next function in CustomApplicationAdapter are triggered:
IScopeHandler.connect(IConnection conn, IScope scope, object[] parms){…}
IScopeHandler.join(IClient client, IScope scope){…}

After this the CustomSelector Constructor is triggered creating an instance for the Flex Consumer. Messages send by the Producer to Clients will pass through these CustomSelectors. Next the setClientID(string clientID) is triggered setting the ClientId for this CustomSelector.

In the Output window this should appear:
[code]
connectAsConsumer()
onChannelConnect([ChannelEvent channelId="WebOrbMessagingChannel" reconnecting=false rejected=false type="channelConnect" bubbles=false cancelable=false eventPhase=2])
onConsumerMessageAck([MessageAckEvent messageId="EAE06CDF-7937-46C3-A3BF-D36DB702B530" correlationId="7DD6330D-536F-C6F9-1B35-EEE6E1050877" type="acknowledge" bubbles=false cancelable=false eventPhase=2])
[/code]

The onChannelConnect and onConsumerMessageAck are actually the same (I think). They are just saying that the Channel did connect in the same way that the NetConnection could connect.

Connecting the Producer

Now click on Connect as Producer to send the messages. Next Flex code is executed:

1
2
3
4
5
6
7
8
9
10
11
12
13
private function connectAsProducer() : void
{
this.addLogMessage("connectAsProducer()");
 
producer = new WeborbProducer();
producer.channelSet = cs;
producer.destination = MESSAGINGDESTINATION;
producer.subqueue = this.subQueueu.text;
producer.addEventListener(MessageAckEvent.ACKNOWLEDGE, onProducerMessageAck);
producer.addEventListener(MessageFaultEvent.FAULT, onProducerMessageFault);
producer.producerId = this.clientId;
producer.send(new AsyncMessage());
}

I think this is quite obvious. Setting the clientId doesn’t really do anthing and I haven’t been able to trace it on the Server side. Sending an empy AsyncMessage() is just a test for me to know if the Producer is able to send the message. If the Consumer receives this empty message, then I know that I can begin sending real messages. If you don’t send something, then the Producer will not be connected. I’m a bit uncertain how this Producer works as it is a class written by WebOrb. You can check it yourself by commenting the producer.send(new AsyncMessage()); line.

On the server side the processClientMessage(V3Message message) function in the CustomSelector is triggered. It is the CustomSelector instance for the Consumer of our Flex applicatoin. No connect or join is triggered because the same Channel as the Consumer is used. And this Channel is already connected. In the Flex Output Panel this should appear:
[code]
onProducerMessageAck([MessageAckEvent messageId="A489BA11-1A51-4E61-994B-D6866DF1DF4E" correlationId="462E277A-BFB5-DC6C-9B5E-EEEF74B6CD56" type="acknowledge" bubbles=false cancelable=false eventPhase=2])
onChannelMessage([MessageEvent messageId="462E277A-BFB5-DC6C-9B5E-EEEF74B6CD56" type="message" bubbles=false cancelable=false eventPhase=2])
Unknown Message
[/code]

Sending three locked messages

Now click the button that sends a locked apple, banana and potatoe message and see what happens…

From here on I’ll let you find things out yourself as I am tired or writing, taking screenshots, copy pasting…

I will explain some choices I made in Part III. If you have suggestions, please write them down. I’m very much sure that things can be done better, and as I continue with the project, I’m sure I’ll comment on errors in my own post… One example is the NetConnection. I have done this to get a clientId. This clientId can be given to the Consumer. I would like to omit this NetConnection and give the clientId of the Consumer to the suscribe method of the Consumer… I have tried to first connect the Channel to get the ClientId and then suscribe the Consumer, but this throws an error…

So, well, I hope this was of any use!

Ciao! Johlero!

 

Thx to the midnight coders!

.NET Invocation guide for Flash Remoting clients AS3

Finding examples with AS2 to do a Remote Call (Fluorine or WebORB) is easy.
However, finding an example for AS3 is very, very hard.
Here’s how you do it (thanks to Ward De Langhe):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private function getGalleryData():void{
 
registerClassAlias("logics.BLL.CollectionEntity",GalleryVO);
registerClassAlias("logics.BLL.GridRowEntity",GalleryRowVO);
registerClassAlias("logics.BLL.PictureEntity",GalleryImageVO);
 
galleryService=new NetConnection();
galleryService.objectEncoding=ObjectEncoding.AMF0;
galleryService.connect(SiteModel.GATEWAY_URL);
//SiteModel.GATEWAY_URL is url path to your gateway
responder=new Responder(galleryResult, galleryFault);
galleryService.call("remoting.service.CollectionService.GetCollection",responder);
}
 
private function galleryResult(result:Object):void{
galleryVO=result as GalleryVO;
}
 
private function galleryFault(fault:Object):void{
}

We haven’t been able to figure aout why ObjectEncoding.AMF3 doesn’t work.
Well, it works, but in the result the objects are not deserialized, so you just get Objects…