Thursday, November 19, 2009

The Regex & StringTokenizer Challenge!

Sometimes programming is difficult - You have to make large architectural decisions, with overreaching implications on maintainability, performance and security. Other times, programming is simply frustrating - Something 'simple' doesn't work the way you expected, you're seriously testing your logic and finally you bring out the paper and pen for some good-old pseudo code!

The problem...
I was doing some pro-bono processing work on an excel file, removing duplicate rows based on some complicated criteria. Preferring to keep my sanity I abandoned VBA so I exported the spreadsheet to CSV. The crunch is, some of the cells were blank and this produced unpredictable results in my code. Here's a sample of the data:

  1. ,,dsafdf,,15,,,,
  2. ,,fdsfjladsjf,,13,,,,
  3. df,,sdff,,bemail,1,1,,
  4. dsf,,sffdsf,,bsgemail,1,1,,
  5. token1,token2,token3,token4,token5,token6,token7,token8,token9

I've added the fifth row for demonstration of the number of tokens. In a row that is complete, we are expecting 9 tokens. Their position is important!

Source Code
I've included some source code showing 3 different approaches:

  1. import java.util.StringTokenizer;

  2. public class Processor {

  3. public static void main (String[] args) {

  4. final String _DELIM = ",";

  5. //An array of the strings
  6. String[] sourceStrings = {
  7. ",,dsafdf,,15,,,,",
  8. ",,fdsfjladsjf,,13,,,,",
  9. "df,,sdff,,bemail,1,1,,",
  10. "dsf,,sffdsf,,bsgemail,1,1,,",
  11. "token1,token2,token3,token4,token5,token6,token7,token8,token9"
  12. };

  13. System.out.println("Approach One: String.split()");
  14. for(String s: sourceStrings)
  15. {
  16. //do the processing on the strings using
  17. //the delimiter as a regular expression;
  18. String[] split = s.split(_DELIM);

  19. System.out.println("Number of tokens : " + split.length);
  20. }

  21. System.out.println("Approach Two: StringTokenizer");
  22. for(String s: sourceStrings)
  23. {
  24. StringTokenizer st = new StringTokenizer(s, _DELIM, false);

  25. System.out.println("Number of tokens : " + st.countTokens());
  26. }

  27. System.out.println("\r\nApproach Three: StringTokenizer returning delimiter - Take One");
  28. for(String s: sourceStrings)
  29. {

  30. StringTokenizer st = new StringTokenizer(s, _DELIM, true);

  31. System.out.println("Number of tokens : " + st.countTokens());
  32. }
  33. }
  34. }

A listing of the output of the code above is shown below:

  1. Approach One: String.split()
  2. Number of tokens : 5
  3. Number of tokens : 5
  4. Number of tokens : 7
  5. Number of tokens : 7
  6. Number of tokens : 9

  7. Approach Two: StringTokenizer
  8. Number of tokens : 2
  9. Number of tokens : 2
  10. Number of tokens : 5
  11. Number of tokens : 5
  12. Number of tokens : 9

  13. Approach Three: StringTokenizer returning delimiter - Take One
  14. Number of tokens : 10
  15. Number of tokens : 10
  16. Number of tokens : 13
  17. Number of tokens : 13
  18. Number of tokens : 17

Approach One: String.split()
The first approach uses the String.split() method passing in the delimiter as a regular expression. As can be seen, this fails the test as we have ranges of 5-9 tokens. The correct answer should be 9.

Approach Two: StringTokenizer
The second approach uses the StringTokenizer to split up the strings. Again, we get unpredictable results. 2 tokens for the first two lines, as it picks only the items that are not the delimiter. The Correct answer here would be 9 and for the items that are null, we should get a null token.

Approach Three: StringTokenizer returning the delimiter
The third approach uses the StringTokenizer with a different constructor that also returns the delimiter. Again, lots of wrong results. The correct number of tokens should be 17 - 8 commas and 9 non comma items, preferrably null.

