mike-obrien.net Curriculum Vitae Blog Labs
Thursday, June 11, 2009

I have an WiX installer project that I wanted to produce the output as the product name and version number. This is pretty simple to do. First unload/edit your project file and then update the "AfterBuild" target as follows:

<Target Name="AfterBuild">
  <GetAssemblyIdentity AssemblyFiles="$(SolutionDir)MyApplication\bin\MyApplication.dll">
    <Output TaskParameter="Assemblies" ItemName="GetVersionAssemblyInfo"/>
  </GetAssemblyIdentity>
  <Copy SourceFiles="$(TargetPath)" DestinationFiles="$(TargetDir)MyApplication%(GetVersionAssemblyInfo.Version).msi" />
  <Delete Files="$(TargetPath)" />
</Target>
Thursday, June 11, 2009 1:09:00 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Tuesday, June 09, 2009

Why, why, why? Is it not possible that someone would want to parse a relative uri that has a querystring? M$, why do you impose these needless limits? Ok, so lets say we have the following UriTemplate:

/books/?language={language}

And you want to pick this uri apart and grab the querystring portion. You can load it into a Uri as relative but the Query and Segments properties will not be accessible and you will get the following exception: "This operation is not supported for a relative URI." There is a simple workaround but I really don't think we should have to do this. Basically you pass in a dummy base uri along with your relative one and this does the trick:

Uri myUri = new Uri(new Uri("dummy:"), "/books/?language={language}");
System.Diagnostics.Debug.Print(myUri.Query);

If you want to vote on changing this, click here for the Microsoft Connect feedback page.

Tuesday, June 09, 2009 3:55:31 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback

If you attempt to define a GET operation that accepts an entity body with WCF REST like this:

[ServiceContract]
public interface IService
{
    [WebGet(UriTemplate = "/")]
    [OperationContract]
    ComplexFilter Search(ComplexFilter filter);
}

You will encounter the following error:

Operation 'xyz' in contract 'abc' uses GET, but also has body parameter 'lmnop'. GET operations cannot have a body. Either make the parameter 'lmnop' a UriTemplate parameter, or switch from WebGetAttribute to WebInvokeAttribute.

Who says that sending an entity body with a GET request is a bad thing?? RFC 2616 sure doesn't (See here and here). The spec neither explicitly allows (As it does for POST and PUT) or disallows sending an entity body. Interestingly M$ does allows sending an entity body with a DELETE operation even though the the spec for DELETE is just as silent about it as it is with GET, shouldn't it be the same restriction? All that is said in the spec is that GET should be used to retrieve an entity and it must be safe and idempotent. It seems clear that this doesn't rule out sending an entity body. If you have an opinion please leave a comment as I'm really interested in hearing what others have to say about this (Or here regarding this on Microsoft Connect). I could be totally wrong on this one but it looks like sending an entity body with a GET request is indeed RESTful.

Now if you think it's bogus that M$ disallows this don't despair! You can trick the WCF REST API into allowing this. It takes a bit of jumping through hoops though. First you need to create a custom WebServiceHost and factory as follows:

public class WebServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new WebServiceHost(serviceType, baseAddresses);
    }
}
public class WebServiceHost : System.ServiceModel.Web.WebServiceHost
{
    public WebServiceHost(
        Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses) { }

    public WebServiceHost(
        object singletonInstance, params Uri[] baseAddresses)
        : base(singletonInstance, baseAddresses) { }

    protected override void OnOpening()
    {
        base.OnOpening();
        ReplaceBehaviorOnAllEndpoints(this,
            typeof(System.ServiceModel.Description.WebHttpBehavior),
            new WebHttpBehavior());

    }

    public void ReplaceBehaviorOnAllEndpoints(
        ServiceHostBase serviceHost, 
        Type replaceType,
        IEndpointBehavior behavior)
    {
        foreach (var endpoint in serviceHost.Description.Endpoints)
        {
            if (replaceType != null)
            {
                IEndpointBehavior exisitingBehavior = endpoint.Behaviors.FirstOrDefault(
                    b => b.GetType() == replaceType);
                if (exisitingBehavior != null)
                    endpoint.Behaviors.Remove(exisitingBehavior);
            }
            endpoint.Behaviors.Add(behavior);
        }
    }
}

