ANTS Memory Profiler

Well, I’m speaking about a commercial product again. This time it’s about ANTS Memory Profiler (by Red Gate software, the ones that made .Net reflector). This product will help you identify any .Net memory leak you could have.

The truth is, .Net never leaks but you can sometimes make stupid conception mistakes, like forgetting to remove references of some objects (that may contains heavy references themselves).

The tool allows you to take snapshots of your running application and compare different snapshots. You can see the difference of memory consumption by some objets or the difference of class instance count.

I’ve used it at work to solve a Sharepoint memory leak. And I finally discovered that the memory leak is really coming from the .Net Sharepoint object model. I’ll talk about this later. Sharepoint is amazing both ways.

Mono Tools for Visual Studio

I talked about this earlier.

The mono tools for Visual Studio is a commercial product that easily enables you to test and port your Windows applications to mono.

As I told before this is really a great idea. Because I think Visual Studio is the best IDE and Linux is the best server OS (in my opinion, it’s more efficient and more robust than Windows Server). So, I think it’s the perfect gateway between this two very different (but very close since Mono appeared) environments to achieve greatness.

And if you don’t want to buy it, well, you don’t have to. Mono is free and you can very easily test your Windows programs on it. You just have to install mono, copy your exe (plus its required DLLs) to the Linux host and type something like “mono SampleProgram.exe”.

Sharepoint 2010 on my Windows 7

I finally took the time to make “Sharepoint 2010” work on my PC. I previously did the installation as recommended by the Microsoft. I had to do one additionnal thing to make it work : “Desactivate the .Net 4 ISAPI filter”.

In the event logs, I had this error :

1
ISAPI Filter 'C:\Windows\Microsoft.NET\Framework\v4.0.21006\aspnet_filter.dll' could not be loaded due to a configuration problem. The current configuration only supports loading images built for a AMD64 processor architecture. The data field contains the error number. To learn more about this issue, including how to troubleshooting this kind of processor architecture mismatch error, see http://go.microsoft.com/fwlink/?LinkId=29349.

And then :

1
Could not load all ISAPI filters for site 'SHAREPOINT - 80'.  Therefore site startup aborted.

And the same of the “Central Administration” :

1
Could not load all ISAPI filters for site 'SHAREPOINT CENTRAL ADMINISTRATION V4'.  Therefore site startup aborted.

It seems to work fine, I’ll get back on it later. What I could see so far is that it’s more pretty, easier to use but it doesn’t seem more efficient or lighter than Sharepoint 2007, you can see the RAM consumption grow high speed with the page loading.

Totally unrelated note : I discovered I was one of the two guys to use the SVN version of WordPress and display it.

JObexFTP from Ricardo Schmidt

WARNING: All the Cinterion related content from this blog will be removed to go to the javacint wiki soon. Please get used to going there.

Ricardo Schmidt made a great multi-platform TC65 control and file management tool called JOBextFTP. This can be used for anyone whose Module Exchange Suite (MES) doesn’t work or doesn’t work correctly.

The biggest effort is put on managing the OBEX file transmission protocol, but it also does offer some simple methods that can be integrated in your development process, like “turnOn”, “turnOff”, “getTime”, “runApp”.

I’m really sorry but I didn’t take the time to test it as I’m currently not making some TC65 development. But it looks very operational to me.

Maybe I should start a list of useful TC65 softwares. I can already see this one to help you deploy programs and files locally and my SMSOTAP to help you deploy programs remotely.


Update: JObexFTP 2.4
Thu, Feb 24, 2011 at 00:51

Hello,

You recieved this email due your interest in JObexFTP.

JObexFTP have new cool features!
Now we can:
– move files and folder with mv command
– change mode of files and folder with chmod command
– see the total disk space and the free space.

Checkout the changelog from the past 2 releases:

JObexFTP 2.4
– Added disk information in about command
– Added move (mv) operation
– Added change mode (chmod) operation
JObexFTP 2.3
– Improved ObexClose.
– Improved estability.
– Faster operations.
– Code clean up.
– Fixed getContents of binary OBEXFile

Thank you for the attention!


Update: New interface
Sat, Jan 29, 2011 at 08:12

Hello,

You received this email due your interest in JObexFTP.

New utilities: emulator and cmd_autoexec_off.sh
New TC65SH UI: An improved user interface based on TC65SH 1.2.0
Makefile: just type `make && sudo make install` and call it by
`jobexftp` from anywhere in your linux. (It also installs the rxtxlib)

Using JObexFTP emulator with your IDE your going to send files to
module, execute and follow their output, all in the IDE. Stack Traces
are automatically parsed by IDE and shown in a programmer friendly way.
Check out attached screen shot.
JObexFTP emulator also turns on your device if is not yet, and attempts
to leave data mode if no response to at commands (just using echo and
cat).

binaries, libraries and sources available to download in github
https://github.com/3esmit/JObexFTP/

Any feature request, issues, experiences or improvements with JObexFTP
please contact me, I will be pleased in have your feedback.

Thank you for the attention,
Ricardo Guilherme Schmidt