So there you have it, a rather simple task (I would have thought), but I did bang my head against the wall trying to find the right solution. If I ever get to be a teacher, this would be great for an exam! There's the challenge! I'll post the solution soon.


  1. public class NonCollapsingStringTokenizer {  
  3.     private String str;  
  4.     private String delim;  
  5.     private int currentPosition;  
  7.     private NonCollapsingStringTokenizer() {  
  9.     }  
  11.     public NonCollapsingStringTokenizer(String str, String delimStr) {  
  12.         this.str = str;  
  13.         this.delim = delimStr;  
  14.     }  
  16.     public String nextToken() {  
  17.         int nextDelimPosition = str.length();  
  18.         int delimPosition = str.indexOf(delim, currentPosition);  
  19.         if (delimPosition >= 0 && delimPosition < nextDelimPosition) {  
  20.             nextDelimPosition = delimPosition;  
  21.         }  
  22.         String token = str.substring(currentPosition, nextDelimPosition);  
  23.         currentPosition = nextDelimPosition + 1;  
  24.         return token;  
  25.     }  
  27.     public boolean hasMoreTokens() {  
  28.         return (currentPosition < str.length());  
  29.     }  
  31.     public int countTokens() {  
  32.         int count = 0;  
  34.         NonCollapsingStringTokenizer clone = (NonCollapsingStringTokenizer) this.clone();  
  35.         while(clone.hasMoreTokens())  
  36.         {  
  37.             clone.nextToken();  
  38.             count++;  
  39.         }  
  41.         return count;  
  42.     }  
  44.     public Object clone() {  
  45.         NonCollapsingStringTokenizer copy = new NonCollapsingStringTokenizer();  
  46.         copy.str = str;  
  47.         copy.delim = delim;  
  48.         return currentPosition;  
  50.     }  
  51. }  

The class above handles the challenge accordingly. This can be tested with the code below:

  1. public static void main(String[] args) {  
  2.         String s = ",,dsafdf,,15,,,,";  
  4.         final String _DELIM = ",";  
  5.         //s = "token1,token2,token3,token4,token5,token6,token7,token8,token9";  
  6.         s = ",,sffdsf,,bsgemail,1,1,5,dsf";  
  8.         NonCollapsingStringTokenizer ncst = new NonCollapsingStringTokenizer(s, _DELIM);  
  9.         int x = 1;  
  10.         while(ncst.hasMoreTokens()) {  
  11.             String nextToken = ncst.nextToken();  
  13.             System.out.println(Integer.toString(x++) + " " + nextToken);  
  14.         }  
  15.     }  

This produces the listing below:

  1. 1   
  2. 2   
  3. 3 sffdsf  
  4. 4   
  5. 5 bsgemail  
  6. 6 1  
  7. 7 1  
  8. 8 5  
  9. 9 dsf  

Tuesday, November 10, 2009

iPhone Programming Tools - What's in my toolbox?

Having developed in many programming languages, one finds one has picked up several tools along the way. Despite the relative immaturity of iPhone OS development, there's a lot of Objective-C and Cocoa developers out there and even the newly welcomed faithful have helped contribute a lot of different tools to the trade. Well I've decided to put down what's in my toolbox to help anybody who stumbles here looking for a tool to do something and also as a big thanks to the developers of these tools. (Always remember, a bad workman always blames his tools). This list is by no means definitive and I'll update it as I go along. Suggestions are welcome - to make this list more useful and also to make my life easier :)

X-Code & Interface Builder (IB)
The de-facto IDEs barely rate a mention because you can't do much useful development without them. Or so I thought until I found out that the 'first' iphone virus (ikee virus) was not developed using X-Code or IB. No! "It's good old C on a linux cross compile tool chain" as the developer reveals in an interview here. No expertise on the iPhone OS required either! On a serious note though, X-Code and IB are useful. I'd love to hear from non-X-Code developers.

Leaks Performance Measurement tool
This is part of the Development tools bundled with the iPhone SDK. It deserves a special mention as it is useful to profile you application during testing to ensure no memory leaks are present in your coding. Developers used to developing in programming languages where memory management and garbage collection is handled for you are prone to write code that contains many memory leaks. If only finding them was as easy as fixing them...