Now create the custom WebHttpBehavior. We're basically creating a surrogate behavior with all the same properties except we set the method to a bogus value. We apply this behavior before the code executes that imposes the restriction (In GetRequestDispatchFormatter). Then afterwards we remove the surrogate and reapply the original behavior.

public class WebHttpBehavior : System.ServiceModel.Description.WebHttpBehavior
{
    #region WebHttpBehavior Overrides
        protected override IDispatchMessageFormatter GetRequestDispatchFormatter(
              OperationDescription operationDescription, ServiceEndpoint endpoint)
        {
            IOperationBehavior originalBehavior = null; 
            IOperationBehavior surrogateBehavior = null;

            TryGetSurrogateBehavior(operationDescription,
                                    ref originalBehavior,
                                    ref surrogateBehavior);

            SwapBehaviors(operationDescription, originalBehavior, surrogateBehavior);

            IDispatchMessageFormatter formatter = base.GetRequestDispatchFormatter(operationDescription, endpoint);

            SwapBehaviors(operationDescription, surrogateBehavior, originalBehavior);

            return formatter;
        }

    #endregion

    #region Private Methods

        private void SwapBehaviors(OperationDescription operationDescription, 
                                   IOperationBehavior remove, IOperationBehavior add)
        {
            if (remove != null && add != null)
            {
                operationDescription.Behaviors.Remove(remove);
                operationDescription.Behaviors.Add(add);
            }
        }

        private void TryGetSurrogateBehavior(OperationDescription operationDescription, 
                         ref IOperationBehavior original, ref IOperationBehavior surrogate)
        {
            if (!IsUntypedMessage(operationDescription.Messages[0]) && 
                operationDescription.Messages[0].Body.Parts.Count != 0)
            {
                WebGetAttribute webGetAttribute = 
                          operationDescription.Behaviors.Find<WebGetAttribute>();
                if (webGetAttribute != null)
                {
                    original = webGetAttribute;
                    surrogate = new WebInvokeAttribute() {
                         BodyStyle = webGetAttribute.BodyStyle,
                         Method = "NONE",
                         RequestFormat = webGetAttribute.RequestFormat,
                         ResponseFormat = webGetAttribute.ResponseFormat,
                         UriTemplate = webGetAttribute.UriTemplate };
                }
                else
                {
                    WebInvokeAttribute webInvokeAttribute = 
                        operationDescription.Behaviors.Find<WebInvokeAttribute>();
                    if (webInvokeAttribute != null && webInvokeAttribute.Method == "GET")
                    {
                        original = webInvokeAttribute;
                        surrogate = new WebInvokeAttribute() {
                            BodyStyle = webInvokeAttribute.BodyStyle,
                            Method = "NONE",
                            RequestFormat = webInvokeAttribute.RequestFormat,
                            ResponseFormat = webInvokeAttribute.ResponseFormat,
                            UriTemplate = webInvokeAttribute.UriTemplate };
                    }
                }
            }
        }

        private bool IsUntypedMessage(MessageDescription message)
        {
            if (message == null)
            {
                return false;
            }
            return ((((message.Body.ReturnValue != null) && 
                (message.Body.Parts.Count == 0)) && 
                (message.Body.ReturnValue.Type == typeof(Message))) || 
                (((message.Body.ReturnValue == null) && (message.Body.Parts.Count == 1)) && 
                (message.Body.Parts[0].Type == typeof(Message))));
        }

    #endregion
}
.NET 3.5 | REST | WCF
Tuesday, June 09, 2009 4:09:13 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Monday, June 08, 2009

Evidentially the request message isn't guaranteed to be around by the time you get to your response code. I found out this the hard way when I was getting sporadic failures accessing properties of the IncomingWebRequestContext in response code. To get around this I just stashed info (While in request code) I was interested in, in the outgoing message properties. Then I retrieve these properties in my response code.

Using the outgoing message properties approach is pretty easy. To give you a little background, I have an error handler behavior that attaches a specified error handler to all channel dispatchers in a particular service. I also have a custom error handler that logs exceptions to a specified object that implemented a particular logging interface. This error handler passes the request http info to the logger. Originally I was directly accessing the IncomingWebRequestContext in the HandleError method of the error handler to get this request info and would sporadically get the error in the title. Now in my error handler behavior I'm adding a message inspector that will add the request http info to the outgoing message properties (I'm piggy backing off the error handler behavior to do this because they are so closely related):