Update : JObexFTP version 2.0
Ricardo Schmidt sent an other mail about his project on the javacint group, on Oct 22, 2010 at 14:51 :

Hello community,

I’ve just released JOBEXFTP 2.0 (beta), which brings tons of
improvements from last version due total refactoring of code. Its
better then never, check out sources!
Now the transfers are stable and 60% much faster. If you are a maniac
object oriented programmer, you going to love it, since it follow many
project patterns as listeners, notify, encapsulation.
The program API is a truly Java library, with easy Obex FTP calls.
Full information available in project README.

The project is released under LGPL and need your feedback.

Access: http://www.github.com/3esmit/jobexftp

Here is the email Ricardo Schmidt sent me on the 17 December 2009 :
I modified and formated some parts of the original mail.

Hello Florent,

With Ondrej ObexTool sources, that I met through your blog, I made up a new tool (think OO). So I made a library to help developers make multiplatform easy communication with TC65 and simmilar modules.
I’m already using it in a desktop configuration program for my M2M application and it just works great.
It have some TODOs and maybe some bugs, but it have a nice object oriented programming. Also I abused of the exceptions, becouse this project is not just an application, but also a library. You can use it to develop your configurator programs, its very easy to do with it.
The code is fully in english, but in future I pretend make it multilanguage. For now it just garantees that TC65 is going to work with it, but I belive TC65i and XT75 should work as TC65, since it under the same AT “platform”.

Some nice features in the library :

  • Auto turns on/off the module
  • If for some previous crash it stayed in DATAMODE, it sends the +++
    until it get the COMMANDMODE again.
  • You can choose for seeing verbose messages and for seeing the AT
    communication.
  • You can send a String as a file to the module.
  • You recieve a file as a String from module (since writing a file is easy).
  • Working in windows platf (under test)

Some nice features in the application :

  • Argument configurable on/off module, verbose and show atcomand.
  • Download, upload and list files just in one connection (no limit) ex.: jobexftp /dev/ttyACM0 -u arq1.ext arq2.ext arq3.ext arq4.ext -l -d filetodown.ext -V -A
  • Option to just stdout the file. (sometimes we just need to see the
    contents)

TODOs:

  • Documentation (kinda important)
  • Auto run jars (in library is ready, just for app)
  • Choose folder to send the file (easy task)
  • MacOSX adaption (maybe its already working, I dont have any macs to test)
  • Multilanguage

The listing comes in the XML from library, I think that this should be treated just in the applications, since the xml is very good for programming analyse.

Hope you like it.

You can get the latest sources from project kenai : http://www.kenai.com/projects/jobexftp

Kenai is NetBeans integrated, and it uses subversion. This project is opensource under GPL (application) and under LGPL (library).

Thank you for the attention,
Ricardo Schmidt

Here is an other message Ricardo sent on the 18 March 2010 :

Hello developers,

Due to the kenai closing, and to have the best control version system, JObexFTP have moved to github.com.
Now we can manage projects better with git control version. If you dont know Git, you should!
The Kenai’s JObexFTP will be closed soon, the repository is already deleted.
Note: The old JObexFTP versions are lost due to new commit.
Please consider joining my project.

What is JObexFTP?
Its a fully opensource library (and application) to transfer files from/to obex server.
You can use it in any J2SE application to enable java communication to module without third party app. You can also use it as a MES in unsupported cinterion plataforms like Linux.
For now it just supports cinterion modules due to ATCommand spec.
It needs RxTx library to have the serial communication.

JObexFTP news:
Now we have fully OO obex objects (files/folders).
The downloading files is speeded up.

Under development:
Human readable folder listing
Multilanguage

Known issues:
Deleting multiple files is buggy and needs reconnection. (Its also buggy in windows MES, but here is a way more buggy)
In Windows sometimes it takes more time to have obex ready to read folder listing (I hate you microsoft)

The M2MP protocol

Today, I’m going to talk about a little protocol that I’ve implemented in few softwares (on TC65, .Net client and .Net server). It won’t be very interesting for most of you (99.99%). But still, I wanted to write it because it has been very useful and it might prevent you from writing the next stupid M2M protocol specifications.

Motivations

I made this simple protocol to solve very simple and common problems on M2M equipments, we want to :

  • Transmit and receive in real-time
  • Transmit as few data as possible
  • Send and receive any kind of data

So the basic ideas are to :

  • Keep a TCP connection open all the time by sending regular keep-alive frames.
  • Define named channels that are only transmitted once
  • Send byte array, and two-dimensionnal byte arrays on these channels

We rely on the TCP protocol for packet checksuming, packet ordering and every little things it does very well.

Basically, we send these kind of frames :

  • Identification frames
  • Channel name to id definition
  • Data transmission of single byte arrays (Byte[]).
  • Data transmission of two-dimentionnal byte arrays (Byte[][]).

The protocol

Identification
The client ask for authentication :

1
2
3
[ 1 ] { 0x01 } // Identification header
[ 1 ] { 0x05 } // Size of the identification
[ 5 ] { 'h', 'e', 'l', 'l', 'o' } // Identifier

