Tuesday, 15 February 2011 18:53
ATTON Jonathan
There are no translations available. Azy and esskyuehl are two libraries written by the company http://www.zentific.com/. The aim of azy is to easily create a communication process between a client and a server like a webservice by using a RPC. The job of esskyuehl is to execute a sql query asynchronously.

Azy and esskyuehl are not really a part of the EFL project but use and are used by the EFL and are available in the official e-svn and from the zentific svn (only Azy currently) : http://svn.zentific.com/branches/azy
Generally in a webservice the server part will have to save or data data to/from a database. A SQL query can be slow for many reasons:
- the query is complicate
- the database is overloaded
- the database is located at the center of the world
- You use sleep() because you like joke
See a bad webservice call:
- client send a request, example : "save this message"
- the server receive the request
- the server execute the sql query "insert into ...". This part can be slow
- the server respond to the client
If between the step 2 and 4, a second client ask something to the server, the client will have to wait during the server process the request of the first client. This is very bad.
One solution can be to use a thread by client but you can have some problems with this solution :
- data corruption between threads
- server overloaded by threads
- EFL are not thread safe, you will have to use hacks
The solution is simple: execute our query in a asynchronous way. All EFL are built on this method. During a query is executed, the server will process request from others clients. When the query will be finished, the server will respond to the client.
To do this we have two parts :
- we can suspend and resume a azy module. We suspend it during the sql query is processed.
- esskyuehl: this library is important because:
- it is a framework to access at many databases manager : MySQL, Postgress, SQLite and others.
- you can connect and execute a query asynchronously
Example
In this example the webservice will
- client will send a message to the server
- [Asynchronous] the server will connect to the database
- [Asynchronous] and then save the message (Insert Into ...)
- and then respond to the client
- finally the client will be closed
source file: azy_mysql.tar.gz
The database
We use MySQL.
- Database: tests
- Table: messages
1 2 3
|
CREATE TABLE `tests`.`messages` ( `message` varchar(255) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
Do not forget to set the USER and the PASSWORD in the file services.azy.
The client
The client source code is simple :
| client.c |
1 2 3 4 5 6 7
|
//send the message // msg=Demo_Message_new(); msg->msg = eina_stringshare_add("I love Dogs !!!!!"); ret = Demo_Message_Get(cli, msg, content, NULL); CALL_CHECK(_Demo_Message_Get_Ret); //
|
The server
The azy part
| services.azy |
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
|
//This method is used by the server to save a message sent by the client //use a asynchronous call. This way the server is not blocked during the execution of the query ! Message Get(Message msg) <% printf("Store in database: %s\n", msg->msg); //create our data Test_Data *data = calloc(1,sizeof(Test_Data)); data->msg = msg; data->m = module; // //Esskyuehl Esql *e; e = esql_new(ESQL_TYPE_MYSQL); data->e = e; esql_database_set(e, "tests"); //connect to the mysql database esql_connect_callback_set(e, _connect, data); esql_connect(e, "127.0.0.1:" ESQL_DEFAULT_PORT_MYSQL, "USER", "PASSWORD); // //suspend azy (only the current call) during the treatment of the sql query //consequently azy can treat others client demand //when the query will be done, we will resume this instance azy_server_module_events_suspend(module); %>
|
Connect and execute the query
| server_code.c |
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
|
static void _callback(Esql_Res *res, void *data) { Test_Data *d = data; Azy_Value *v; Azy_Content *content; printf("Message saved!\n"); //disconnect from the mysql db esql_disconnect(d->e); //set the return value of the azy call //we return a copy of the message sent by the client v = Demo_Message_to_azy_value(d->msg); content = azy_server_module_content_get(d->m); azy_content_retval_set(content, v); // //resume the azy call instance azy_server_module_events_resume(d->m); //free our data free(d); } void _connect(Esql *e, void *data) { Test_Data *d = data; Esql_Query_Id id; //execute the query //the second argument is a data you can set and then retrieve it in _callabck() id = esql_query_args(e, d, "INSERT INTO messages values('%s');", d->msg->msg); if (!id) /**< queue up a simple query */ { fprintf(stderr, "Could not create query!\n"); ecore_main_loop_quit(); } esql_query_callback_set(id, _callback); }
|
Last Updated on Tuesday, 15 February 2011 21:02
Monday, 10 January 2011 21:51
ATTON Jonathan
There are no translations available. Azy is a library written by the company http://www.zentific.com/. The aim is to easily create a communication process between a client and a server like a webservice by using a RPC. Zentific need a asynchronous and scalable library, they was using libzxr (fork from libxr) but the library is synchronous and use Glib.

Azy is not really a part of the EFL project but use and is used by the EFL and is available in the official e-svn and from the zentific svn : http://svn.zentific.com/branches/azy (both are the same version)
Azy can works like a classic HTTP server and client. We will use it to create a client which will download a image from the server and then sent it to the server.
- Original file : initial.jpg
- Downloaded file : FILE_GET.jpg
- uploaded file : FILE_PUT.jpg
The source file : azy_dl_up.tar.gz
How to download a file from the server
We use the special method __download__. You can have only one __download__method, the namespace where you describe it is not important.
| services.azy |
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
|
__download__
<%
const char *namespace = NULL, *action = NULL;
const char *p, *p2;
const char *path;
Eina_Bool ret = EINA_FALSE;
int iid = 0;
//retrieve a copy of the path, we will work on it
path = eina_stringshare_add(azy_net_uri_get(net_));
//first char of the path must be '/'
EINA_SAFETY_ON_TRUE_GOTO(*path != '/', error);
//then we retrieve the namespace, the action and the id
p = strchr(path + 1, '/');
EINA_SAFETY_ON_NULL_GOTO(p, error);
/* grab namespace */
namespace = eina_stringshare_add_length(path + 1, p - path - 1);
EINA_SAFETY_ON_TRUE_GOTO((!namespace) || (!namespace[0]), error);
p2 = strchr(p + 1, '/');
EINA_SAFETY_ON_NULL_GOTO(p2, error);
action = eina_stringshare_add_length(p + 1, p2 - p - 1);
EINA_SAFETY_ON_TRUE_GOTO((!action) || (!action[0]), error);
errno = 0;
iid = strtol(p2 + 1, NULL, 10);
//If we have no ID, the path id is invalid
EINA_SAFETY_ON_TRUE_GOTO((errno == ERANGE && (iid == LONG_MAX || iid == LONG_MIN))
|| (errno && (!iid)), error);
//print the result
printf("namespace=%s, action=%s, id=%d\n", namespace, action, iid);
//create the response, here we set the HTTP code 200 because everything is ok
azy_net_code_set(net_, 200);
azy_net_header_set(net_, "Content-Type", "image/jpg");
//
if(!strcmp(namespace, "Photo")
&& !strcmp(action, "Get"))
{
//here we should use the iid to retrieve the image
//in this example we always return the same image
const char *file = "./initial.jpg";
/*
we are gonna use mmap to set the image as content
*/
//copy the file content and sent it
int fd = open(file, O_RDONLY);
struct stat st;
fstat(fd, &st); /* use fstat to get the size */
unsigned char *content_file = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
Azy_Net_Data azy_data;
azy_data.data = content_file;
azy_data.size = st.st_size;
azy_server_module_send(module, net_, &azy_data);
munmap(content_file, st.st_size); /* unmap mmapped data */
close(fd);
ret = EINA_TRUE;
//
}
eina_stringshare_del(path);
eina_stringshare_del(namespace);
eina_stringshare_del(action);
return ret;
error:
eina_stringshare_del(namespace);
eina_stringshare_del(action);
azy_net_code_set(net_, 404);
azy_server_module_send(module, net_, NULL);
return EINA_TRUE;
%>
|
The client is a bit more easy because we do not have to parse a url.
| client.c |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/**
* Yes we are connected !
*/
static Eina_Bool
connected(void *data, int type, Azy_Client *cli)
{
Azy_Client_Call_Id id;
//we ask the photo id to the server
azy_net_uri_set(azy_client_net_get(cli), "/Photo/Get/4");
azy_net_version_set(azy_client_net_get(cli), 0); /* disconnect after sending */
id = azy_client_blank(cli, AZY_NET_TYPE_GET, NULL, NULL);
azy_client_callback_set(cli, id, _get);
return ECORE_CALLBACK_RENEW;
}
|
| client.c |
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
|
/**
* This method is called when we have received the photo
*/
static Eina_Error
_get(Azy_Client *cli, Azy_Content *content, void *ret)
{
const char *FILEPATH = "./FILE_GET.jpg";
int fd;
int *map;
if (azy_content_error_is_set(content))
{
printf("Error encountered: %s\n", azy_content_error_message_get(content));
azy_client_close(cli);
ecore_main_loop_quit();
return azy_content_error_code_get(content);
}
//retrieve the size and the content
int size = azy_content_return_size_get(content);
unsigned char* data = (unsigned char*)azy_content_return_get(content);
printf("FILE RECEIVED\n");
printf("Write file in %s\n", FILEPATH);
printf("File size : %d\n", size);
//open the output file
fd = open(FILEPATH, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
//write at the end of the file in order to really create it.
lseek(fd, size-1, SEEK_SET);
write(fd, "", 1);
map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
//copy the content into the output file
memcpy(map, data, size);
munmap(map, size);
close(fd);
//upload the received file to the server
_upload(cli);
return AZY_ERROR_NONE;
}
|
Now run the server and the client:
1
2
3
4
|
- watchwolf::19:38:26 [64] -> ./server
__Download__
namespace=Photo, action=Get, id=4
|
1
2
3
4
|
- watchwolf::19:37:51 [26] -> ./client
FILE RECEIVED
Write file in ./FILE_GET.jpg
File size : 92722
|
Finally we can use diff to compare both file
1
2
3
4
5
|
- /home/Watchwolf/Projects/test/azy_dl_up -
- watchwolf::21:49:35 [20] -> diff FILE_GET.jpg initial.jpg
- /home/Watchwolf/Projects/test/azy_dl_up -
- watchwolf::21:50:23 [21] ->
|
Nothing is returned, both file are identical !
How to upload a file to the server
We use the special method __upload__. As for the __download__ method, the namespace where you describe the method is not important.
| services.azy |
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
|
/*
Methods called when azy receive a PUT HTTP request.
*/
__upload__
<%
const char *namespace = NULL, *action = NULL;
const char *p, *p2;
const char *path;
Eina_Bool ret = EINA_FALSE;
int iid = 0;
printf("__Upload__\n");
//retrieve a copy of the path, we will work on it
path = eina_stringshare_add(azy_net_uri_get(net_));
//first char of the path must be '/'
EINA_SAFETY_ON_TRUE_GOTO(*path != '/', error);
//then we retrieve the namespace, the action and the id
p = strchr(path + 1, '/');
EINA_SAFETY_ON_NULL_GOTO(p, error);
/* grab namespace */
namespace = eina_stringshare_add_length(path + 1, p - path - 1);
EINA_SAFETY_ON_TRUE_GOTO((!namespace) || (!namespace[0]), error);
p2 = strchr(p + 1, '/');
EINA_SAFETY_ON_NULL_GOTO(p2, error);
action = eina_stringshare_add_length(p + 1, p2 - p - 1);
EINA_SAFETY_ON_TRUE_GOTO((!action) || (!action[0]), error);
errno = 0;
iid = strtol(p2 + 1, NULL, 10);
//If we have no ID, the path id is invalid
EINA_SAFETY_ON_TRUE_GOTO((errno == ERANGE && (iid == LONG_MAX || iid == LONG_MIN))
|| (errno && (!iid)), error);
//print the result
printf("namespace=%s, action=%s, id=%d\n", namespace, action, iid);
//create the response, here we set the HTTP code 200 because everything is ok
azy_net_code_set(net_, 200);
azy_net_header_set(net_, "Content-Type", "image/jpg");
//
if(!strcmp(namespace, "Photo")
&& !strcmp(action, "Put"))
{
//save the image received
const char *FILEPATH = "./FILE_PUT.jpg";
//retrieve the size and the content
Azy_Net_Data *data = azy_server_module_recv_get(module);
printf("FILE RECEIVED\n");
printf("Write file in %s\n", FILEPATH);
printf("File size : %d\n", (int)data->size);
//open the output file
int fd = open(FILEPATH, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
//write at the end of the file in order to really create it.
lseek(fd, data->size-1, SEEK_SET);
write(fd, "", 1);
int *map = mmap(0, data->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
//copy the content into the output file
memcpy(map, data->data, data->size);
munmap(map, data->size);
close(fd);
}
eina_stringshare_del(path);
eina_stringshare_del(namespace);
eina_stringshare_del(action);
return ret;
error:
eina_stringshare_del(namespace);
eina_stringshare_del(action);
azy_net_code_set(net_, 404);
azy_server_module_send(module, net_, NULL);
return EINA_TRUE;
%>
|
The client send the file
| client.c |
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
|
//this code is closed to the code of __download__
const char *file = "./FILE_GET.jpg";
printf("Sending the file %s\n", file);
/*
we are gonna use mmap to set the image as content
*/
//copy the file content and sent it
int fd = open(file, O_RDONLY);
struct stat st;
fstat(fd, &st); /* use fstat to get the size */
unsigned char *content_file = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
Azy_Net_Data azy_data;
azy_data.data = content_file;
azy_data.size = st.st_size;
//upload
azy_net_uri_set(azy_client_net_get(cli), "/Photo/Put/4");
Azy_Client_Call_Id id = azy_client_put(cli, &azy_data, NULL);
azy_client_callback_set(cli, id, _put);
//
munmap(content_file, st.st_size); /* unmap mmapped data */
close(fd);
|
Execute the server and the client. The result is the file FILE_PUT.jpg.
Last Updated on Wednesday, 12 January 2011 20:31
Friday, 07 January 2011 14:54
ATTON Jonathan
There are no translations available. The propertie "tween" in Edje allows to easily create a animation with a list of images. We will create the animation of a explosion.
source : explode.tar.gz
We are doing all the work in edje.
| explode.edc |
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
|
collections {
group {
name: "explode";
images {
image: "explode1_1.png" COMP;
image: "explode1_2.png" COMP;
image: "explode1_3.png" COMP;
image: "explode1_4.png" COMP;
image: "explode1_5.png" COMP;
image: "explode1_6.png" COMP;
image: "explode1_7.png" COMP;
image: "explode1_8.png" COMP;
}
parts {
part {
name: "explode1";
type: IMAGE;
mouse_events: 0;
description {
state: "default" 0.0;
//the part is not visible
visible: 0;
aspect: 1.0 1.0;
//the animation start and end with the image "normal"
//in our case this image is empty.
image {
tween: "explode1_1.png";
tween: "explode1_2.png";
tween: "explode1_3.png";
tween: "explode1_4.png";
tween: "explode1_5.png";
tween: "explode1_6.png";
tween: "explode1_7.png";
normal: "explode1_8.png";
}
}
description {
//the part is visible
state: "explode" 0.0;
inherit: "default" 0.0;
visible: 1;
color: 255 255 255 255;
}
}
}
program {
name: "start_explode";
//the signal "show" is sent by evas_object_show()
//consequently when the objet is show, this program is called
signal: "show";
//we set the part to the state "explode"
//when the part will be set the animation will
//automaticaly start
action: STATE_SET "explode" 0.0;
transition: LINEAR 0.5;
target: "explode1";
after: "end_explode";
}
//when the animation is finished we send the signal
//"end_explode" to the program
program {
name: "end_explode";
action: SIGNAL_EMIT "end_explode" "";
}
}
}
|
Use edje_player to display the animation. This program is available in the svn of enlightenment or maybe in your packages.
1
|
edje_player explode.edj
|
Last Updated on Friday, 07 January 2011 15:13
Wednesday, 05 January 2011 12:57
ATTON Jonathan
There are no translations available.
Eet allow to save and load data easily.
File: eet.tar.gz
How to use EET to save data
Creating our data
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
|
//Describe a dog
typedef struct
{
const char *name;
const char *race;
} Dog;
//describe 2 list of dogs
typedef struct
{
Eina_List *dogs_I_have;
Eina_List *dogs_I_add;
} Dogs;
// Add a dog in the list
Eina_List *dog_add(Eina_List *l, const char* name, const char* race)
{
Dog *dog = calloc(1, sizeof(Dog));
dog->name = eina_stringshare_add(name);
dog->race = eina_stringshare_add(race);
l = eina_list_append(l, dog);
return l;
}
//Free a dog
void dog_free(Dog *dog)
{
eina_stringshare_del(dog->name);
eina_stringshare_del(dog->race);
free(dog);
}
//print a dog
void dog_print(Dog *dog)
{
printf("\tDog : %s, Race : %s\n", dog->name, dog->race);
}
|
Create the eet descriptor
Eet need to know what we which to save, the descriptor does this job.
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
|
//create the eet descriptor of the struct Dog
static Eet_Data_Descriptor * dog_edd_new()
{
Eet_Data_Descriptor *edd;
Eet_Data_Descriptor_Class eddc;
EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Dog);
//set to null because wu use eina_stringshare
eddc.func.str_direct_alloc = NULL;
eddc.func.str_direct_free = NULL;
edd = eet_data_descriptor_file_new(&eddc);
EET_DATA_DESCRIPTOR_ADD_BASIC(edd, Dog, "name", name, EET_T_STRING);
EET_DATA_DESCRIPTOR_ADD_BASIC(edd, Dog, "race", race, EET_T_STRING);
return edd;
}
//create the eet descriptor of the struct Dogs
static Eet_Data_Descriptor * dogs_edd_new()
{
Eet_Data_Descriptor *edd;
Eet_Data_Descriptor_Class eddc;
Eet_Data_Descriptor *edd_dog = dog_edd_new();
EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Dogs);
//set to null because wu use eina_stringshare
eddc.func.str_direct_alloc = NULL;
eddc.func.str_direct_free = NULL;
edd = eet_data_descriptor_file_new(&eddc);
EET_DATA_DESCRIPTOR_ADD_LIST(edd, Dogs, "dog_I_have", dogs_I_have, edd_dog);
EET_DATA_DESCRIPTOR_ADD_LIST(edd, Dogs, "dog_I_add", dogs_I_add, edd_dog);
return edd;
}
|
Then we create our data
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//create
Dogs *dogs = calloc(1, sizeof(Dogs));
dogs->dogs_I_have = NULL;
dogs->dogs_I_add = NULL;
dogs->dogs_I_have = dog_add(dogs->dogs_I_have, "Mystrale", "Shepherd german");
dogs->dogs_I_have = dog_add(dogs->dogs_I_have, "fiona", "Malinois");
dogs->dogs_I_add = dog_add(dogs->dogs_I_add, "Effy", "Czechoslovakian Wolfdogs");
dogs->dogs_I_add = dog_add(dogs->dogs_I_add, "Sai", "No race");
//
|
Saving data
1
2
3
4
5
6
7
8
|
Eet_Data_Descriptor *edd = dogs_edd_new();
Eet_File *f = eet_open("./dogs.eet", EET_FILE_MODE_WRITE);
eet_data_write(f, edd, "key/dogs", dogs, 0);
eet_close(f);
eet_data_descriptor_free(edd);
|
Loading data
1
2
3
4
5
6
7
8
|
edd = dogs_edd_new();
f = eet_open("./dogs.eet", EET_FILE_MODE_READ);
dogs = eet_data_read(f, edd, "key/dogs");
eet_close(f);
eet_data_descriptor_free(edd);
|
Last Updated on Wednesday, 05 January 2011 13:58
|
|