public class ErrorHandlerBehavior : IServiceBehavior
{
    public const string HttpRequestInformationProperty = "HttpRequestInformation";

    public void ApplyDispatchBehavior(
            ServiceDescription serviceDescription, 
            ServiceHostBase serviceHostBase)
    {
       foreach (ChannelDispatcher dispatcher in 
                serviceHostBase.ChannelDispatchers)
       {
            // Add the error handler...

            // Add the parameter inspector that gets the request info and
            // stores it in the outgoing message properties
            foreach (EndpointDispatcher endpoint in dispatcher.Endpoints)
                  endpoint.DispatchRuntime.MessageInspectors.Add(
                       new HttpRequestInformationInspector());
       }
    }
}

So now error handlers have this info at their disposal. Then in the message inspector we grab the http request info and stash it in the outgoing message properties:

private class HttpRequestInformationInspector : IDispatchMessageInspector   
{
    public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        HttpRequestInformation info = new HttpRequestInformation();

        string contentLengthHeader =
            WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.ContentLength];

        long contentLength;
        if (!string.IsNullOrEmpty(contentLengthHeader))
            long.TryParse(contentLengthHeader, out contentLength);
        else
            contentLength = -1;

        info.ContentLength = contentLength;
        info.Uri = OperationContext.Current.IncomingMessageHeaders.To;
        info.Method = WebOperationContext.Current.IncomingRequest.Method;
        info.ContentType = WebOperationContext.Current.IncomingRequest.ContentType;
        info.Accept = WebOperationContext.Current.IncomingRequest.Accept;
        info.UserAgent = WebOperationContext.Current.IncomingRequest.UserAgent;
        info.Headers = WebOperationContext.Current.IncomingRequest.Headers;

        OperationContext.Current.OutgoingMessageProperties.Add(
            HttpRequestInformationProperty, info);
        return null;
    }

    public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { }
}
public class HttpRequestInformation
{
    public Uri Uri { get; set; }
    public string Method { get; set; }
    public string ContentType { get; set; }
    public long ContentLength { get; set; }
    public string Accept { get; set; }
    public string UserAgent { get; set; }
    public WebHeaderCollection Headers { get; set; }
}