The server replies “ok” or “not ok”

1
2
[ 1 ] { 0x01 } // Identification request
[ 1 ] { 0x01 } // OK, 0x00 for not ok

Ping / Keep alives
Client sends a ping request :

1
2
[ 1 ] { 0x02 } // client ping request header
[ 1 ] { 0x15 } // the number (here 0x15) can be incremented or random

Server answers the ping request :

1
2
[ 1 ] { 0x02 } // client ping server response header
[ 1 ] { 0x15 } // where 0x15 is the number of the ping request

Server sends a ping request :

1
2
[ 1 ] { 0x03 } // server ping request header
[ 1 ] { 0x16 } // the number (here 0x16) can be incremented or random

Client answers the ping request :

1
2
[ 1 ] { 0x03 } // server ping client response header
[ 1 ] { 0x16 } // where 0x16 is the 0x16 number of the ping request

Defining a named channel
This applies to both client to server and server to client communcation.

1
2
3
4
[ 1 ] { 0x20 } // channel definition frame header
[ 1 ] { 0x0D } // where 13 is the size of the following message
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 12 ] { 'c', 'h', 'a', 'n', 'n', 'e', 'l', ' ', 'n', 'a', 'm', 'e' } // the name of the channel : "channel name"

Sending some data
This applies to both client to server and server to client communcation.

For 0 to 254 sized messages :

1
2
3
4
[ 1 ] { 0x21 } // one byte sized data transmission frame header
[ 1 ] { 0x06 } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 5 ] { 0x01, 0x02, 0x03, 0x04, 0x05 } // data transmitted on the channel

For 255 to 65534 sized data messages :

1
2
3
4
[ 1 ] { 0x41 } // two bytes sized data transmission frame header
[ 2 ] { 0x00, 0x06 } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 5 ] { 0x01, 0x02, 0x03, 0x04, 0x05 } // data transmitted on the channel

For 65 535 octets to 4 294 967 294 sized data messages :

1
2
3
4
[ 1 ] { 0x61 } // two bytes sized data transmission frame header
[ 4 ] { 0x00, 0x00, 0x00, 0x06 } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 5 ] { 0x01, 0x02, 0x03, 0x04, 0x05 } // data transmitted on the channel

For data arrray transmission, it’s the same except frame header are 0x22, 0x42, 0x62 instead of 0x21, 0x41, 0x61. If you transmit data array like this one Byte[][] data = { { 0x01, 0x02 }, { 0x03, 0x04, 0x05 }, { 0x06, 0x07, 0x08, 0x09 } } :

1
2
3
4
5
6
7
8
9
[ 1 ] { 0x22 } // one byte sized data transmission frame header
[ 1 ] { 0x0D } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 1 ] { 0x02 } // size of the first array
[ 2 ] { 0x01, 0x02 } 
[ 1 ] { 0x03 } // size of the second array
[ 3 ] { 0x03, 0x04, 0x05 } 
[ 1 ] { 0x04 } // size of the third array
[ 4 ] { 0x06, 0x07, 0x08, 0x09 }

For bigger arrays, you need to define :

1
2
3
4
5
6
7
8
9
[ 1 ] { 0x42 } // one byte sized data transmission frame header
[ 1 ] { 0x10 } // size of the data (16)
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 2 ] { 0x00, 0x02 } // size of the first array
[ 2 ] { 0x01, 0x02 } 
[ 2 ] { 0x00, 0x03 } // size of the second array
[ 3 ] { 0x03, 0x04, 0x05 } 
[ 2 ] { 0x00, 0x04 } // size of the third array
[ 4 ] { 0x06, 0x07, 0x08, 0x09 }

and so on :

1
2
3
4
5
6
7
8
9
[ 1 ] { 0x62 } // one byte sized data transmission frame header
[ 1 ] { 0x16 } // size of the data (22)
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 4 ] { 0x00, 0x00, 0x00, 0x02 } // size of the first array
[ 2 ] { 0x01, 0x02 } 
[ 4 ] { 0x00, 0x00, 0x00, 0x03 } // size of the second array
[ 3 ] { 0x03, 0x04, 0x05 } 
[ 4 ] { 0x00, 0x00, 0x00, 0x04 } // size of the third array
[ 4 ] { 0x06, 0x07, 0x08, 0x09 }

Sample data types transmitted

Strings
Easy… Just the bytes value of the string

Position
when moving :

1
2
3
4
5
[ 4 ] Timestamp : UInt32 (since 1970 or 2009 if you wish)
[ 4 ] Longitude : Float
[ 4 ] Latitude : Float
[ 2 ] Speed : UInt16
[ 2 ] Altitude : UInt16

= 16 bytes

when stopped :

1
2
3
[ 4 ] Timestamp : UInt32 (since 1970 or 2009 if you wish)
[ 4 ] Longitude : Float
[ 4 ] Latitude : Float

= 12 bytes

Battery
Percentage :

1
[ 1 ] Percentage (Byte)

Voltage :

1
[ 2 ] Voltage in mV (UInt16)

Sample communication

This is just to show how is working a sample communication

