El blog de Carlos Ble

BrainStream.Write(Posts)

Splash screen in Gtk#

Filed under: Software Development — Carlos Ble at 2:16 pm on Monday, September 1, 2008

This sample shows how to create a splash screen using Monodelop and Gtk#. Create a new Window. Within its properties, unset the Decorator so that it won't have any title bar. Add a HBox or VBox inside the window with only one placeholder. Add a button inside and select Custom in the Button Type under the Button Properties. Add an image inside the button.
Source code that makes it work:

 
using System;
using System.Threading;
 
namespace sigaDesktopClient
{
	public partial class MainWindow : Gtk.Window
	{
 
		public MainWindow() :
				base(Gtk.WindowType.Toplevel)
		{
			this.Build();
			ThreadStart tStart = new ThreadStart(this.EndSplash);
			Thread t = new Thread(tStart);
			t.Start();
		}
 
		public void EndSplash()
		{
			Thread.Sleep(2000);
			Gtk.Application.Invoke(
	                delegate (object sender, EventArgs args)
                    {
                         StartApplication();
                    }
			);
		}
 
		private void StartApplication()
		{
                        // WRITE THE CODE TO OPEN THE MAIN GUI HERE
			this.Hide();
		}
 
		protected virtual void OnClick (object sender, System.EventArgs e)
		{
			StartApplication();
		}
 
		protected virtual void OnEntered (object sender, System.EventArgs e)
		{
			StartApplication();
		}
	}
}
 

Consuming Python Web Services with C#

Filed under: Software Development — Carlos Ble at 1:36 pm on Friday, August 29, 2008

Consuming Python Web Services written and deployed with Soaplib is possible using C# and Mono. I haven't try on .Net yet but if it works on Mono it definitively will work on .Net. You can read about Soaplib in previous posts.
First of all, write a web service that returns a single string and another that returns an array of strings:

 
  @soapmethod(String, _returns=String)
  def srv_getServerData(self, serverName):
      return str(Server.objects.filter(nombre=serverName).values())
 
  @soapmethod(_returns=Array(String))
  def srv_getServersList(self):
      results = []
      for servicesValues in Service.objects.values():
          results.append(str(servicesValues))
      return results
 

Second: Run the web server and generate the WSDL file. I do that using a Python client.

 
def getWSDL():
    wsdl = getClientInfoService().server.wsdl("https://" +
                Server.getServerPublicIp() + ':' +
                constants.webServicesPort + constants.serviceUrl)
    fileName = '/tmp/service.wsdl'
    wsdlFile = open(fileName, 'w')
    wsdlFile.write(wsdl)
    wsdlFile.close()
    print "File generated:", '/tmp/service.wsdl'
 

Third: Generate the C# proxy reading the WSDL. We need the wsdl tool (package mono-1.0-devel on Ubuntu).
wsdl service.wsdl
That will create the file Service.cs. In this file there are several typecasts that are not working properly. In order to get the data from the service I modify it using sed.

 
sed 's/return ((.*Response)(results\[0\]))/return ((System.Xml.XmlNode)results\[0\])/g' Service.cs > tmp1
sed 's/public srv_.*Response/public System.Xml.XmlNode/g' tmp1 > Service.cs
 

Note that my services methods start with the "srv_" prefix, so I use that in the regex above to match them.
Fourth: C# code to get that from the services:

 
        // method 1:
        srv_getServerData y = new srv_getServerData();
	y.serverName = "pepino";
	Service client = new Service();
	System.Xml.XmlNode res = client.srv_getServerData(y).ChildNodes[0];
        String result = String.Empty;
	result = res.InnerText;
 
        // method 2:
	Service client = new Service();
	srv_getServersList x = new srv_getServersList();
	System.Xml.XmlNodeList result = client.srv_getServersList(x).ChildNodes;
        String result = String.Empty;
	foreach (System.Xml.XmlNode n in result)
	      result += '\n' + n.InnerText;
 

It works!!!

NUnit on Mono: TypeLoadException

Filed under: Software Development — Carlos Ble at 10:25 am on Thursday, August 28, 2008

