C#

You are currently browsing articles tagged C#.

This will not serve as a comprehensive tutorial in developing services and clients for Axis2/C, but rather intended to provide some help for starters.

Apache Axis2/C is an engine for web services developed in C – there’s another implementation in Java. You can find an illustration of SOA here.

You first need to download and install Apache Axis2/C 1.3.0. You can download the binaries or sources from the Apache Axis2/C website. It would be easier to install the binaries if you are a beginner.

So let’s get started with developing a service. We’ll create a service to sort a set of integers in ascending or descending order.

Service

The service should have two functions:

  • axis2_get_instant – creates the service skeleton
  • axis2_remove_instant – removes the instant

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
AXIS2_EXPORT int
  axis2_get_instance(axis2_svc_skeleton_t **inst,
                     const axutil_env_t *env)
{
 *inst = axis2_sort_create(env);
 if (!(*inst))
  return AXIS2_FAILURE;
 
 return AXIS2_SUCCESS;
}
 
AXIS2_EXPORT int
  axis2_remove_instance(axis2_svc_skeleton_t *inst,
                        const axutil_env_t *env)
{
 if (inst)
  return AXIS2_SVC_SKELETON_FREE(inst, env);
 
 return AXIS2_FAILURE;
}
 
/* Create the service skeleton */
axis2_svc_skeleton_t *
  axis2_sort_create(const axutil_env_t *env)
{
 axis2_svc_skeleton_t *svc_skeleton = NULL;
 svc_skeleton = AXIS2_MALLOC(env->allocator,
                             sizeof(axis2_svc_skeleton_t));
 
 svc_skeleton->ops = &sort_svc_skeleton_ops_var;
 
 svc_skeleton->func_array = NULL;
 
 return svc_skeleton;
}

axis2_get_instant should create axis2_svc_skeleton struct with corresponding functions assigned. These functions are

  1. init – initializes the skeleton
  2. invoke – invokes the service
  3. on_fault – this is called when an fault is detected
  4. free – frees the skeleton instant

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
/* Functions for axis2_svc_skeleton */
int AXIS2_CALL
  sort_free(axis2_svc_skeleton_t *svc_skeleton,
            const axutil_env_t *env);
 
axiom_node_t* AXIS2_CALL
  sort_invoke(axis2_svc_skeleton_t *svc_skeleton,
              const axutil_env_t *env,
              axiom_node_t *node,
              axis2_msg_ctx_t *msg_ctx);
 
int AXIS2_CALL
  sort_init(axis2_svc_skeleton_t *svc_skeleton,
            const axutil_env_t *env);
 
axiom_node_t* AXIS2_CALL
  sort_on_fault(axis2_svc_skeleton_t *svc_skeli,
                const axutil_env_t *env, axiom_node_t *node);
 
/* Skeleton */
static const axis2_svc_skeleton_ops_t sort_svc_skeleton_ops_var = {
 sort_init,
 sort_invoke,
 sort_on_fault,
 sort_free
};

Now comes the main part of the service – implementation of the invoke function.

This function will be called with a reference to request. The XML request will be passed as a axiom_node_t, and you can navigate through it easily using functions axiom_node_get_first_child and axiom_node_get_next_sibling.

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
/* Process the request */
axiom_node_t* AXIS2_CALL
  sort_invoke(axis2_svc_skeleton_t *svc_skeleton,
               const axutil_env_t *env,
               axiom_node_t *node,
               axis2_msg_ctx_t *msg_ctx)
{
 return axis2_sort(env, node);
}
 
 /* Process the request */