Note that I'm manually grabbing the content length from the headers. Interestingly the content length header may not always be present. And its absence may not mean that the content length is zero (That's why I return a negative number if it is absent). The problem is the IncomingWebRequestContext ContentLength property does not check for the existence of the header before it parses it. So if its not there (Which is what's going to happen with a GET request) it will throw a null ref exception (Which can be confusing). I have submitted feedback here it you want to read more about this and Microsoft's response.

So now it's available in response code:

public bool HandleError(Exception error)
{
    HttpRequestInformation info;

    if (OperationContext.Current.OutgoingMessageProperties.ContainsKey(
        ErrorHandlerBehavior.HttpRequestInformationProperty))
        info = (RequestInformation)OperationContext.Current.OutgoingMessageProperties[
            ErrorHandlerBehavior.HttpRequestInformationProperty];
    else
        info = new RequestInformation();

    LogHandler.Write(error, info);
    return true;
}

Do you know of a better way to handle this? If so, please leave a comment as I'm not 100% satisfied with this approach and would be interested in a simpler alternative.

Monday, June 08, 2009 5:51:57 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback

I'm now to the point where I have more than one source file in an F# project (Woo hoo!) and the issue of entry point came up. Here is what the language spec says about it (As of 1.9.6):

"12.1.4 Explicit “Main” Entry Point

The last file specified in the compilation order for a .EXE may additionally contain an explicit entry point, indicated by annotating a function in a module with EntryPointAttribute.

     - The attribute can be applied only to a let-bound function in a module. It may not be a member.

     - Only one function may be given this attribute, and this must be the last declaration in the last file processed on the command line. The function may be in a nested module.

     - The function is asserted to have type “string[] -> int” prior to being checked. If this assertion fails an error is reported.

     - At runtime the arguments passed on startup are an array containing the same as entries as System.Environment.GetCommandLineArgs(), minus the first entry in that array.

The function becomes the entry point to the program. It immediately forces the static initializer for the file in which the function exists. It will then run the body of the function."

.NET 3.5 | F#
Monday, June 08, 2009 1:12:30 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Tuesday, May 26, 2009

One of the things I really wanted with our REST API is per method basic authentication. So lets say we have a library with a books resource. We want everyone to be able to read this resource but we want people to authenticate when modifying this resource. As far as I could tell there is no way to do this OOB with WCF REST without resorting to breaking your resource up into two parts, read and write and hosting the secure one in a separate IIS app with basic auth enabled. The only way I could figure out how to do this properly is implement basic auth in an operation invoker. This way you could define authentication on a per method basis as follows (With the BasicAuthenticationInvoker) without breaking the resource up:

[ServiceContract]
public interface IBookService
{
    [WebGet(UriTemplate = "/{isbn}")]
    [OperationContract]
    Book GetBook(string isbn);
            
    [WebInvoke(UriTemplate = "/{isbn}", Method=Verbs.Delete)]
    [OperationContract]
    [BasicAuthenticationInvoker]
    void DeleteBook(string isbn);
}

It's pretty simple to do this and doesn't require integration with IIS. First define a class that will act as a behavior attribute and an operation invoker:

public class BasicAuthenticationInvoker : Attribute, IOperationBehavior, IOperationInvoker 
{
}

Next, implement the operation behavior. We will store the original invoker to call if the user successfully authenticates, our invoker will basically act as a proxy. We can ignore the other three implemented methods.

#region Private Fields

    private IOperationInvoker _invoker;

#endregion

#region IOperationBehavior Members

    public void ApplyDispatchBehavior(OperationDescription operationDescription, 
        DispatchOperation dispatchOperation)
    {
        _invoker = dispatchOperation.Invoker;
        dispatchOperation.Invoker = this;
    }

    public void ApplyClientBehavior(OperationDescription operationDescription, 
        ClientOperation clientOperation) { }
    public void AddBindingParameters(OperationDescription operationDescription, 
        BindingParameterCollection bindingParameters) { }
    public void Validate(OperationDescription operationDescription) { }

#endregion

Now implement the operation invoker. First we call our private authenticate method, if this is successful we'll call the invoker, otherwise we return nothing.

#region IOperationInvoker Members

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        if (Authenticate("New York Public Library"))
            return _invoker.Invoke(instance, inputs, out outputs);
        else
        {
            outputs = null;
            return null;
        }
    }

    public object[] AllocateInputs() { return _invoker.AllocateInputs(); } 

    public IAsyncResult InvokeBegin(object instance, object[] inputs, 
        AsyncCallback callback, object state)
    { throw new NotSupportedException(); }

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
    { throw new NotSupportedException(); }

    public bool IsSynchronous { get { return true; } }

#endregion

The private authentication methods are as follows. The authenticate method checks the username and password and if successful returns true, otherwise it sets the authenticate header and the status code to unauthorized.

private bool Authenticate(string realm)
{
    string[] credentials = 
        GetCredentials(WebOperationContext.Current.IncomingRequest.Headers);

    if (credentials != null && 
        credentials[0] == "tony" && 
        credentials[1] == "clifton") return true;

    WebOperationContext.Current.OutgoingResponse.Headers["WWW-Authenticate"] = 
        string.Format("Basic realm=\"{0}\"", realm);
    WebOperationContext.Current.OutgoingResponse.StatusCode = 
        HttpStatusCode.Unauthorized;
    return false;
}

private string[] GetCredentials(WebHeaderCollection headers)
{
    string credentials = WebOperationContext.Current.IncomingRequest.
        Headers["Authorization"];
    if (credentials != null) credentials = credentials.Trim();

    if (!string.IsNullOrEmpty(credentials))
    {
        try
        {
            string[] credentialParts = credentials.Split(new char[] { ' ' });
            if (credentialParts.Length == 2 && 
                credentialParts[0].Equals("basic", 
                StringComparison.OrdinalIgnoreCase))
            {
                credentials = ASCIIEncoding.ASCII.GetString(
                    Convert.FromBase64String(credentialParts[1]));
                credentialParts = credentials.Split(new char[] { ':' });
                if (credentialParts.Length == 2) return credentialParts;
            }
        }
        catch { }
    }

    return null;
}