1
2
3
4
--> [] { 0x01, 0x04, 0x01, 0x02, 0x03, 0x04 } // identification frame
<-- [] { 0x01, 0x01 } // identification accepted
--> [] { 0x20, 0x08, 0x00, 'b', 'a', 't', 't', 'e', 'r', 'y' } // We define the channel "battery" with id 0x00
--> [] { 0x21, 0x02, 0x00, 70 } // We send 70 (meaning 70%) on the "battery" channel.

On top of that

On top of that, I added some settings management and status specifications.

Settings management
The server can get and set some settings on the “_set” channel.
If it’s a two-dimentionnal array and the first element is “s” (like “set”) it means that it sets some settings, the next elements will be “setting1=value1”, “setting2=value2”, etc.
If it’s a two-dimentionnal array and the first element is “g” (like “get”) it means that it needs the client to report the value of some settings, like “setting1”, “setting2”
If it’s a single dimentionnal array that contains “ga” (like “get all”) it means that the server wants the client to report the value of every settings.
If the client doesn’t know some settings that were sent to it, it can report it using “u”.
The client can also send “c” (in the same format as the get) asynchronously if a setting changed on the client side.

Sample transmission:
Setting values:

1
 --("_set")--> { "s", "setting1=value1", "setting2=value2" }

It’s the responsibility of the server to send an acknowledge request to know that the client has actually successfully received this settings.

Getting values:

1
2
 --("_set")--> { "g", "setting1", "setting2" }
<--("_set")--  { "g", "setting1=value1", "setting2=value2" }

Setting and gettings value :

1
2
 --("_set")--> { "sg", "setting1=value1", "setting2=value2" }
<--("_set")--  { "g", "setting1=value1", "setting2=value2" }

Gettings all the values :

1
2
 --("_set")--> "ga"
<--("_set")--  { "g", "setting1=value1", "setting2=value2" }

Sending a value that is unknown to the client :

1
2
 --("_set")--> { "sg", "setting1=value1", "setting98=value98", "setting99=value99" }
<--("_set")--  { "g", "setting1=value1", "setting98", "setting99" }

Same since here except we don’t specify the get. The client might decide to tell us the setting is unknown.

1
2
 --("_set")--> { "s", "setting1=value1", "setting98=value98", "setting99=value99" }
<--("_set")--  { "u", "setting98", "setting99" }

Status parameters management
The server can get some status parameters on the “_sta” channel. Status are pretty much like settings except that they describe parameters that cannot be changed from the server.

Getting a parameter:

1
2
 --("_sta")--> { "g", "version" }
<--("_sta")--  { "g", "version=0.0.1" }

Getting all parameters:

1
2
 --("_sta")--> { "ga" }
<--("_sta")--  { "g", "hw=tc65i/1.1", "sw=sampleprogram/0.0.1", "cap=sms,applicationA/1.0" }

Value changed on the client side

1
<--("_set")-- { "c", "setting1=value1" }

Capacities
Previously, capacities where requested by server with the “?” message on the “_cap” channel but they are now handled through the standard status message and the “cap” parameter.

1
2
 --("_sta")--> { "g", "cap" }
<--("_sta")--  { "g", "cap=sms_relay_send_1,gpio_1" }

Command execution

1
2
3
4
// To send a command
 --("_cmd")--> { "e", "<commandId>", "command", "arg1", "arg2" }
// To acknowledge the command
<--("_cmd")--  { "a", "<commandId>" }

In the future commands could algo be acknowledge as a group by sending an acknowledge request.

Files transmission
The server sends:

1
<--("_fi:u")-- { byte[5] = "file1", byte[4] = (uint32_t) file1Size, byte[16] = file1Hash, byte[5] = "file2", byte[4] = (uint32_t) file2Size, byte[16] = file2Hash }

If the client wants to get the detailed subhashes:

1
2
 --("_fi:h")--> { byte[5] = "file1", byte[4] = (uint32_t) size, byte[4] = (uint32_t) offset1, byte[4] = (uint32_t) offset2 }
<--("_fi:h")--  { byte[5] = "file1", byte[4] = (uint32_t) size, byte[4] = (uint32_t) offset1, byte[16] = hash, byte[4] = (uint32_t) size, byte[16] = hash }

Then if the client has a different value of the files:

1
2
 --("_fi:g:file1")--> { byte[4] = (uint32_t) offset, byte[4] = (uint32_t) size }
<--("_fi:g:file1")--  { byte[4] = (uint32_t) offset, byte[ x ] = data, byte[16] = file1Hash }

Publish/Subscribe (MQTT like)
You could subscribe to a channel “toto” using:

1
2
 --("_pubsub")--> { "s" , "toto" }
<--("_pubsub")--  { "sa", "toto", [ 0x01 ] }

You could unsubscribe to a channel “toto” using:

1
2
 --("_pubsub")--> { "u", "toto" }
<--("_pubsub")--  { "ua", "toto", [ 0x01 ] }

And the rest is left like this.