MonoDevelop 1.0 is available for Ubuntu 8.04, that is great news!. Mono 1.2.6 is the current version for this distro too. MonoDevelop 1.0 is 100 times more stable than 0.14 and it makes me feel productive now. It almost does not crash, the gui layout is very nice and the autocompletion works pretty well. I discarded OpenSuse eventually because it is sooooo slow, soooo heavy. By the way, Ubuntu Hardy works very well on my desktop computer but it makes my laptop crash very often :-(.
Mono community seems very small to me. I'd say people is using Mono to run their .Net apps on Linux but I don't see many people developing on Mono directly. Its development is in progress though, so good news again.

The tougher problem I've found so far is that NUnit wasn't working right. One of my unit tests is loading an assembly using Assembly.Load and then trying to get its types with assembly.GetTypes() and NUnit was throwing TypeLoadException. The solution has been to download NUnit 2.4.8 from the web, unzip it, change the nunit-console.exe.config to set encoding=utf-8 in the first line, and copy all the files under the bin folder to /usr/lib/nunit/. It is working fine, the test works.

The other problem is that adding an App.config file to my project makes gmcs (mono compiler) fail with "Parsing error". I have no clue, what's going on here?.

Traducción: Los Mocks no son Stubs

Filed under: Software Development — Carlos Ble at 2:24 pm on Thursday, July 31, 2008

Acabo de terminar la traducción de éste excelente ensayo de Martin Fowler. Quizás sea la primera de una serie de traducciones pensadas para acercar buena literatura inglesa sobre software a hispano hablantes que de otra manera jamás leerían tales artículos. Cualquier feedback sobre si os parece interesante la traducción o comentarios son bienvenidos.

Aquí lo teneis: http://carlosble.com/traducciones/mocksArentStubs.html

Two years blogging

Filed under: Uncategorized — Carlos Ble at 8:59 am on Friday, July 25, 2008

Dos años escribiendo este blog!. This month is the second birthday of this blog. Time flies. Having a technical blog is being very positive as it is a place to store information that I might need later. It helps people sometimes, is good for my CV and it has helped me to get a job too. Writing in english is a challenge because here in Spain I don't talk in english so I forget many things and I get to lots of doubts. I make lots of mistakes by the way. The stats says that my readers are mainly english speaking people so it is worthwhile to write in this language although colleagues over here complain about that. .Net and C# is the topic people read the most. Wordpress stats says that there are between 20 and 90 readers everyday.

Some stats:

Top Posts

WPF TreeView, 663 views

Samba, dominios, impresoras y políticas con Windows XP, 349 views

Could not load type X from assembly Y, 313 views

User Interface Design for Programmers

Filed under: Now Reading — Carlos Ble at 2:14 pm on Thursday, July 24, 2008

User Interface Design for Programmer is a masterpiece written by Joel Spolsky in 2001. Although might be considered old, it is still absolutely useful and great. Some chapters are available online. I'd recommend this book to any software developer and also to advanced users because it is not technical, it is just a lot of common sense driven by example. This book is ideal for QA engineers and other people who review applications. Every paragraph is interesting.
Thanks for this book Joel, it is really good indeed. Thanks to Eladio for the gift ;-)

Hammett is joining Microsoft

Filed under: Uncategorized — Carlos Ble at 2:53 pm on Friday, July 18, 2008

Awesome news! Hamilton Verissimo is joining Microsoft. These are just great news because another great open source developer is joining the Redmon company. The CastleProject should be mature enough to survive without Hammett although he says he can still work on it. Best luck mate, congratulations.
I hope Microsoft develop more open source projects like IronPython and the super hackers they are hiring leverage the company with the open source spirit.
By the way, regards to my colleagues Atilla and Ben who joined MS last year in Dublin :-)

Despues del Taller

Filed under: Free/Libre Software — Carlos Ble at 7:58 pm on Thursday, July 17, 2008