This same approach could also be used to examine parameters as part of the authentication process (If you are using tokens or the like).

.NET 3.5 | REST | WCF
Tuesday, May 26, 2009 6:47:08 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Thursday, May 21, 2009

This is really lame. I'm creating a custom service host (That inherits from WebServiceHost) and I want to be able to programmatically add behaviors from configuration. Since the WebServiceHost is zero config there are no service elements to define a behavior config. Now I know I could manually create those service elements with an webHttpBinding and specify the behavior config, but I really do like the zero configuration feature, I just want to be able to define behaviors in config. Unfortunately the BehaviorExtensionElement's CreateBehavior is marked as internal. I'm not sure if M$ will make this public in the future or not but it looks like the only good way to get around it for now is with reflection (Or create a dummy service element with the behavior config, have the runtime load the behaviors and then copy them to your service, boo). I think in this instance reflection on an internal member is safe as that method has to be there in order for the behavior to be created by the runtime. I don't see how it can be changed since that would break custom behaviors. I snagged this simple extension method from the aforementioned thread, it uses reflection to invoke the internal CreateBehavior method.

public static object CreateBehavior(this BehaviorExtensionElement extensionElement)
{
    return extensionElement.GetType().
        GetMethod(
            "CreateBehavior",
            System.Reflection.BindingFlags.Instance |
            System.Reflection.BindingFlags.NonPublic).
                Invoke(extensionElement, new object[0] { });
}

I also whipped up a few ServiceHost extension methods that add the behaviors (Adapted from some reflected framework code):

public static void LoadBehaviors(
    this ServiceHost serviceHost,
    string behaviorConfiguration)
{
    ServiceBehaviorElement serviceBehaviors = 
        GetServiceBehaviorElement(serviceHost, behaviorConfiguration);
    if (serviceBehaviors != null)
    {
        foreach (BehaviorExtensionElement behaviorExtension in serviceBehaviors)
        {
            object extension = behaviorExtension.CreateBehavior();
            if (extension != null)
            {
                Type extensionType = extension.GetType();
                if (typeof(IServiceBehavior).IsAssignableFrom(extensionType))
                {
                    if (serviceHost.Description.Behaviors.Contains(extensionType))
                    {
                        serviceHost.Description.Behaviors.Remove(extensionType);
                    }
                    serviceHost.Description.Behaviors.Add((IServiceBehavior)extension);
                }
            }
        }
    }
}

public static ServiceBehaviorElement GetServiceBehaviorElement(
    this ServiceHost serviceHost, string behaviorConfiguration)
{
    BehaviorsSection behaviorsSection = 
        (BehaviorsSection)ConfigurationManager.
        GetSection("system.serviceModel/behaviors");
    foreach (ServiceBehaviorElement behavior in behaviorsSection.ServiceBehaviors)
    {
        if (behavior.Name == behaviorConfiguration)
            return behavior;
    }
    return null;
}

Then you simply run this on the ServiceHost:

serviceHost.LoadBehaviors("myBehaviorConfig");

Now if your creating your own service host you can override the ApplyConfiguration method and add them there. In the following example I add a check to see if there is a service element for this service. If not then I apply the behaviors, otherwise I skip it and assume that the service element has the desired configuration.

protected override void ApplyConfiguration()
{
    base.ApplyConfiguration();
    if (!this.HasServiceElement())
        this.LoadBehaviors("myBehaviorConfig");
}
The following is the HasServiceElement & supporting GetServiceElement ServiceHost extension methods:
public static bool HasServiceElement(this ServiceHost serviceHost)
{
    return (GetServiceElement(serviceHost) != null);
}

public static ServiceElement GetServiceElement(this ServiceHost serviceHost)
{
    ServicesSection servicesSection = (ServicesSection)ConfigurationManager.
        GetSection("system.serviceModel/services");
    ServiceElementCollection services = servicesSection.Services;
    foreach (ServiceElement element in services)
    {
        if (element.Name == serviceHost.Description.ConfigurationName)
            return element;
    }
    return null;
}
.NET 3.5 | REST | WCF
Thursday, May 21, 2009 8:57:40 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Friday, May 15, 2009