Gateway acting and sub equipements
Any equipment can act as a gateway by encapsulating data of an sub-equipment in a channel name like this “_eq/[sub equipment identifier]” but this has never been used for the moment.

Every packet coming from the sub-equipment is relayed to the server as a data packet.
If the equipment, identified locally by “mac:112233445566” sends an identification to the server, this will work like this :
Subequipment to Equipment:

1
[ 0x01 0x04 't' 'o' 't' 'o' ]

This will be relayed to the server as data message like this :

1
--("_eq/mac:112233445566")--> [ 0x01, 0x04, 't', 'o', 't', 'o' ]

Meaning in raw data :

1
[0x21 0x07 0x03 0x01 0x04 't' 'o' 't' 'o' ]

(where 0x03 is the generated channel id)

Stupid protocols

I’ve seen a lot of protocols from big companies doing some really stupid things like :

  • Fixed size frames for not fixed size data.
  • 4 zero filled bytes preamble. Why the hell would we need preamble in a TCP connection ?
  • 8 bytes timestamp in millisecond when data has a 1 second precision.
  • 4 bytes integer for specifying some number that never exceed 32.
  • Checksums on top of the TCP checksumming mechanism.
  • Redundant data at the beginning and the end.
  • Disconnecting very frequently (TCP establishment + identification costs a lot).

TC65 : Settings management

Someone recently asked me and the javacint group how we should handle settings. So I’ll give you two answers :

  • ejw’s reply : You should use some simple DataInputStream and DataOutputStream objects.
  • Mine : If you only store simple values (numbers and text), you should use a simple text file. It enables you to easily see and modify settings outside the TC65.

So, my little gift of today will be a simple settings management class. The idea is that this file is in a very simple format (that can be read in any PC) and it only stores settings that have changed. This is very important, it allows you to change the default behavior at next software update but also enable you to override some of the settings for each chip (like the last IMSI identifier of the SIM card).