axiom_node_t *
  axis2_sort(const axutil_env_t *env, axiom_node_t *node)
{
 AXIS2_ENV_CHECK(env, NULL);
 
 if(!node) return sort_error(env);
 
 axiom_node_t *order_node = axiom_node_get_first_child(node, env);
 axis2_char_t *order_str = get_string(order_node, env);
 
 if(!order_str) return sort_error(env);
 
 axiom_node_t *list = axiom_node_get_next_sibling(order_node, env);
 
 if(!list) return sort_error(env);
 
 axiom_node_t* current = axiom_node_get_first_child(list, env);
 
 if(!current) return sort_error(env);
 
 int i;
 
 a = AXIS2_MALLOC(env->allocator,
                  sizeof(long int) * MAX);
 
 for(i = 0; i < MAX && current; i++)
 {
  axis2_char_t *str = get_string(current, env);
  a[i] = strtol(str, NULL, 10);
 
  current = axiom_node_get_next_sibling(current, env);
 }
 
 int N = i;
 
 if(axutil_strcmp(order_str, "asc"))
  return build_sort_response(env, N, 1);
 else if(axutil_strcmp(order_str, "des"))
  return build_sort_response(env, N, -1);
 else
  return sort_error(env);
}

After parsing the input you should calculate and prepare the response of the service.

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
/* Generate the response */
axiom_node_t *
  build_sort_response(const axutil_env_t *env, int N, int order)
{
 axis2_char_t value_str[255];
 axiom_node_t *main_node;
 int i, j;
 
 for(i = 0; i < N; i++)
  for(j = 1; j < N; j++)
   if((a[j] - a[j-1]) * order > 0)
 {
  long int temp = a[j];
  a[j] = a[j-1];
  a[j-1] = temp;
 }
 
 axiom_namespace_t *ns1 = axiom_namespace_create(env, "http://axis2/test/sort", "ns1");
 
 axiom_element_create(env, NULL, "result", ns1, &main_node);
 
 for(i = 0 ; i < N; i++)
 {
  axiom_node_t *value_node;
 
  sprintf(value_str, "%ld", a[i]);
 
  axiom_element_t *value_ele = axiom_element_create(env, main_node, "value", NULL, &value_node);
  axiom_element_set_text(value_ele, env, value_str, value_node);
 }
 
 AXIS2_FREE(env->allocator, a);
 
 return main_node;
}

That’s it now we have created the service, but we need to create the service descriptor services.xml

1
2
3
4
5
<service name="sort">
 <parameter name="ServiceClass" locked="xsd:false">sort</parameter>
 <description>Sort service example.</description>
 <operation name="sort"/>
</service>

Here’s the code of the service: sort_service.c and the descriptor: services.xml.

Then compile the service using gcc

gcc -shared -olibsort.so -I$AXIS2C_HOME/include/axis2-1.3.0/ -L$AXIS2C_HOME/lib -laxutil -laxis2_axiom -laxis2_parser -laxis2_engine -lpthread -laxis2_http_sender -laxis2_http_receiver sort_service.c

The environment variable AXIS2C_HOME should be set to the axis2 installation folder.

Copy libsort.so and services.xml to folder AXIS2C_HOME/services/sort/.

Now start the axis server or restart if it’s already running, buy executing axis2_http_server. And check if the service is correctly deployed by checking http://localhost:9090/axis2/services.

Now that the service is running fine we can start creating the client.

Client

First we should set the properties and create the svc client

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
 const axis2_char_t *client_home = NULL;
 axis2_svc_client_t* svc_client = NULL;
 axiom_node_t *payload = NULL;
 axiom_node_t *ret_node = NULL;
 
 /* Create environment */
 axutil_env_t *env = axutil_env_create_all("sort_client.log", AXIS2_LOG_LEVEL_TRACE);
 
 /* Location of the service */
 axis2_char_t *address = "http://localhost:9090/axis2/services/sort";
 printf("Using address : %s\n", address);
 
 /* Creating options */ 
 axis2_options_t *options = axis2_options_create(env);
 axis2_endpoint_ref_t* endpoint_ref = axis2_endpoint_ref_create(env, address);
 axis2_options_set_to(options, env, endpoint_ref);
 
 /* Creating svc client */
 client_home = AXIS2_GETENV("AXIS2C_HOME");
 if (!client_home && !strcmp(client_home, ""))
 {
  printf("AXIS2C_HOME not set\n");
  return -1;
 }
 svc_client = axis2_svc_client_create(env, client_home);
 
 if (!svc_client)
 {
  printf("Error creating service client\n");
  AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Stub invoke FAILED: Error code:"
    " %d :: %s", env->error->error_number,
    AXIS2_ERROR_GET_MESSAGE(env->error));
  return -1;
 }
 axis2_svc_client_set_options(svc_client, env, options);