I just submitted a few suggestions regarding the WCF/REST implementation. If you feel the same, please vote on them. If you don't agree with them, please leave a comment, I'd love to hear some thoughts on these issues.

REST/WCF Automatic Selection of Formatter Based On Accept & Content Type Headers

REST/WCF UriTemplate Querystring Parameter Name Case Sensitivity

REST/WCF UriTemplate Optional Querystring Parameters

.NET 3.5 | REST | WCF
Friday, May 15, 2009 5:06:19 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Wednesday, May 06, 2009

This has been done over and over on the web so I'll add my solution to the mix. Basically it automatically discovers the services in the web application and builds a static mapping table. Then it examines each request and attempts to find a mapping. If it finds one it performs the url rewriting, if not it lets it go though as is. Just register the module and your good to go.

Http Module:

public class ServiceAnonymityModule : IHttpModule
{
    #region IHttpModule Implementation

        public void Dispose() { }

        public void Init(HttpApplication app)
        {
            app.BeginRequest +=
                (s, e) => ServiceAnonymityMapper.EnsureServiceMapping();
        }

    #endregion
}

Mapping:

public static class ServiceAnonymityMapper
{
    #region Private Fields

        private static IEnumerable<KeyValuePair<string, string>> serviceMapping;

    #endregion

    #region Static Constructor

        static ServiceAnonymityMapper()
        { serviceMapping = GetServiceMapping(); }

    #endregion

    #region Public Methods

        public static void EnsureServiceMapping()
        {
            string path = HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath;

            Func<KeyValuePair<string, string>, bool> serviceMatch = map =>
                !path.StartsWith(map.Value) &&
                (
                    path.StartsWith(map.Key + '?', StringComparison.OrdinalIgnoreCase) ||
                    path.StartsWith(map.Key + '/', StringComparison.OrdinalIgnoreCase) ||
                    string.Compare(path, map.Key, true) == 0
                );

            KeyValuePair<string, string> mapping =
                serviceMapping.FirstOrDefault(serviceMatch);

            if (mapping.Key != null && mapping.Value != null)
            {
                HttpContext.Current.RewritePath(
                    mapping.Value + "/", 
                    path.Remove(0, mapping.Key.Length),
                    HttpContext.Current.Request.QueryString.ToString(),
                    false);
            }
        }

    #endregion

    #region Private Methods

        private static IEnumerable<KeyValuePair<string, string>> GetServiceMapping()
        {
            List<KeyValuePair<string, string>> serviceMapping =
                new List<KeyValuePair<string, string>>();

            string webRoot = HttpContext.Current.Server.MapPath("~/");
            string[] serviceFiles = Directory.GetFiles(
                webRoot, "*.svc", SearchOption.AllDirectories);

            Func<string, bool, string> getRelative = (path, ext) =>
                "~/" +
                Path.Combine(Path.GetDirectoryName(path),
                    ext ?
                        Path.GetFileNameWithoutExtension(path) :
                        Path.GetFileName(path))
                    .Remove(0, webRoot.Length).Replace('\\', '/');

            var servicePaths = from servicePath in serviceFiles
                               orderby servicePath.Length descending
                               select new KeyValuePair<string, string>(
                                    getRelative(servicePath, true),
                                    getRelative(servicePath, false));

            serviceMapping.AddRange(servicePaths);
            return serviceMapping;
        }

    #endregion
}
REST | WCF
Wednesday, May 06, 2009 10:29:49 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback
Monday, May 04, 2009

I came across a weird issue with proxy's generated by the Axis2 code generator from a WCF WSDL. Basically there is ambiguity between the String type defined in the Microsoft WSDL for primitive types which gets generated in the proxy and the java.lang.String. Supposedly this issue has been fixed but I still had an issue with the code generator plugin that installs with the Eclipse Ganymede plugin manager. Anyway's, to work around it I just navigated to the error:

image

And specified the FQ name for the Java string type and it worked fine:

image

Sunday, May 03, 2009 11:32:49 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  |  Trackback