SQLite Admin is an GUI administration tool for SQLite - which is used as the portable database for iPhone applications. This tool is useful to view the schema of the SQLite database created by CoreData and pre-populate SQLite databases. It requires the Adobe Air runtime.

This is a utility to read the backups from an iPhone device. The backups are created using iTunes sync. This tool used in conjunction with the SQLite admin lets you get the SQLite backups from a device, not just the simulator - hence you can see not just your application's databases but those of other apps!

iPhone PNG normalization tools
When I need to access an image that has been used in a third-party or apple's applications, I usually rename the application's *.ipa file from the iTunes music folder to a zip file and unpack it. Then I show the contents of the "*.app" bundle. Inside this bundle are the resources used in the application. The png images are not in an easily readable format. I use the following two tools to normalize them:
  1. iPhonePNG. This is a pre-compiled objective-c project
  2. iPhone PNG Normalizer. This is a python script
Do not use this to infringe on the copyright of others.

3 Twenty is a static library that provides many useful UI components, such as image viewers and general utilities. It is based on the facebook iphone app which is one of the most downloaded facebook apps.

More tools to be added as discovered.

iPhone / Objective-C reading .csv files

I've been working on an iPhone application where I needed to save some data into the SQLite database via CoreData. The data I had was in a .csv file and I needed to preload this data on to the phone. I had two choices:
  1. Create an SQLite database by some programmatic means and import this as the store for CoreData.
  2. Read the .csv file and create NSManagedObject instances which can be saved to the context.
Option 1 seemed a little more daunting for me, so I chose the second option using NSUserDefaults to save a flag that I'd already read the .csv. I'm still investigating the first option and will report back if I'm successful. Anyway, here's a sample of the contents of the file:
  1. 31939,-37.838596,144.96726,1 - abcd
  2. 31752,-37.856249,144.979277,2 - efgh
  3. 31751,-37.854868,144.978053,3 - ijkl
  4. 31750,-37.856094,144.977565,4 mnop
  5. 31749,-37.855388,144.9753,4 qrst
So that's an excerpt of the .csv - four columns of integer, double, double and a string. Including your csv file in the resources folder of your XCode project. Ensure that the "Target" checkbox is checked (Its on the top of the right content pane when the .csv file has been selected). Next is the code to load this data:
  1. //
  2. // load the points from our local resource
  3. //
  4. NSString* filePath = [[NSBundle mainBundle] pathForResource:@"a_csv_file" ofType:@"csv"];
  5. NSString* fileContents = [NSString stringWithContentsOfFile:filePath];
  6. NSArray* pointStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
  7. //NSMutableArray* points = [[NSMutableArray alloc] initWithCapacity:pointStrings.count];
  8. for(int idx = 0; idx < pointStrings.count; idx++)
  9. {
  10. // break the string down even further to the columns
  11. NSString* currentPointString = [pointStrings objectAtIndex:idx];
  12. NSArray* arr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];
  13. NSNumber *colTwo = [[NSNumber alloc] initWithDouble: [[arr objectAtIndex:1] doubleValue]];
  14. NSNumber *colThree = [[NSNumber alloc] initWithDouble: [[arr objectAtIndex:2] doubleValue]];
  15. NSNumber *colOne = [[NSNumber alloc] initWithInt:[[arr objectAtIndex:0] intValue]];
  16. NSString *colFour = [[NSString alloc] initWithFormat:@"%@", [arr objectAtIndex:3]];
  17. //... we can now use these pointers to save to the CoreData database
  18. }
In line 06 of the code above, we've selected a newLineCharacterSet as the delimiter so that the file is split by lines. In line 14, we then use the comma character set to split the columns by comma. Et Voila! Finally, see the rather odd way of reading strings. Must be a simpler way...

Monday, November 9, 2009

Configure Tomcat 6 for JAX-WS