Then build the request.

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
axiom_node_t *
   build_om_request(const axutil_env_t *env)
{
 axiom_node_t *main_node;
 axiom_node_t *order_node;
 axiom_node_t *values_node;
 axis2_char_t value_str[255];
 int i;
 
 
 axiom_namespace_t *ns1 = axiom_namespace_create(env, "http://axis2/test/sort", "ns1");
 
 axiom_element_create(env, NULL, "sort", ns1, &main_node);
 
 axiom_element_t *order_ele = axiom_element_create(env, main_node, "order", NULL, &order_node);
 axiom_element_set_text(order_ele, env, "des", order_node);
 
 axiom_element_t *values_ele = axiom_element_create(env, main_node, "values", NULL, &values_node);
 
 int N = 20;
 
 for(i = 0 ; i < N; i++)
 {
  axiom_node_t *value_node;
 
  sprintf(value_str, "%ld", i + 100);
 
  axiom_element_t *value_ele = axiom_element_create(env, values_node, "value", NULL, &value_node);
  axiom_element_set_text(value_ele, env, value_str, value_node);
 }
 
 return main_node;
}

Send the request.

1
ret_node = axis2_svc_client_send_receive(svc_client, env, payload);

Here’s the code of the service: sort_client.c. Now compile the client.

gcc -o sort_client -I$AXIS2C_HOME/include/axis2-1.3.0/ -L$AXIS2C_HOME/lib -laxutil -laxis2_axiom -laxis2_parser -laxis2_engine -lpthread -laxis2_http_sender -laxis2_http_receiver sort_client.c -ldl -Wl,--rpath -Wl,$AXIS2C_HOME/lib

Run it

Run the client to see what happens.

What happens if the client sends an empty request?

Tags: , , , ,

This is a getting started tutorial to Windows Communication Foundation (WCF) Services. I have discussed a simple service to calculate the area of a rectangle; yup, very simple ;)

1. First you need to download and install Visual Studio 2005 extensions for .NET Framework 3.0 (Windows Workflow Foundation).

Oops, looks like it needs a windows validation (bit of a problem if you have a pirate copy :P )

2. Once the installation is complete, start Visual Studio to create the project.

Goto File->New->Web Site, and you’ll see a new template called WFC Service.

Click OK to create an empty WCF Service.

3. Time to get started with the coding

Service.svc

<% @ServiceHost Language=C# Debug="true" Service="GeometryService" CodeBehind="~/App_Code/Service.cs" %>

Only a small change here – the name of the service

Web.config

<?xml version="1.0"?>
 
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
  <system.serviceModel>
    <services>
      <!-- Before deployment, you should remove the returnFaults behavior configuration to avoid disclosing information in exception messages -->
      <service name="GeometryService" behaviorConfiguration="GeometryServiceBehavior">
        <endpoint contract="IGeometryService" binding="wsHttpBinding"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="GeometryServiceBehavior" >
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
 
  <system.web>
    <compilation debug="true"/>
  </system.web>
 
 </configuration>

Enable metadata publishing and change the name of the service.

Service.cs

using System;
using System.ServiceModel;
using System.Runtime.Serialization;
 
// A WCF service consists of a contract (defined below as IMyService, DataContract1), 
// a class which implements that interface (see MyService), 
// and configuration entries that specify behaviors associated with 
// that implementation (see <system.serviceModel> in web.config)
 
[ServiceContract()]
public interface IGeometryService
{
 [OperationContract]
 int GetArea(Rectangle rect);
 [OperationContract]
 int GetPerimeter(int width, int height);
}
 
public class GeometryService : IGeometryService
{
 public int GetArea(Rectangle rect)
 {
  return rect.Area();
 }
 
 public int GetPerimeter(int width, int height)
 {
  Rectangle rect = new Rectangle(width, height);
  return rect.Perimeter();
 }
}
 