Al final resultó que hubieron 10 asistentes y con un nivel muy bueno. Todos siguieron el ejemplo y consiguieron ejecutar la aplicación que hicimos con MonoDevelop, un pequeño "encriptador". Gracias a Berto y Choms que estuvieron de apoyo y al los asistentes por estar tan atentos.
Algunos hasta ampliaron el programa. Me sorprendió que siendo tan jóvenes tuvieron tanto nivel. Parece que MonoDevelop gustó bastante a pesar de que teniamos la version 0.14 que es la que hay para Ubuntu y que petaba de vez en cuando. La version 1.0 lleva meses fuera pero sólo está para Suse :-(

Gracias a Innova7 por invitarnos y cedernos un stand, a Rodrigo por su energía y a mis compañeros Quique, Carlos, Ancor y Pablo por la puesta a punto de las máquinas.

Taller de C# y Mono en TenerifeLanParty

Filed under: Free/Libre Software — Carlos Ble at 12:46 pm on Tuesday, July 15, 2008

La Oficina de Software Libre de la ULL tendrá un stand en la TenerifeLanParty de este 2008. Por motivos de trabajo, de miercoles a viernes estaré por allí ayudando con los talleres y el jueves a las 16h me han colocado un taller de Lenguaje C# y Mono que impartiré en caso de que hayan suficientes asistentes. Basicamente aplicaciones de escritorio Gtk#. Por experiencia en este tipo de eventos imagino que no habrá público en los talleres porque la gente suele ir a jugar a videojuegos. Para dar conferencias o talleres técnicos con éxito hay que ofrecer comida gratis, poner los horarios con mucho tiempo de antelación, darle mas publicidad y estar avalado por una firma grande como Microsoft, Sun, IBM, o alguna de estas. Y si ademas se cobra una buena pasta por la entrada, entonces ya es la bomba.
Pero bueno, allí estaremos para echarnos unas risas.

TDD sample iteration II

Filed under: Software Development — Carlos Ble at 12:35 pm on Friday, July 11, 2008

This is the next post to TDD sample iteration where I was trying to manage system daemons through a python web service. For some reason I thought I did the proof of concept so that restarting a service was working properly, but I definitively didn't. I didn't realize that restarting a daemon from the web service causes the daemon to run in the tcp port used by the web service, which hangs it on. It is the same problem that seems to happen to this guy.
So, despite of having several unit tests for this case, some of them even mocking objects, the lack of a real acceptance tests misleaded to believe that code was working fine.
From this I've learned that apart from the unit tests, it is important to write some tests that call the function on an actual scenario where execution changes the state of the system or database. Running such tests from a controlled environment where changes can be reverted completes the confidence you have in your software. The last step would be to write acceptance tests that interact with the GUI so that once the QA guys know how to use the application, they can write tests that click on buttons, fill forms, and so on. This increases the speed of the regresion tests and make life of QA engineers much more exciting.

Now, regarding the problem I was having, it turns out that susprocess.Popen creates a fork but the child process shares the resources with its parent. So restarting cron daemon (which does not need any tcp port), from the web service running on a tcp port, leads the cron daemon to run in the same socket. Thanks to my friend Rene, who gave me this script (and thanks to the author, of course), I've managed to fix the problem.

 
class Service(models.Model):
  #..... some code here
    def _changeServiceStatus(self, command):
        return Utils.runExternalCmdOnSeparateProcess(self.command, command)
 

Utils module:

 
def runExternalCmdOnSeparateProcess(command, args, timeOut=20):
    """
    Use this to manage daemons only. As a side efect, this method
    generates a zombie process that keeps in the process table till
    the time the parent process dies. The zombie process does not
    waste any resources though. Tipical command:
    /etc/init.d/apache2 restart
    """
    tmpOutputFileName = '/tmp/' + str(random.random()) # output file
    if not timeOut:
        timeOut = 30
    pid1 = os.fork()
    if not pid1:     # if this is the new process (process 1)
       os.setsid() # detach from parent
       pid2 = os.fork() # second fork
       if pid2:  # if it is the process 1, exit
          os._exit(0) # this leads to a zombie process that remains till process 0 dies
       else:     # if it is the process 2
          closeAllResources()
          execInfo = runCommand(command + " " + args)  # do the job
          setOutputInfo(tmpOutputFileName, execInfo)   # save the output of the job
          os._exit(0) # just finish, do not return anything
    else: # main thread, the one that starts the call (process 0)
        i = 0
        while not os.path.exists(tmpOutputFileName):
            time.sleep(1) # while the process 2 does not create the output file, wait
            i += 1
            if i > timeOut:
                return ['5', 'Operation time out']
        isFileClosed = "0"
        i = 0
        time.sleep(1)
        while isFileClosed == "0": # while the grandchild does not close the output file wait
            execInfLsof = runCommand("lsof" + " " + tmpOutputFileName)
            isFileClosed = execInfLsof[0]
            if isFileClosed <> "0":
               return getOutputInfo(tmpOutputFileName)
            else:
               time.sleep(1)
               i += 1
               if i > timeOut:
                   return ['5', 'Operation time out']
 
def closeAllResources():
    """
    Close all the resources hold by the current process
    """
    maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
    if (maxfd == resource.RLIM_INFINITY):
         maxfd = MAXFD
    for fd in range(0, maxfd):
        try:
            os.close(fd)
        except OSError:    # ERROR, fd wasn't open to begin with (ignored)
            pass
    os.open(os.devnull, os.O_RDWR)    # standard input (0)
    os.dup2(0, 1)            # standard output (1)
    os.dup2(0, 2)            # standard error (2)
 
def runCommand(cmd):
    retCode = 0
    errMessage = ''
    try:
      retInfo = commands.getstatusoutput(cmd)
      retCode = retInfo[0]
      errMessage = retInfo[1]
      # some commands fail but they still return 0, check that
      if retCode == 0 and isActualOutcomeFailure(errMessage):
          retCode = 3
    except Exception, e:
      Logger.getInstance().logException(e)
      retCode = 4
      errMessage = e.message
    return [str(retCode), preprocessReturnMessage(str(errMessage))]
 

.

 
def isActualOutcomeFailure(msg):
    """
     Some commands seem to return 0 although the operation wasn't success. Try to fix that.
     To-do: Add as many errors as possible to the errors array below.
    """
    errors = ['Permission denied', 'Permiso denegado']
    for err in errors:
        if re.search(err, msg):
            return True
    return False
 
def preprocessErrorMessage(msg):
    """
     The soaplib does not accept non alphanumeric chars nor long messages
    """
    regex = re.compile(r'\W')
    cleaned = regex.sub("_", msg)
    maxLength = 100
    cleaned = cleaned[:maxLength]
    return cleaned
 
def preprocessReturnMessage(msg):
    return preprocessErrorMessage(msg)
 
def setOutputInfo(tmpFileName, execInfo):
    try:
        outputFile = open(tmpFileName, 'w')
        outputFile.write(execInfo[0] + " " + execInfo[1])
        outputFile.close()
    except Exception, e:
        Logger.getInstance().logException(e)
 
def getOutputInfo(tmpFileName):
   try:
       outFile = open(tmpFileName, 'r')
       returnArray = outFile.read()
       retCode = returnArray[0]  # parse the output file
       if retCode <> '0':
          errMessage = returnArray[1:]
       else:
          errMessage = 'Success'
       errMessage = preprocessErrorMessage(errMessage)
       retInfo = [str(retCode), preprocessReturnMessage(str(errMessage))]
   except IOError, e:
       retInfo = ['6', preprocessReturnMessage('IOError, outputfile not created' + e.message)]
       Logger.getInstance().logException(e)
   except Exception, e:
       retInfo = ['7', preprocessReturnMessage(e.message)]
       Logger.getInstance().logException(e)
   finally:
       if os.path.exists(tmpFileName):
          os.remove(tmpFileName) # delete the output file
       return retInfo
 

Unit tests: Try the function but don't change the state of the system.

 
    def testRunCommandTimeOutOnSeparateProcess(self):
        retInfo = Utils.runExternalCmdOnSeparateProcess("sleep", 30, timeOut=3)
        self.assertEqual(retInfo[0], '5')
        self.assertEqual(retInfo[1], 'Operation time out')
 
    def testRunSuccesfullCommandOnSeparateProcess(self):
        retInfo = Utils.runExternalCmdOnSeparateProcess("ls", "~")
        self.assertEqual(retInfo[0], '0')
 
    def testRunFailureCommandOnSeparateProcess(self):
        """
          Always run these tests as a regular user, not root
        """
        retInfo = Utils.runExternalCmdOnSeparateProcess("mkdir", "/root/tmp", timeOut=3)
        self.assertEqual(retInfo[0], '2')
 

Kind of acceptance test: Does change the system, restarts the cron daemon

 
def tRestartCron():
    clientManagementService = make_service_client('localhost:17777/management', ServersManagementService(), https=True)
    retInfo = clientManagementService.srv_restartService('cron')
    print retInfo
     # web service wouldn't return anything if cron blocks the port, so return means success
     # also check that cron process has been restarted and is not running on any tcp port
     # run this on a controlled environment, with test machines.
 

The were also small modifications on the unit tests I had before, in the post 1 .

Next Page »