I did something wrong in this example by writing all the methods with a starting Uppercase letter. The reason is that I do like a it a lot better like that (this is the C# style) but style, the good thing to do is to stick to each language’s coding conventions. I can’t really update this code as I have modified this class to a more complex version where other class register to this one to receive settings change event.

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package Common;
 
import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import com.siemens.icm.io.file.*;
 
/**
 * Settings management class
 * @author Florent Clairambault
 */
public class Settings {
 
        // The cached settings
	private Hashtable _settings;
 
        // File of the settings
	private final String _fileName = "a:/settings.txt";
 
        // Singleton instance of the settings
        private static Settings _instance;
 
	/**
	 * Get Default instance of the Settings class
	 * @return The default instance of the Settings class
	 */
	public static synchronized Settings getInstance() {
		if ( _instance == null )
			_instance = new Settings();
		return _instance;
	}
 
	/**
	 * Free the singleton instance
	 */
	public static synchronized void freeInstance() {
		_instance = null;
	}
 
	/**
	 * Load settings
	 */
	public synchronized void load() {
		StringBuffer buffer = new StringBuffer();
		Hashtable settings = getDefaultSettings();
		try {
 
 
			FileConnection fc = (FileConnection) Connector.open( "file:///" + _fileName, Connector.READ );
			InputStream is = fc.openInputStream();
 
			while ( is.available() > 0 ) {
				int c = is.read();
 
				if ( c == '\n' ) {
					loadTreatLine( settings, buffer.toString() );
					buffer = new StringBuffer();
				} else
					buffer.append( (char) c );
			}
			is.close();
			fc.close();
 
		} catch ( IOException ex ) {
			// The exception we shoud have is at first launch : 
			// There shouldn't be any file to read from
 
			if ( Logger.E_CRITICAL )
				Logger.Log( 19, "Settings.Load", ex );
		}
		_settings = settings;
	}
 
	/**
	 * Treat each line of the file
	 * @param def Default settings
	 * @param line Line to parse
	 */
	private static void loadTreatLine( Hashtable settings, String line ) {
		if ( Logger.E_VERBOSE )
			Logger.Log( 78, "loadTreatLine( [...], \"" + line + "\" );" );
		String[] spl = Common.strSplit( '=', line );
		String key = spl[0];
		String value = spl[1];
 
		// If default settings hashTable contains this key
		// we can use this value
		if ( settings.containsKey( key ) ) {
			settings.remove( key );
			settings.put( key, value );
		}
 
	}
 
 
	/**
	 * Get default settings
	 * @return Default settings Hashtable
	 */
	private Hashtable getDefaultSettings() {
		Hashtable defaultSettings = new Hashtable();
 
		// General M2MSoft settings :
		defaultSettings.put( "code", "8888" );
		defaultSettings.put( "servers", "87.106.206.30:3000" );
		defaultSettings.put( "apn", "gprs,m2minternet,\"\",\"\",,0" );
		defaultSettings.put( "imsi", "0000" );
		defaultSettings.put( "watchdogtimer", "20" );
		defaultSettings.put( "version", "0" );
		defaultSettings.put( "phoneManager", "+33686955405" );
 
		return defaultSettings;
	}
 
	/**
	 * Reset everything
	 */
	public synchronized void resetEverything() {
		try {
			FileConnection fc = (FileConnection) Connector.open( "file:///" + _fileName, Connector.READ_WRITE );
			if ( fc.exists() )
				fc.delete();
                        _settings = new Hashtable();
		} catch ( Exception ex ) {
			if ( Logger.E_CRITICAL )
				Logger.Log( 16725, "Settings.ResetEverything", ex );
		}
	}
 
	/** 
	 * save setttings
	 */
	public synchronized void save() {
 
		// If there's no settings, we shouldn't have to save anything
		if ( _settings == null )
			return;
 
		try {
			Hashtable defSettings = getDefaultSettings();
			Enumeration e = defSettings.keys();
			FileConnection fc = (FileConnection) Connector.open( "file:///" + _fileName, Connector.READ_WRITE );
			if ( fc.exists() )
				fc.delete();
			//fc = (FileConnection) Connector.open("file:///" + _fileName, Connector.READ_WRITE);
			fc.create();
			OutputStream os = fc.openOutputStream();
 
			while ( e.hasMoreElements() ) {
				String key = (String) e.nextElement();
				String value = (String) _settings.get( key );
				<a href="http://www.google.com/search?hl=en&q=allinurl%3Astring+java prix viagra pharmacie france.sun.com&btnI=I%27m%20Feeling%20Lucky">String defValue = (String) defSettings.get( key );
 
				if ( // if there is a default value
					defValue != null && // and
					// the value isn't the same as the default value
					defValue.compareTo( value ) != 0 ) {
					String line = key + "=" + value + '\n';
 
					if ( Logger.E_DEBUG )
						Logger.Log( 131, "Settings.save.line = \"" + line + "\"" );
 
					os.write( line.getBytes() );
				}
 
			}
			os.flush();
			os.close();
			fc.close();
		} catch ( Exception ex ) {
			if ( Logger.E_CRITICAL )
				Logger.Log( 131, "Settings.save", ex );
		}
 
	}
 
	/**
	 * Init (and ReInit) method
	 */
	private void checkLoad() {
		if ( _settings == null )
			load();
	}
 
	/**
	 * Get a setting's value as a String
	 * @param key Key Name of the setting
	 * @return String value of the setting
	 */
	public synchronized String getSetting( String key ) {
		checkLoad();
		if ( _settings.containsKey( key ) )
			return (String) _settings.get( key );
		else
			return null;
	}
 
	/**
	 * Sets a setting
	 * @param key Setting to set
	 * @param value Value of the setting
	 */
	public synchronized void setSetting( String key, String value ) {
		checkLoad();
		if ( _settings.containsKey( key ) )
			_settings.remove( key );
 
		_settings.put( key, value );
 
		OnSettingChanged( key );
	}
 
	/**
	 * Get a setting's value as an int
	 * @param key Key Name of setting
	 * @return Integer value of the setting
	 * @throws java.lang.NumberFormatException When the int cannot be parsed
	 */
	public int getSettingInt( String key ) throws NumberFormatException {
		String value = getSetting( key );
 
		if ( value == null )
			return -1;
 
		return Integer.parseInt( value );
	}
}

Latter on, I added some settings consumer capacities. It allows to use this class in some sort of “third party” developed components. Each component had to register itself to the settings management class, the settings management class could then request the default value settings it will provide, and it could set/get settings. The settings event class also launched an event when a setting was changed.

Google Chrome Browser & OS

The browser
I’ve always been on the BĂȘta version of Google Chrome. And the current bĂȘta (soon to be stable I guess) now supports plugins, the most interesting ones are AdThwart which blocks ads and Gmail Checker which displays the number of received mails.
Making some chrome extensions seems to be really easy, it’s entirely based on javascript.
The other very interesting thing is the developper tools. They are very close to Firebug, it’s now really easy to debug JS and CSS within Chrome.
The browser also bookmarks synchronizing using the google account. But I’m not sure everybody will love this (big brother blah blah blah).

Firebug like developper tools interface :

The OS
It surely starts fast but as, in this beta version, it’s just a browser it’s not really interesting. I hope they will add a lot of stuff around it.

Ajax Push Engine 1.0 released

I’m quite unlucky because I’ve downloaded the APE BETA5 just before they (I mean Anthony Catel) released the 1.0. The quite interesting part is that it fixes all the bugs I’ve talked about sooner. So we could say my last post won’t be very useful to anyone.

Still, if you want to compile APE on CentOS x64, you have have to edit the modules/Makefile file and add the “-L/usr/lib64/mysql” command arg. It will give you something like that :

1
2
3
ifeq ($(HAS_MYSQL), yes)
        MYSQL_FLAGS = -L./deps/mysac/ -I./deps/mysac/ -lmysac -L/usr/lib64/mysql -lmysqlclient_r
endif

They also added some new great demos but they are not included in the 1.0 sources. I’ve been closely following their websites for the last two days and they did an awesome job in a very short time. And I guess a lof of people are taking interest in this project because you can see a lot of monkeys on the APE home page.

As soon as possible, I will make some little test with the very interesting javascript server-side code based on SpiderMonkey.

WP Codebox Quick fix for WordPress 2.9

This bug has been fixed !

I like to have the latest version of WordPress, this is why I use the SVN version. And recently the CSS of the “WP Codebox” plugin stopped working. Here the explanation and the solution :

It seems that starting with the 2.9 version, you can register the styles in the “wp_print_scripts” action method. So in the wp-codebox.php file, you have to put this :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
add_action('wp_print_scripts', 'Codebox_ScriptsAction');
function Codebox_ScriptsAction ()
{
    $cb_path = get_bloginfo('wpurl') . "/wp-content/plugins/wp-codebox"; //URL to the plugin directory
    if (! is_admin()) {
        wp_enqueue_script('jquery');
        wp_enqueue_script('codebox', $cb_path . '/js/codebox.js', array('jquery'), '0.1');
    }
}
 
add_action('wp_print_styles', 'Codebox_StylesAction');
function Codebox_StylesAction() {
        $cb_path = get_bloginfo('wpurl') . "/wp-content/plugins/wp-codebox"; //URL to the plugin directory
        if (! is_admin()) {
                wp_enqueue_style('codebox', $cb_path . '/css/codebox.css', array(), '0.1', 'screen');
        }
}

And it will work…

Ajax Push Engine 1.0 Beta 5

I talked quickly about APE in a recent post. I recently downloaded a new version of their program and
I’ve installed it successfully and it works much better. Since the first released verison, it has become quite easy, you just have to launch “./build.sh”. I had some problems with the linker on my CentOS system and the mysql libraries. But I solved it by added “-L/var/lib64/mysql” to some line of the Makefile.

How to make the demos work
They are some tiny bugs that took me quite some time to bypass :

  • To use the controller demo, you need to put the modules/conf/inlinepush.conf file into bin. I guess this will be fixed someday soon but still, that’s how it works right now.
  • To use the move demo, you need to change the scripts/main.ape.js file and add this line :
    1
    
    include("examples/move.js");
  • To use the TCPSocket demo, you need to copy the modules/conf/proxy.conf file into bin.

Note : Each time you modify a script file (within the “scripts” directoy of APE), you have to restart the server.

The very interesting javascript backend
This project is very interesting because everything in the backend is handled by some little javascript files, the move example file is this one :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Ape.registerCmd('setpos', true, function(params, infos) {
        if ((!$defined(params.x) || !$defined(params.y)) && (!isFinite(params.x) || !isFinite(params.y))) return 0;
 
        infos.user.setProperty('x', params.x);
        infos.user.setProperty('y', params.y);
 
        // We get the channel
        var chan = Ape.getChannelByPubid(params.pipe);
 
        // And send the position data of the calling user on it
        if (chan) {
                chan.pipe.sendRaw('positions', {'x': params.x, 'y': params.y}, {'from': infos.user.pipe});
        } else {
                return ['109', 'UNKNOWN_PIPE'];
        }
 
        return 1;
});

This is what allows the client to send this kind of request :

1
[{"cmd":"SETPOS","chl":10,"params":{"x":282,"y":38,"pipe":"3dcaa701e8ee3394d65d5f71cd18e428"},"sessid":"b897476d835d12c150fb9fc337c7c93d"}]

If you don’t add the include line, the answer will be something like that :

1
[{"time":"1260123358","raw":"ERR","data":{"chl":10,"code":"003","value":"BAD_CMD"}}]

The “BAD_CMD” reply means that this commands hasn’t be registered by the Ape.registerCmd method. If you come here because that happened to you, just fix it.

For the Controller demo, this is exactly the same. The file is :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Ape.registerCmd("inlinepush", false, function(params, infos) {
        // We check the password ("password" parameter of the "inlinepush.conf" file)
        if (params.password == Ape.config("inlinepush.conf", "password")) {
                // If every request parameters have been correctly defined
                if ($defined(params.channel) && $defined(params.data) && $defined(params.raw)) {
                        // We get the channel
                        var chan = Ape.getChannelByName(params.channel);
                        if (!$defined(chan)) return ["401", "UNKNOWN_CHANNEL"];
 
                        // We send the data (as received) back to the channel
                        chan.pipe.sendRaw(params.raw, params.data);
 
                        return {"name":"pushed","data":{"value":"ok"}};
                } else {
                        return 0;
                }
        } else {
                return ["400", "BAD_PASSWORD"];
        }
})

If you get the the BAD_PASSWORD error response and you’re not sure why, you can change the BAD_PASSWORD line by :

1
return ["400", "BAD_PASSWORD, the right one is :"+Ape.config("inlinepush.conf", "password")];

If you get “BAD_PASSWORD, the right one is :”. There’s a good chance you didn’t move the inlinepush.conf into the bin directory.

Sample communications
Here is a sample chat communication

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
Echange CHAT :
--> [{"cmd":"script","params":{"domain":"ape.webingenia.com","scripts":["http://ape.webingenia.com/Source/mootools-core.js","http://ape.webingenia.com/Source/Core/APE.js","http://ape.webingenia.com/Source/Core/Events.js","http://ape.webingenia.com/Source/Core/Core.js","http://ape.webingenia.com/Source/Pipe/Pipe.js","http://ape.webingenia.com/Source/Pipe/PipeProxy.js","http://ape.webingenia.com/Source/Pipe/PipeMulti.js","http://ape.webingenia.com/Source/Pipe/PipeSingle.js","http://ape.webingenia.com/Source/Request/Request.js","http://ape.webingenia.com/Source/Request/Request.Stack.js","http://ape.webingenia.com/Source/Request/Request.CycledStack.js","http://ape.webingenia.com/Source/Transport/Transport.longPolling.js","http://ape.webingenia.com/Source/Transport/Transport.SSE.js","http://ape.webingenia.com/Source/Transport/Transport.XHRStreaming.js","http://ape.webingenia.com/Source/Transport/Transport.JSONP.js","http://ape.webingenia.com/Source/Core/Utility.js","http://ape.webingenia.com/Source/Core/JSON.js","http://ape.webingenia.com/Source/Core/Session.js"]}}]
<-- (useless html response)
 
// We  try some "CONNECT" and then "JOIN" commands with the name "superfc"
--> [{"cmd":"CONNECT","chl":1,"params":{"name":"superfc"}},{"cmd":"JOIN","chl":2,"params":{"channels":"test"}}]
// Error, the nick name is already use
<-- [{"time":"1260119123","raw":"ERR","data":{"code":"005","value":"NICK_USED"}}]
 
// We  try some "CONNECT" and then "JOIN" commands with the name "Florent"
--> [{"cmd":"CONNECT","chl":5,"params":{"name":"Hello"}},{"cmd":"JOIN","chl":6,"params":{"channels":"test"}}]
// This works !
<-- [{"time":"1260119134","raw":"LOGIN","data":{"sessid":"36a60d006d50882d0d2cd6bcfda90cb9"}},
{"time":"1260119134","raw":"IDENT","data":{"user":{"casttype":"uni","pubid":"8b5fec5af11df915d9806a3be4b46c35","properties":{"name":"Hello"}}}},
{"time":"1260119134","raw":"CHANNEL","data":{"users":[{"casttype":"uni","pubid":"8b5fec5af11df915d9806a3be4b46c35","properties":{"name":"Hello"},"level":1},{"casttype":"uni","pubid":"cc03a08f304a75cc133827c6c4b561c8","properties":{"name":"superfc"},"level":1},{"casttype":"uni","pubid":"541e78a0fbfbc08457c800aacccd212d","properties":{"name":"Florent"},"level":1}],"pipe":{"casttype":"multi","pubid":"2b27244bdb42c193d5158e818ec577d0","properties":{"name":"test"}}}}]
 
// We send 
--> [{"cmd":"SESSION","chl":7,"params":{"action":"set","values":{"currentPipe":"2b27244bdb42c193d5158e818ec577d0"}},"sessid":"36a60d006d50882d0d2cd6bcfda90cb9"}]
<-- [{"time":"1260119136","raw":"LEFT","data":{"user":{"casttype":"uni","pubid":"cc03a08f304a75cc133827c6c4b561c8","properties":{"name":"superfc"}},"pipe":{"casttype":"multi","pubid":"2b27244bdb42c193d5158e818ec577d0","properties":{"name":"test"}}}}]
 
// These next requests are long polls with nothing
--> [{"cmd":"CHECK","chl":9,"sessid":"36a60d006d50882d0d2cd6bcfda90cb9"}]
 
// Nothing new
<-- [{"time":"1260119186","raw":"CLOSE","data":{"value":"null"}}]
 
--> [{"cmd":"CHECK","chl":10,"sessid":"36a60d006d50882d0d2cd6bcfda90cb9"}]
<-- [{"time":"1260119211","raw":"CLOSE","data":{"value":"null"}}]
 
--> [{"cmd":"CHECK","chl":11,"sessid":"36a60d006d50882d0d2cd6bcfda90cb9"}]
<-- [{"time":"1260119239","raw":"CLOSE","data":{"value":"null"}}]
 
// We send some text ("hello")
--> [{"cmd":"SEND","chl":19,"params":{"msg":"Hello hello","pipe":"2b27244bdb42c193d5158e818ec577d0"},"sessid":"36a60d006d50882d0d2cd6bcfda90cb9"}]
// And we receive it ("hello")
<-- [{"time":"1260119399","raw":"DATA","data":{"msg":"hello","from":{"casttype":"uni","pubid":"541e78a0fbfbc08457c800aacccd212d","properties":{"name":"Florent"}},"pipe":{"casttype":"multi","pubid":"2b27244bdb42c193d5158e818ec577d0","properties":{"name":"test"}}}}]

If you don’t understand what is long polling, please look at previous post on the subject.

Please not that that the “CLOSE” RAW isn’t some sort of “session closed” message. I’m not sure this it’s what the developer planned but right now it only means that there’s nothing to receive. So if you get that, don’t panic. Everything is working fine.

If you need any help…
Please, don’t hesitate add some question in the comments, I’ll be happy to help you.