[DataContract]
public class Rectangle
{
 int width;
 int height;
 
 public Rectangle(int width, int height)
 {
  Width = width;
  Height = height;
 }
 
 [DataMember]
 public int Width
 {
  get { return width; }
  set { width = value < 0 ? 0 : value; }
 }
 
 [DataMember]
 public int Height
 {
  get { return height; }
  set { height = value < 0 ? 0 : value; }
 }
 
 public int Area()
 {
  return Height * Width;
 }
 
 public int Perimeter()
 {
  return 2 * (Height + Width);
 }
}

This is the code for the service.

4. Run the service – click F5 :)

This is what you get on the browser. So, lets do what they want.

5. Run the following on you command on Visual Studio Command Prompt

svcutil.exe http://localhost:1479/GeoWFCService/Service.svc?wsdl

Watch out, the path might be different ;) . And if you don’t know what Visual Studio command prompt is

6. Let’s create the client

Create (or rather add) a new project; this could even be a website

7. Then run the service – Ctrl + F5

8. Add a web reference to the service

8. Copy the GeometryService.cs created by svcutil in step 5 to the clients working (main) folder and add them to the project

You should also copy the configurations of output.config created by svcutil to app.config

9. Again time for coding

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
namespace GeoWFCClient
{
 public partial class Form1 : Form
 {
  public Form1()
  {
   InitializeComponent();
  }
 
  private void calculateButton_Click(object sender, EventArgs e)
  {
   GeometryServiceClient gsclient = new GeometryServiceClient();
 
   /* The overloaded constructor of Rectangle is not present here */
   Rectangle rect = new Rectangle();
 
   rect.Width = Int32.Parse(widthTextBox.Text);
   rect.Height = Int32.Parse(heightTextBox.Text);
 
   areaTextBox.Text = gsclient.GetArea(rect).ToString();
   perimeterTextBox.Text = gsclient.GetPerimeter(rect.Width, rect.Height).ToString();
  }
 }
}

Set the textboxes and labels with appropriate names.

10. When you compile you’ll get a set of error because some assemblies are not referenced. Add references to them

11. Now see how it works

Start the service if haven’t done so already, and run the client

Wow! It works :D

Ok, hope you learned something with this and I got to go study for exam :P

Tags: , , , , ,

Bulk Insert

Have you ever wanted to insert a large set of records to a MySQL database? One way to do is to insert row by row, but this will take a looong time. I was also faced with this problem last week since I had to insert a large set of data to a table from ASP.NET.

First of all I used a transaction.

OdbcConnection con = new OdbcConnection("...");
con.Open();
OdbcTransaction trans = con.BeginTransaction();
for(int i = 0; i < 10000; i++)
{
 OdbcCommand cmd = new OdbcCommand("INSERT ... ");
 cmd.ExecuteNonQuery();
}
trans.Commit();
con.Close();

This increase the speed by about three times and it worked in a reasonable time for small data sets, but not for very large ones. Another advantage with this is that if the insertion fails at middle of the transaction none of the data will be inserted.

Then, as a friend of mine suggested me I used the SQL command LOAD DATA INFILE. I wrote all the records to a comma separated text file and the used LOAD DATA INFILE to insert the data to the database; this ran very fast since al the data is added at once as a batch, communications with the database are less and table indexes are updated once for all the data.

LOAD DATA INFILE 'sample.txt' INTO TABLE table_name
 FIELDS TERMINATED BY ',' ENCLOSED BY '"'
 (field_list);

The text file should not contain any spaces between ,’s (commas). Although this works fine, you won’t be able to use it if you don’t have proper rights set for the MySQL user id; this might happen if you’re using a commercial server, where you’re unable to alter the permissions.

At last you can use large packets of SQL statements; that is, break the large data set into small (not so small – about 1 megabytes) sets or records, and insert each of these small data sets one by one (or using a transaction).

INSERT INTO table_name (field_list) VALUES
 ('data', 'data', ... )
 ('data', 'data', ... )
 ...
 ('data', 'data', ... )

You cannot use very large packets since there is a limit to the packet size.

 

Tags: , , ,