As a servlet container, Tomcat is not a full J2EE container hence requires some additional configuration in order to support JAX-WS. This article describes this configuration step-by-step:

  1. Download a copy of the JAX-WS library – Instructions here were tested this on version 2.1.5 (
  2. Copy the file JAXWS2.1.5-20081030.jar to an appropriate folder on your computer – say, /root/tools/ JAXWS2.1.5-20081030.jar
  3. Execute the jar: java –jar JAXWS2.1.5-20081030.jar
  4. This in effect unpacks the files contained in this jar. You should get a folder /root/tools/jaxws-ri containing the supporting libraries, docs, tools and samples. Let’s call this path $JAX-WS-HOME$.
  5. Copy the jar files in $JAX-WS-HOME$/lib/ to the Tomcat (let’s call the install path for this $CATALINA_HOME$) library$CATALINA_HOME$/lib and $CATALINA_HOME$/shared/lib path.
  6. Create an folder in the Tomcat installation directory called “endorsed”. The full path should be$CATALINA_HOME$/endorsed.
  7. Copy the files “jaxb-api.jar” and “jaxws-api.jar” from the “$JAX_WS_HOME$/lib” to the “$CATALINA_HOME$/endorsed/”folder.
  8. Edit the Tomcat configuration file “$CATALINA_HOME$/conf/” (You might want to take a backup before you edit it). There should be a property with the key “shared.loader”. Make sure the path $CATALINA_HOME$/shared/lib/*.jar is included in the comma separated list of paths in the value for this property. For example it could look like :shared.loader=/etc/tomcat/shared/lib/*.jar. That’s the main set of configuration that needs to be done.
  9. Before running tomcat, ensure the following property is to be set on the script used to start tomcat:
  10. This ensures the two jars we copied into the endorsed folder are picked up.
  11. Start tomcat.
UPDATE: This post makes assumptions about your source and web-archive being correctly set up. When I was doing this, I was working in a Contract-First approach and I wrote a quick guide to that here. This covers how your web.xml and sun-jaxws.xml file should be done. When I get sometime I'll upload a redacted version of my code as it has some work stuff. Good luck!

Contract-First Web Services in practice - part 5 of 5 (Java / J2EE)

This is the last article in the series of 5 and today we're looking at contract first development in Java/J2EE. I've left this for last after considering PHP and NET due to the various application servers and technologies in which this is possible. Java is also my forte hence I'm not shying away from this challenge. We'll look at JAX-WS on J2EE and Tomcat.

JAX-WS / EE5 Application Servers
If you prefer the blue pill and like the neat, java 5+, annotation heavy, jaxws-does-everything-for-you alternate reality, then you're in for a treat. The JAX-WS RI is available in most J2EE servers. I've picked JBOSS (the 4.2.2 GA) which is what I was using for work. JBOSS ships with wstools in the bin directory. Run the command "wsconsume -k SampleService.wsdl". This creates client stubs and a server interface "com.kimenye.SampleServicePortType". This is the class to be implemented. Once this is done, create a web.xml file and create a servlet with the servlet-class as the implementation created. Package this as a web-archive, and pop it into the deploy directory of the app server.

Tomcat 6
Tomcat 6 is a servlet container and not a full J2EE container hence it is not compulsory for it to provide JAX-WS. See my post on how to configure Tomcat 6 to use JAX-WS. Follow the steps below to implement the sample service:
  1. Use "wsimport" tool to create the service stubs. This tool is available from the bin directory of an unpacked JAX-WS installation. The full command to run is : wsimport -keep SampleService.wsdl
  2. Create a web application in your preffered IDE. Copy the artifacts generated from the wsimport into your source. Implement the port type interface. For our example this class will be com.kimenye.ProvisionServicePortType. Compile the implementations and the generated classes.
  3. Copy the wsdl into a folder named wsdl under the WEB-INF directory.
  4. Create a file named sun-jaxws.xml in the WEB-INF directory and fill in the content below:
    1. <endpoints xmlns=""
    2. version="2.0">

    3. <endpoint name="SampleService" interface="com.applicationsnet.service.soap.SampleServicePortType"
    4. implementation=com.kimenye.SampleServiceImpl" wsdl="WEB-INF/wsdl/SampleService.wsdl"
    5. service="{}SampleService" port="{}SampleServicePort"
    6. url-pattern="/SampleService" />

    7. </endpoints>
  5. Edit the web.xml file to include the content below:
    1. <web-app xmlns="" xmlns:xsi=""
    2. xsi:schemaLocation=""
    3. version="2.4">
    4. <description>sample</description>
    5. <display-name>sample</display-name>
    6. <listener>
    7. <listener-class></listener-class>
    8. </listener>
    9. <servlet>
    10. <description>Sample End-Point</description>
    11. <display-name>Sample End-Point</display-name>
    12. <servlet-name>SampleServicePortType</servlet-name>
    13. <servlet-class></servlet-class>
    14. <load-on-startup>1</load-on-startup>
    15. </servlet>
    16. <servlet-mapping>
    17. <servlet-name>SampleServicePortType</servlet-name>
    18. <url-pattern>/SampleService</url-pattern>
    19. </servlet-mapping>
    20. </web-app>
  6. Create the web-archive file and copy it to your tomcat web applications directory.
  7. Edit the startup script for tomcat to include the property : – This is related to how the soap faults are formatted and is IMPORTANT!
  8. Start tomcat and enjoy!

Friday, November 6, 2009

Contract-First Web Services in practice - part 4 of 5 (.NET)

Welcome to part 4 of 5 of this article series putting contract first SOA through its paces. In the last article, I looked at how to create a php implementation of a wsdl defined in part two. In this article we look at how to create a service implementation in the .NET framework. This will be done in Visual Basic but could work for C#.NET.

Contract-first web service development is generally aspired to but often ignored in favour of code-first approaches because it is often quicker to do so. This is more so prevalent in the .NET framework where the ease of developing ".asmx" web services in Visual Studio drives people down this path. One way of creating "contract-first" web services is create a "code-first" one that closely resembles the contract you want to implement. This is may or may not work depending on the complexity of the contract and is not advised. The approach I advise is to create a binding interface using the "wsdl.exe" program that ships as part of the .NET framework or visual studio. With the path to this executable added to the %PATH% environment variable run the following command in a directory containing the wsdl file.

wsdl /language:VB /serverInterface SampleService.wsdl

If C#.NET artifacts are required replace the language with CS. This creates a single file "SampleServiceInterfaces.vb" that contains the data structures defined in the schema of the wsdl. It is heavily annotated to ensure that when the objects are serialized they are in compliance with wsdl. To complete the web service creation follow the steps below:
  1. Add the bindings interface to an existing or newly created ASP.NET web site.
  2. Now add a new "asmx" web service.
  3. Open the code behind class for the new web service. Make this class implement the binding interface instead of extending Service. Visual Studio should now create the stubs for the methods to be implemented.
  4. Implement the wsdl methods.
For our example, this should work brilliantly for our wsdl defined except for the typed exceptions. In the "infinite wisdom" of the designers of the .NET framework including thrown exceptions in the method signature is not supported. In fact, the binding interface does not create any types for the exceptions. I've created a custom class that creates the required xml for the exceptions.
  1. Imports System.Web.Services.Protocols  
  2. Imports System.Xml  
  5.       Public Class CustomSoapException  
  6.             Inherits SoapException  
  8.             Public Enum ExceptionCode  
  9.                   ServiceException  
  10.             End Enum  
  12.             Public Sub New(ByVal ExCode As ExceptionCode, ByVal Message As String)  
  13.                   Me.New(ExCode, SoapException.ClientFaultCode, Message)  
  14.             End Sub  
  16.             Public Sub New(ByVal ExCode As ExceptionCode, ByVal SoapFaultCode As XmlQualifiedName, ByVal Message As String)  
  17.                   MyBase.New(ExCode.ToString(), SoapFaultCode, Nothing, CreateXmlNode(ExCode, Message))  
  18.             End Sub  
  20.             ''' <summary>  
  21.             '''  
  22.             ''' <ServiceException>  
  23.             '''    <message>The exception message goes here.</message>  
  24.             ''' </ServiceException>  
  25.             '''  
  26.             ''' </summary>  
  27.             ''' <returns></returns>  
  28.             ''' <remarks></remarks>  
  29.             Private Shared Function CreateXmlNode(ByVal ExCode As ExceptionCode, ByVal Message As String) As XmlNode  
  30.                   Dim doc As New System.Xml.XmlDocument  
  32.                   Dim detailNode As System.Xml.XmlNode = doc.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, _  
  33.                               SoapException.DetailElementName.Namespace)  
  35.                   Dim excType As System.Xml.XmlNode = _  
  36.                               doc.CreateNode(XmlNodeType.Element, _  
  37.                               ExCode.ToString(), _  
  38.                               "")  
  40.                   Dim messageNode As System.Xml.XmlNode = _  
  41.                               doc.CreateNode(XmlNodeType.Element, _  
  42.                               "message", _  
  43.                               "")  
  46.                   messageNode.InnerText = Message  
  48.                   excType.AppendChild(messageNode)  
  50.                   detailNode.AppendChild(excType)  
  52.                   Return detailNode  
  53.             End Function  
  54.       End Class  
Throw an instance of this class to comply with the wsdl. That's it for the .NET implementation. In the next article I'll detail how to do the implementation in Java and wrap up my thoughts on the process.

Thursday, November 5, 2009

Contract-First Web Services in practice - part 3 of 5 (PHP)

Welcome to the third article in this series of 5 where I'm putting contract-first service oriented architecture through its paces. For a recap, in article one I defined the rules of engagement, and article two I outlined the contract for the service to be developed. With this behind us, the next challenge is to begin the representation of this service in the 3 programming languages and PHP is first of the blocks.

There are several libraries for use in tackling SOAP in PHP (Pear, Nu Soap, PHP Soap). Not being my forte, I selected the inbuilt soap library PHP Soap that ships with PHP 5. I needed a simple approach that supported WSDL 1.1 and "document literal". I didn't think this would be too much to ask until I discovered that there was an affinity for "RPC encoded" style of web services which fits in well with positional parameters which are well supported in php.

The first step in implementing the service is to create the endpoint handler. This is the php file at the url which will respond to this service and is set in the soap:address element of the wsdl. In the example wsdl the endpoint is "http://localhost:8080/index.php", so the endpoint handler is the index.php. The content for this file is shown below:
  1. <?php
  2. //cache the wsdl
  3. ini_set("soap.wsdl_cache_enabled", "0");
  4. //load the code for the service handler
  5. require_once('./SampleServiceClass.php');
  6. //create a new soap server. Refer to the wsdl file
  7. $server = new SoapServer('SampleService.wsdl');
  8. //set the class which will be handling the request
  9. $server->setClass('SampleServiceClass');
  10. //begin the server
  11. $server->handle();
  12. ?>
  1. The first statement is a directive to cache the wsdl for better performance (suitable for production environments but reasonable in this case as it's contract first so we're taking the WSDL as given).
  2. The next statement imports a file SampleServiceClass.php which we have not yet created. More on this soon.
  3. The third statement creates a soap server instance. The constructor takes as one of its arguments, the wsdl file defined in article 2.
  4. The fourth statement sets the class SampleServiceClass (yet to be defined in the file SampleServiceClass.php)
  5. The final statement starts the server instance.
With those five statements, we're setting up the endpoint by tying in the wsdl to the service handler which we will create in the next step.
  1. <?php
  2. //Exception Types
  3. define('SERVICE_EXCEPTION', 'ServiceException'); //thow this error when the user does not exist
  4. //Soap Exception Fault Codes
  5. define('ERROR_CODE_CLIENT', "Client");
  6. define('ERROR_CODE_SERVER', "Server");
  7. /**
  8. *
  9. * This class handles the Sample Service Soap Requests
  10. *
  11. */
  12. class SampleServiceClass
  13. {
  14. /**
  15. * Create an exception
  16. *
  17. * <ns2:SampleException xmlns:ns2="">
  18. * <message>Some weird error occurred</message>
  19. * </ns2:SampleException>
  20. *
  21. * @param object $exception
  22. * @return
  23. */
  24. function createException($exception,$message)
  25. {
  26. $typedException = new StdClass();
  27. $typedException->message = $message;
  28. $detail = null;
  29. $error_code = ERROR_CODE_CLIENT;
  30. if ($exception == SERVICE_EXCEPTION)
  31. {
  32. $detail->ServiceException = $typedException;
  33. }
  34. return new SoapFault($error_code,$exception,null,$detail,$exception);
  35. }
  36. /**
  37. * This is the sample operation.
  38. *
  39. * @param object $SampleOperationRequest
  40. *
  41. * @return
  42. */
  43. function SampleOperation($SampleOperationRequest)
  44. {
  45. $num_of_args = func_num_args();
  46. //In this example the custom fields are compulsory
  47. if(func_num_args()<1)
  48. {
  49. return $this->createException(SERVICE_EXCEPTION, "SampleOperationRequest -Incorrect parameters passed in.");
  50. }
  51. $Demo = $SampleOperationRequest->DemoField;
  52. $CustomFields = $SampleOperationRequest->SampleFields;
  53. $num_custom_fields = count($CustomFields->SampleField);
  54. /**
  55. * This is to demonstate how to access the custom fields.
  56. *
  57. * If only one custom field is required, access is
  58. *
  59. * $fieldOne = $CustomFields->CustomField->FieldValue
  60. */
  61. if($num_custom_fields == 2)
  62. {
  63. if($CustomFields->SampleField[0]->FieldName == 'a field')
  64. {
  65. $fieldOne = $CustomFields->CustomField[0]->FieldValue;
  66. $fieldTwo = $CustomFields->CustomField[1]->FieldValue;
  67. }
  68. if($CustomFields->SampleField[0]->FieldName == 'another field')
  69. {
  70. $fieldTwo = $CustomFields->CustomField[0]->FieldValue;
  71. $fieldOne = $CustomFields->CustomField[1]->FieldValue;
  72. }
  73. }
  74. $ro1 = new StdClass();
  75. $ro1->DemoField = "Scott";
  76. $custom_logo = new StdClass();
  77. $custom_logo->FieldName = 'logo';
  78. $custom_logo->FieldValue = 'A logo';
  79. $nickname = new StdClass();
  80. $nickname->FieldName = 'nickname';
  81. $nickname->FieldValue = 'Scotty';
  82. $custom_logo2 = new StdClass();
  83. $custom_logo2->FieldName = 'logo';
  84. $custom_logo2->FieldValue = 'A logo';
  85. $nickname2 = new StdClass();
  86. $nickname2->FieldName = 'nickname';
  87. $nickname2->FieldValue = 'Scotty';
  88. $custom_fields = array($custom_logo, $nickname);
  89. $custom_fields2 = array($custom_logo2, $nickname2);
  90. $ro2->SampleFields = $custom_fields;
  91. $ro2 = new StdClass();
  92. $ro2->DemoField = "Hello";
  93. $ro2->SampleFields = $custom_fields2;
  94. $objs = array($ro1, $ro2);
  95. return $objs;
  96. }
  97. }
  98. ?>
This class is a lot more substantial than the previous as it handles the Soap requests.
  1. First, is the definition of a couple of exception constants.
  2. Second, the method "createException" throws the contract specified exception.
  3. Finally, the SampleOperation method reads the input and returns the array output.

That's it!

Wednesday, November 4, 2009

iPhone NSTimer on separate loop

This is a quick post about the usage of NSTimer to create a multi-threaded experience within an iPhone application. It does NOT address "backgrounding" or other challenges with creating threads in the highly restricted iPhone OS runtime.

I encountered this problem when I was trying to recreate a stopwatch application similar to the "Stopwatch" tab in the "Clock" application. I had an NSTimer object firing every tenth of a second, updating the label displaying the duration by calling a method "updateLabels".

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:(0.1) target:self selector:@selector(updateLabels) userInfo:nil repeats:YES];

This quickly demonstrated my naivete regarding how the iPhone OS environment handles its run sequence. What happened was when the user touched the phone, my timer was delayed hence there was a noticeable lag (not firing until the user stopped touching the screen) in updating the labels. As this was not happening on the apple supplied app, I hit the search trail and found out about the "NSRunLoop" in this post. This object controls the run sequence of events in the app lifecycle and it places user events ahead of scheduled threads. The correct way of running the timer was hence:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:(0.1) target:self selector:@selector(updateLabels) userInfo:nil repeats:YES];

[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

[pool release];