Showing posts with label Web Services. Show all posts
Showing posts with label Web Services. Show all posts

Monday, April 11, 2011

How to consume a SharePoint Web Service with WCF Client

SharePoint Web Services are great because they can be called from any computer that can connect to SharePoint, not just the server that is running SharePoint. This is nice for installations of SharePoint like MOSS 2007 or 2010, but it is critical for SharePoint Online (BPOS) or SharePoint 360 where you will NOT have access to the server that SharePoint runs on. The problem is they are not nearly as easy to work with as just using the SharePoint object model, and they don’t expose everything through the web services. This is a real bummer! It is still a very useful alternative for situations where you cannot use the SharePoint Object Model.

There are a few tricks for working with SharePoint Web Services. There are lots of ways you can access the web services. I prefer to use WCF to access the services so that I get cool syntax from XElement.

Here is a list of web services that are available. Let’s use the Webs web service. Below are the step by step (close anyway) to using the web service in Visual Studio 2010.

  1. Create a console (for simplicity) or any other project type in Visual Studio 2010. NOTE: This can be on your laptop that is NOT running SharePoint.
  2. Go to the docs for the Webs Web Service. Here you will see that the url is: http://<Site>/_vti_bin/Webs.asmx you need to replace <Site> with your hostname or dns name, etc.
  3. Add a Service Reference to the above url (after you replaced <Site> with your site info). I am naming my MyWebs, but you can call it whatever you like, just make the appropriate changes to the code I show later.
  4. If you require all users to be authenticated (don’t accept anonymous users) then you will need to change the WCF bindings that were automatically created in your app.config such that it will pass security information to the web service. To do this, just search for the url of the web service. That will take you to a XML tag called endpoint. In that same XML tag you will see an attribute called WebsSoap. In this same app.config do a search for WebsSoap. This will take you to a binding tag. In that binding tag you will see a security tag. That security tag should have a mode=”None” attribute. Change mode=”None” to mode=”TransportCredentialOnly”. Then just below that you will see a transport tag, change the clientCredentialType=”None” to clientCredentialType=”Ntlm” and change the proxyCredentialType=”None” to proxyCredentialType=”Ntlm”. 
  5. All SharePoint services are work on Site Collections or Sites (webs) so really the docs should say http://<Site>/<subsite>/_vti_bin/Webs.asmx because if you are dealing with a subsite you need to use that url. You don’t need to add a service reference for each site, you can change it in the code. Be sure to change the endpoint address line in the example below.
  6. Since we are using WCF, we get XElement which gives us LINQ syntax and objects from XML. How cool is that! When determining what is available as attributes it is easiest to just look in the debugger (use the visualizer).
  7. Now that we the results as objects we can do whatever we want with them.

NOTE: You can also do this with the old Web Service client, but you don’t have XElement, LINQ support, but changing the URL is as easy as just changing the url and the Credentials property is all you need to change the credentials. You also don’t really have to worry about the WCF app.config stuff, but in the end I still like WCF.

 

private static void GetWebs()
{
    using (var ws = new MyWebs.WebsSoapClient())
    {
        // pass the proper credentials. Comment/Uncomment the proper lines depending on your situation
        ws.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials; // use current security context
        //ws.ClientCredentials.Windows.ClientCredential = new NetworkCredential("username", "password", "domain"); // use another Active Directory account
        //ws.ClientCredentials.Windows.ClientCredential = new NetworkCredential("username", "password"); // use account that is used for BPOS

        // change url to the site we want to work with
        ws.Endpoint.Address = new System.ServiceModel.EndpointAddress("
http://myhost/sites/SomeSiteHere/_vti_bin/Webs.asmx");

        // get all the webs at and below the specified site
        var results = ws.GetAllSubWebCollection();
               
        // use XElement and LINQ to get our results and create objects from the XML.
        var test = from r in results.Elements()
                    orderby r.Attribute("Url").Value
                    select new { Title = r.Attribute("Title").Value, Url = r.Attribute("Url").Value };

        // the results are just objects now, so do whatever you want with them.
        foreach (var item in test)
        {
            //Console.WriteLine(item.Url + " | " + item.Title);
            Console.WriteLine(item.Url);
        }

    }
}

Thursday, January 7, 2010

How to query Microsoft Dynamics CRM

There are several choices for querying Microsoft Dynamics CRM. Other than direct database (SQL Server) access, they all go through the web service that is provided by MS Dynamics CRM. Below I provide what I learned when I tried to pull data from MS Dynamics CRM.

Assumptions

I assume you are using Microsoft Dynamics CRM 4.0 (though I think most will apply to 3.0 also). I assume you already have a MS Dynamics CRM installed and working. If you are like me, someone else manages and installed it, but you need to pull or update data in it.

You will need an account to the system. It doesn’t have to be a particularly powerful user, but it does have to have the proper permissions to read or write or create or delete or whatever you want your program to do. Your program will be logging into the system with those credentials.

I assume you want to use the web service instead of direct database access. If you are lucky enough to have direct database access and you are doing read only operations, then this will definitely be the fastest. I was not so lucky, so I have to use the web service, which is not bad really.

What data is available?

The first thing you need is the url of the user interface to the MS Dynamics CRM. For example, https://crm or http://crm. For our examples, I’ll assume https is being used, but http should be the same. Once you have that you can go to the url

http://crm/sdk/list.aspx

This will give you a list of all entities that are available for query. It also provides metadata about each of the entities; including columns and their metadata. This is extremely useful.

How to find the web service

There are actually two web services. One that I never use, but you may want or need to, and another that I use all the time.

The first web service is a discovery web service. Its purpose is to get information about the environment that you are in. The url for this web service is

https://crm/mscrmservices/2007/AD/CrmDiscoveryService.asmx

The second (and most used) web service is the main web service and can be found at

https://crm/mscrmservices/2007/CrmServiceWSDL.aspx

You can use those urls in Visual Studio to add a web reference to the appropriate web service(s). This will import a WSDL and convert the WSDL to C# code for you. I recommend you do it this way. However, if you really want to download the WSDL and work with it you can do that also. You can also download the WSDL from the UI.

If you show all files in your solution you can expand your web service reference and look at the Reference.cs. These are the c# datatypes for the entities and properties that we saw when you looked at http://crm/sdk/list.aspx.

Working with the web service

Depending on your security, environment, etc you may need to specify more about the web service than many of the simple examples on the web show. In my case, I had to specify my own Authentication Token that specified my organization name in the CRM.

Below is a method that I created to call instead of calling the standard new CrmService() constructor.

private static CrmService GetWebService()
{
CrmAuthenticationToken token = new CrmAuthenticationToken();
token.AuthenticationType = AuthenticationType.AD;
token.OrganizationName = "YourCompanyNameHere";

CrmService crmService = new CrmService();
crmService.CrmAuthenticationTokenValue = token;
crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;

// big performance improvement for subsequent calls to web service
crmService.PreAuthenticate = true;

return crmService;
}

Where are all the methods on the web service?

When I first looked at the CrmService web service I was shocked. I thought I would see tons of methods that I can call, but instead I found a seven methods. Each one has a different use and each has its own advantages and disadvantages. Click on one of the links below to go to the docs that also contain good examples on using the methods.

Create - Creates an entity instance. Use this to create a new record in the database.

Delete - Deletes an entity instance. Use this to delete an existing record from the database.

Retrieve - Retrieves an entity instance using the specified ID. Use this when you only want one entity and you know its ID. This is the fastest for returning a single row for a given entity. This returns a strongly typed object. Only the properties you specify to be returned are populated in the object though. You can use intellisense on these objects.

RetrieveMultiple - Retrieves a collection of entity instances based on the specified query criteria. This returns a collection of strongly typed objects. Only the properties you specify to be returned are populated in the objects though. You can use intellisense on these objects. Can only operate on one entity, and gives Object Oriented filtering, paging, etc. This is the fastest for returning multiple rows for a given entity.

Update - Updates an entity instance. This uses a strongly typed object. You can use intellisense on these objects.

Fetch - Retrieves entity instances in XML format based on the specified query expressed in the FetchXML query language. This does NOT use strongly typed objects, and intellisense doesn’t work so you will have to lookup everything or use a saved query. Both the query and the results are xml based. This is by far faster than RetrieveMultiple or Execute (assuming you don’t need most of the properties in an entity to be brought back). This is also the only choice when you need to left outer join multiple tables. In general, this is your best choice for reporting because of all the joining, and the selecting properties from multiple entities. It is as close to SQL as you are going to get without being SQL. You have to work a little harder to work with this, but performance and flexibility are maximized. NOTE: Retrieve and RetrieveMultiple are technically faster according to the docs, but I think it depends on the query.

Execute - Executes business logic and special operations using messages. A ton of things (Anything that inherits from Request) can be passed to this method and the results are returned in a Response object. Search the Reference.cs for “ : Request” and you will see all objects that inherit from Request. You can join (link) entities, but only for the filter, not for selecting properties.

Samples

For starters download the SDK, or look on MSDN for the same samples, but on the web.

Execute Example

Below is an example of how you can use the Execute method to return multiple rows (entities) for a given query. You can build complex filters, but you can only select from the entity that the query is for. You specify what properties you want to bring back, and when you access the objects in the response you should only access the properties you specified in the request. Paging is built in and easy to do.

private void GetUsersUsingExecute()
{
RetrieveMultipleResponse results = null;
int pageNum = 1;
while (results == null || (results != null && results.BusinessEntityCollection.MoreRecords))
{

// specify the columns we want to return
ColumnSet cols = new ColumnSet();
cols.Attributes = new string[] { "domainname", "systemuserid" };

// Create the FilterExpression.
FilterExpression filter = new FilterExpression();

PagingInfo pageInfo = new PagingInfo();
pageInfo.Count = 1000; // the number of rows in each batch
pageInfo.PageNumber = pageNum;

// Create the QueryExpression.
QueryExpression query = new QueryExpression();

// Set the properties of the QueryExpression.
query.EntityName = EntityName.systemuser.ToString();
query.ColumnSet = cols;
query.Criteria = filter;
query.PageInfo = pageInfo;
query.Distinct = true;

// Create the request object.
RetrieveMultipleRequest request = new RetrieveMultipleRequest();

// Set the properties of the request object.
request.Query = query;

// Execute the request.
results = (RetrieveMultipleResponse)GetWebService().Execute(request);


BusinessEntity[] users = results.BusinessEntityCollection.BusinessEntities;
foreach (systemuser user in users)
{
Console.WriteLine(user.systemuserid.Value);
Console.WriteLine(user.domainname);
}


pageNum++;
} // end while

}


FetchXML Example

FetchXML is in my opinion the preferred method to retrieve data from the CRM due to its optimal performance and flexibility.

Helper Methods

You will want to use these methods when you are using FetchXML. You can just copy and paste them. They basically make working with the XML much easier.

private string ExtractNodeValue(XmlNode node, string childNodeName)
{
XmlNode childNode = node.SelectSingleNode(childNodeName);
return (childNode == null) ? string.Empty : childNode.InnerText;
}

private string ExtractNodeAttributeValue(XmlNode node, string childNodeName, string childNodeAttributeName)
{
XmlNode childNode = node.SelectSingleNode(childNodeName);
return (node == null) ? string.Empty : GetAttriubteValue(childNode, childNodeAttributeName);
}

private bool HasMoreResults(XmlDocument doc)
{
return GetAttriubteValue(doc.DocumentElement, "morerecords") == "1";
}

private string GetPagingCookie(XmlDocument doc)
{

XmlAttribute attribute = doc.DocumentElement.Attributes["paging-cookie"];
return (attribute != null) ? attribute.InnerXml : string.Empty;
}

private string GetAttriubteValue(XmlNode node, string attributeName)
{
XmlAttribute attribute = node.Attributes[attributeName];
return (attribute != null) ? attribute.Value : string.Empty;
}



private string SetPagingInfo(string fetchXml, string pagingCookie, int pageNumber, int fetchCount)
{
return string.Format(fetchXml, pageNumber, fetchCount, pagingCookie);
}

private string FormatXmlQuery(string xmlQuery)
{
// add some white space for readability
xmlQuery = xmlQuery.Replace("<", "\r\n<");
xmlQuery = xmlQuery.Replace("<link-entity", "\r\n<link-entity");
xmlQuery = xmlQuery.Replace("<filter", "\r\n<filter");
xmlQuery = xmlQuery.Replace("<order", "\r\n<order");
return xmlQuery;
}


XML Query

I store this in an XML file and read it in when I need it. Otherwise, trying to do this inline in code is messy and error prone. I STRONGLY recommend this method or at least not putting inline in your code. This example is complex for several reasons. To start with, there are several entities that are linked together and I am selecting attributes from each of them. There are even some outer joins here. Anything with mycustom_ in the name are properties that are only in my CRM; they are custom properties setup in the CRM. The alias attribute of the link-entity tag is ugly here, but is actually what is generated by the UI. The important thing to know is I can change it here, but it also has to be changed in my code. This xml also has c# placeholders for supporting paging. Also, note that I am not filtering the rows. I am selecting all rows (paged). I could have added a filter though.

<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false" page="{0}" count="{1}" paging-cookie='{2}'>


<entity name="opportunity">

<attribute name="mycustom_opportunity_revenue"/>
<attribute name="mycustom_customer_part_number"/>

<link-entity name="mycustom_project" from="mycustom_projectid" to="mycustom_projectid" visible="false" link-type="outer" alias="opportunitymycustom_projectidmycustom_projectmycustom_projectid">
<attribute name="mycustom_customer_description"/>
<attribute name="mycustom_name"/>
</link-entity>

<link-entity name="systemuser" from="systemuserid" to="owninguser" visible="false" link-type="outer" alias="opportunityowningusersystemusersystemuserid">
<attribute name="domainname"/>
</link-entity>

<link-entity name="account" from="accountid" to="customerid" visible="false" link-type="outer" alias="opportunitycustomeridaccountaccountid">
<attribute name="name"/>
<attribute name="mycustom_baseregion"/>
</link-entity>

<link-entity name="mycustom_mag" from="mycustom_magid" to="mycustom_magid" visible="false" link-type="outer" alias="opportunitymycustom_magidmycustom_magmycustom_magid">
<attribute name="mycustom_name"/>
</link-entity>

<link-entity name="product" from="productid" to="mycustom_12ncid" visible="false" link-type="outer" alias="opportunitymycustom_12ncidproductproductid">
<attribute name="name"/>
</link-entity>



</entity>

</fetch>

The FetchXML code

The constants below MUST match EXACTLY those that are in the XML Query file that is read in the method below. The code below supports paging and reading the XML query from a file. This code requires the Helper methods shown above. There are no strongly typed properties or anything like that. If you need your data as anything except string, you will need to convert it. This is actually a small price to pay for having the most flexibility and performance possible when using the web service (obviously direct database access would be the fastest).

// NOTE: These need to match the XML Query
private readonly string OWNER_ALIAS = "opportunityowningusersystemusersystemuserid";
private readonly string PROJECT_ALIAS = "opportunitymycustom_projectidmycustom_projectmycustom_projectid";
private readonly string CUSTOMER_ALIAS = "opportunitycustomeridaccountaccountid";
private readonly string MAG_ALIAS = "opportunitymycustom_magidmycustom_magmycustom_magid";
private readonly string MYCUSTOM12NC_ALIAS = "opportunitymycustom_12ncidproductproductid";

public void GetOpportunitiesSampleUsingFetchXML()
{
// Set up the CRM Service.
CrmService service = GetWebService();

// Define the fetch attributes.
// The number of records per page to retrieve.
int fetchCount = 500;

// Initialize the page number.
int pageNumber = 1;

// The current paging cookie. For retrieving the first page,
// pagingCookie should be null.
string pagingCookie = null;

// read the query from file
string xmlQueryText = File.ReadAllText("Query.xml");
string fetchXml = xmlQueryText;


while (true)
{
// Build fetchXml string with the placeholders.
string xml = SetPagingInfo(fetchXml, pagingCookie, pageNumber, fetchCount);

// Execute the fetch query and get the xml result.
string fetchResult = service.Fetch(xml);

// Load the fetch result into XMLDocument to parse its cotents.
XmlDocument doc = new XmlDocument();
doc.LoadXml(fetchResult);

// The paging-cookie attribute holds the paging cookie to pass in the next query.
pagingCookie = GetPagingCookie(doc);

// Retrieve the result nodes.
XmlNodeList resultNodes = doc.DocumentElement.SelectNodes("result");

// Retrieve all records from the result set.
foreach (XmlNode resultNode in resultNodes)
{
string region = ExtractNodeValue(resultNode, CUSTOMER_ALIAS + ".mycustom_baseregion");
string mycustom_customer_description = ExtractNodeValue(resultNode, PROJECT_ALIAS + ".mycustom_customer_description");
string mycustom_project_name = ExtractNodeValue(resultNode, PROJECT_ALIAS + ".mycustom_name");
string mycustom_customer_part_number = ExtractNodeValue(resultNode, "mycustom_customer_part_number");
string mycustom_opportunity_revenue = Convert.ToDecimal(ExtractNodeValue(resultNode, "mycustom_opportunity_revenue"));
string ownerAccount = ExtractNodeValue(resultNode, OWNER_ALIAS + ".domainname");
string dwaccount = ExtractNodeValue(resultNode, CUSTOMER_ALIAS + ".name");
string mycustom_mag_name = ExtractNodeValue(resultNode, MAG_ALIAS + ".mycustom_name");
string mycustom_12nc_name = ExtractNodeValue(resultNode, MYCUSTOM12NC_ALIAS + ".name");

string opportunityid = ExtractNodeValue(resultNode, "opportunityid");

}

// Check for morerecords, if it returns 1.
if (HasMoreResults(doc))
{
// Increment the page number to retrieve the next page.
pageNumber++;
}
else
{
// If no more records in the result nodes, exit the loop.
break;
}

} // end while

}


Other Resources for FetchXML

Fetch XML Schema - Docs, Examples, Syntax, etc

Intro to FetchXML and see Other Resources on it for great blogs

Retrieve Example

Here is a tweaked example from the docs for the Retrieve method. This works very similarly to the Execute Example with regards to the QueryExpression.

CrmService service = GetWebService();

// Create the column set object that indicates the properties to be retrieved.
ColumnSet cols = new ColumnSet();

// Set the properties of the column set.
cols.Attributes = new string [] {"fullname"};

// contactGuid is the GUID of the record being retrieved.
Guid contactGuid = new Guid("4D507FFE-ED25-447B-80DE-00AE3EB18B84");

// Retrieve the contact.
// The EntityName indicates the EntityType of the object being retrieved.
contact contact = (contact)service.Retrieve(EntityName.contact.ToString(), contactGuid, cols);

Retrieve Multiple Example

Here is a tweaked example from the docs for the Retrieve method. This works very similarly to the Execute Example with regards to the QueryExpression.

CrmService service = GetWebService();

// Create the ColumnSet that indicates the properties to be retrieved.
ColumnSet cols = new ColumnSet();

// Set the properties of the ColumnSet.
cols.Attributes = new string [] {"fullname", "contactid"};

// Create the ConditionExpression.
ConditionExpression condition = new ConditionExpression();

// Set the condition for the retrieval to be when the contact's address' city is Sammamish.
condition.AttributeName = "address1_city";
condition.Operator = ConditionOperator.Like;
condition.Values = new string [] {"Sammamish"};

// Create the FilterExpression.
FilterExpression filter = new FilterExpression();

// Set the properties of the filter.
filter.FilterOperator = LogicalOperator.And;
filter.Conditions = new ConditionExpression[] {condition};

// Create the QueryExpression object.
QueryExpression query = new QueryExpression();

// Set the properties of the QueryExpression object.
query.EntityName = EntityName.contact.ToString();
query.ColumnSet = cols;
query.Criteria = filter;

// Retrieve the contacts.
BusinessEntityCollection contacts = service.RetrieveMultiple(query);


Generate FetchXML query using UI

If you want to use FetchXML, but are overwhelmed or just don’t want to think that hard you are in luck. The Advanced Find in the user interface allows you to save queries that you build using their ultra cool query builder. Lucky for us they are saved in FetchXML format, so all we have to do is read the saved query from the CRM and tweak it for our uses.

The code to Extract the Saved Query from the CRM

public string GetUserQuery(string queryName)
{
// specify the columns we want to return
ColumnSet cols = new ColumnSet();
cols.Attributes = new string[] { "fetchxml"};

// Create a ConditionExpression.
ConditionExpression nameCond = new ConditionExpression();
nameCond.AttributeName = "name";
nameCond.Operator = ConditionOperator.Equal;
nameCond.Values = new object[] { queryName};

FilterExpression filter = new FilterExpression();
filter.FilterOperator = LogicalOperator.And;
filter.Conditions = new ConditionExpression[] { nameCond };

// Create the QueryExpression.
QueryExpression query = new QueryExpression();
query.EntityName = EntityName.userquery.ToString();
query.ColumnSet = cols;
query.Criteria = filter;
query.Distinct = true;

// Create the request object.
RetrieveMultipleRequest request = new RetrieveMultipleRequest();
request.Query = query;

// Execute the request.
RetrieveMultipleResponse results = (RetrieveMultipleResponse)GetWebService().Execute(request);

if (results.BusinessEntityCollection.BusinessEntities.Length > 0)
{
userquery result = (userquery)results.BusinessEntityCollection.BusinessEntities[0];

string xmlQuery = result.fetchxml;
xmlQuery = FormatXmlQuery(xmlQuery);
return xmlQuery;

}
else
{
return string.Empty;
}



}

Just call the method and write the results to a file or console. Then

string xmlQuery = p.GetUserQuery("My Custom Query");
Console.WriteLine("Saved Query: " + xmlQuery);

Once you have the query, you will need to modify the fetch tag to look like the one in my FetchXML example.

Here is an alternate way to do the same thing. Be careful if your query is complex with outer joins, etc, you will lose some of that when you convert to a QueryExpression. If you have the ID or get it as described in the alternate way link, you can use the code below to get the actual FetchXML stored in the CRM.

public string GetUserQuery(Guid queryId)
{
CrmService ws = GetWebService();
userquery userQuery = (userquery)ws.Retrieve(EntityName.userquery.ToString(), queryId, new AllColumns());
string xmlQuery = userQuery.fetchxml;
xmlQuery = xmlQuery.Replace("<", "\r\n<");

return xmlQuery;
}

Switching Between FetchXML and QueryExpression

There are two methods that all you to switch between FetchXML and QueryExpression. They are FetchXmlToQueryExpression and QueryExpressionToFetchXml. See here for more details on the subject. A word of warning, the documentation does NOT tell you this, but you cannot do outer joins or handle selects from anything but the main entity. If this is possible, someone please let me know. For example, if I try to convert the XML in the FetchXML example to a QueryExpression, I will lose the columns I selected when I look at the results. On the other hand, I can go from any QueryExpression to FetchXML syntax without any problem because FetchXML is much more expressive.

Additional Resources

After you read my entry, you may want to start here for info on how to use queries, etc.

Top most Docs for all versions of Microsoft Dynamics CRM at: http://technet.microsoft.com/en-us/library/bb496810.aspx

Tuesday, November 17, 2009

How to use the Ajax Toolkit AutoCompleteExtender

I can never seem to remember the basics of how to use the AJAX Toolkit AutoCompleteExtender. It isn’t that it is all that difficult, it is just that there are things that I forget. The sample page is actually quite useful.

Step 1: Create a Web Service that you can call.

First be sure to uncomment or add the ScriptService attribute to your web service. It should look like this and be above the class declaration.

[System.Web.Script.Services.ScriptService]

The second thing to know is when you create your web method you MUST use very specific parameters for your web method. The one exception (sort of) is if you use the ContextKey property, but even then you are only adding a parameter. The name of the method is NOT important because you specify that in the extender properties. Your method must have two parameters named EXACTLY as shown below and in the order below. The return type can be string[] or List<string> because they both end up as string[] when sent as xml.

[WebMethod]
public List<string> GetEmails(string prefixText, int count)
{
…
}

Step 2: Add the Extender to the page or user control where you want to use it.

I would start by pasting the below on your page or user control.

<ajaxToolkit:AutoCompleteExtender
runat="server"
ID="myAutoComplete"
TargetControlID="txtSomeField"
ServicePath="~/MyWebService.asmx"
ServiceMethod="GetEmails"
MinimumPrefixLength="3"
CompletionInterval="300"
EnableCaching="true"
CompletionSetCount="50"
DelimiterCharacters=";, :" />
Then paste the following under your <%@ Control… > or <%@ Page …> tags (see the first line in the file).
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>
You will notice that my example doesn’t contain any animations, javascript, etc. I personally like it with no animation. The default seems zippier when I type. It also has the big advantage of making it very simple to put this in a user control and have that user control used multiple times on a page without issue. You can apparently still get it to work, but I don’t think it is worth it. Check out the solution posted here to see how to make it work. The sample shown on the sample page provided with the toolkit just won’t work if you use the extender multiple times on a page.

You may also notice I removed the stylessheet references, and the BehaviorID (which is in the sample project, but not in the documentation) is also not there. You do NOT want to set this if you use this multiple times on a page.

You can tweak the other parameters if you like. The ~ in the ServicePath only works for Web Sites, not Web Applications from what I understand. Please let me know if I am wrong since I have not actually done so. I did find that I needed the ~ if everthing is not at the top level in the web site. For example, I used the extender on a user control that is in a Controls directory under the root. The MyWebService.asmx is a the top root, so some kind path is needed. http://….MyWebService.asmx will also work, but that is difficult to make production and dev difficult to work with.

Step 3: Add ScriptManager

You will need either the ToolkitScriptManager or the ScriptManager on the page or a master page, etc for any AJAX stuff to work. Just remember, you can only have one of these on a page when the page is rendered. That means you can’t have it on a master page and on a user control.

Thursday, August 27, 2009

How to get the current user from a Silverlight application

First, when I say current user, I am assuming that you are using NTLM / Windows Authentication / Active Directory. In a ASP.NET website, you would use something like User.Identity.Name. However, in Silverlight there is no such thing. So, the short answer appears to be that you can’t. At least not directly from Silverlight.

The trick is in what other tools do we have that we can use. One option ASP.NET and Javascript combination to get the value from ASP.NET, pass it to the Silverlight control as an initparam, and use it in the Silverlight app. This is is a HUGE security hole. I can’t believe people actual accept this as an option. All someone would have to do is create a .html page that has the Silverlight application object on it, and set the username to some admin user, and there is full access, no password needed. For the code, click here.

Needless to say, I don’t think the above technique is an option. The best option by far is to create a web service and get the value from it. While it is still possible to fake the response from the web server, would be a bit more difficult and would require faking a network route or something similar.

The best option if you can help it is to never need it from Silverlight. Always use this value from the web service that your Silverlight app uses already. That way there is nothing to fake, etc. It is all server-side then. If you have to have the username in the actual Silverlight application, then I recommend don’t use it for security purposes without doing server-side validation also. Think of Silverlight as a first line of defense, and the server-side (web service) being made more robust.

In case, you don’t know what the web service method would look like, below is an example.

public string GetCurrentUsername()
{
return User.Identity.Name;
}

Wednesday, August 6, 2008

Automatically backup and tag a release using Subversion

In a previous entry I discussed automating the backup and tagging of a project using Visual Source Safe (VSS). Today, I want to do the same thing, but using Subversion. The example below backs up a MyApp.war file on my Tomcat server and copies it to my local backup directory. It then pauses to remind me to check in all my changes (into Subversion). Finally, it creates a tag (like a label for your VSS folks) that includes the date and time the tag was created and notes that it is a deployment tag. @echo off set NOW=%date:~10,4%-%date:~4,2%-%date:~7,2%--%time:~0,2%.%time:~3,2% set NOW_FRIENDLY=%date:~10,4%-%date:~4,2%-%date:~7,2% %time:~0,2%:%time:~3,2% zip -r "C:\backups\MyApp-prior%NOW%.war.zip" "\\myServer\c$\Program Files\Apache Software Foundation\Tomcat 6.0\webapps\MyApp.war" echo Backup of web site has been saved at: "C:\backups\MyApp-prior%NOW%.war.zip" echo Please make sure all changes are checked in pause svn copy svn://subversionServer/MyRoot/MyApp/trunk svn://subversionServer/MyRoot/MyApp/tags/DEPLOYED-%NOW% -m"DEPLOYED %NOW_FRIENDLY%" echo A label has been created in Subversion called: DEPLOYED-%NOW% pause

Thursday, March 20, 2008

Lessons learned with Web Services in Eclipse 6

I am using MyEclipse 6. It is quite nice. I learned the hard way were to find some resources and how to do things like Exception handling. I found the process to be much more difficult and the learning curve much steeper than I thought it would be. Here are some things I learned. MyEclipse has a project type called Web Service Project. Use this to start a new project that will be your web service. It also allows you to use XFire web services (not CXF until version ?? of MyEclipse). You can use Java 1.5 or Java 5. Getting Started:
Start with the tutorial I mention in my blog to get started: http://www.myeclipseide.com/documentation/quickstarts/webservices/
Examples: Get the XFire distribution (http://xfire.codehaus.org/Download and pick the Binary Distribution in zip package for the newest release) because it has some good examples in it. I didn't find this until way too late. You can also download the source code for XFire on this page if you want to also. Once you download the zip I highly recommend the Book example. It shows how to return complex types, and how to create your own custom exceptions. How to store application settings
I am sure there are many ways to do this... check out my blog on: http://justgeeks.blogspot.com/2008/03/storing-application-settings-in.html Make Complex types that use java.util.Date as a data type be nillable when generated in the WSDL.
The basic solution is create a file called YourClassNameHere.aegis.xml and put it in the same location as the class that defines the complex type that contains the java.util.Date property. In the file, do something like this:
<mappings>
<mapping>
<property name="datePropertyName" nillable="true"/>
<property name="anotherDatePropertyName" nillable="true"/>
</mapping>
</mappings>
Now your automatically generated WSDL will have nillable="true" for the properties that you specified in the mapping file. This is particular important when using ASP.Net 2.0 and Nullable Types to consume your web service. It means the difference between DataTime and DateTime?. For more details check out: http://www.nileshk.com/node/69#comment-12609 Exception Handling You can follow the example in the Book example noted above, or you can do it another way also. You can do much like they did in the Book example, except just have your custom exception class extend java.lang.Exception instead of the XFire specific type. This will cause your custom exception class to be serialized and set as the Detail property in the SoapException. You can then parse it using a standard XML parser on the client side. You will be catching an Exception of type SoapException on ASP.Net. If you want your properties of your custom exception class to show up in the serialized text, you will need to make sure that you have setters and getters for each member variable in your class. One key is that your web methods must explicitly throw your custom exception, not just java.lang.Exception. If you don't, the Detail property of the SoapException won't be filled in when it is serialized and sent to your client (web service consumer). This basically tells Aegis to use our custom exception type instead of just java.lang.Exception. Changing parameters (in0, in1, in2, etc) to the real names when the WSDL is generated. I searched long and hard to find a simple solution for this one. Much to my amazement there did not seem to be too many intuitive solutions. I picked the easiest one. My solution is very simple and requires no maintenance. It seems to be a bug to me, so.... Do just like the tutorial referenced in the Getting Starting section of this blog and add the new web service just as says EXCEPT when you get to the page that asks you for the Service Interface and Service impl. class, set the Service interface to the same thing as Service impl class. This will affect your server.xml file. Finish the wizard as noted in the tutorial. Next, open the xxxImpl.java file and remove the implements xxxx from the class declaration such that it does not implement the interface file. If there is an interface file you can safely remove it from your project now. You will never need it now. Now, when the WSDL is generated it will use the proper parameter names as you would expect. Go figure! Word of caution, I did not have as much luck doing effectively manually doing the same thing AFTER I did the wizard as the tutorial suggests. So, I suggest doing it from the start when you do it in the wizard. I also found that I couldn't query the for the WSDL without my CPU going to nearly 100% when I did it manually. I don't really know why, but it did. I suspected that maybe I just had to many methods in my class and was slowing the parsing of the class to generate the WSDL or something, so I put all my real code in another class and just have stub like web methods that simply call the other object and a method on it. This seemed to help, but all these issues could have been do to needing a reboot and trying too many different things and messing up some files. Hard to tell, so let me know if anyone else has a similar experience. Other Tips Make your life easier, just forget about getting access to the Request and other servlet type things. It is just too much of a pain. Stay away from web.xml when it comes to storing application settings. Accessing it programmatically seems to be container (application server) specific, so use a properties file or xml file.

Storing application settings in a property file

This is not a totally necessary class, but it doesn't abstract loading text from a properties file. For example if you need to store application specific settings or even localizations you can use this class to access it using flexible paths (dot notation or directory path style). Here is how to use it: Properties props = PropertyLoader.loadProperties("/some/package/Config.properties"); String val = props.getProperty(key); package mypackage;
import java.util.ResourceBundle; import java.util.Properties; import java.io.InputStream; import java.util.Locale; import java.util.Enumeration;
public class PropertyLoader { /** Creates a new instance of PropertyLoader */ public PropertyLoader() { } /** * Looks up a resource named 'name' in the classpath. The resource must map * to a file with .properties extention. The name is assumed to be absolute * and can use either "/" or "." for package segment separation with an * optional leading "/" and optional ".properties" suffix. Thus, the * following names refer to the same resource: * <pre> * some.pkg.Resource * some.pkg.Resource.properties * some/pkg/Resource * some/pkg/Resource.properties * /some/pkg/Resource * /some/pkg/Resource.properties * </pre> * * @param name classpath resource name [may not be null] * @param loader classloader through which to load the resource [null * is equivalent to the application loader] * * @return resource converted to java.util.Properties [may be null if the * resource was not found and THROW_ON_LOAD_FAILURE is false] * @throws IllegalArgumentException if the resource was not found and * THROW_ON_LOAD_FAILURE is true */ public static Properties loadProperties (String name, ClassLoader loader) { if (name == null) throw new IllegalArgumentException ("null input: name"); if (name.startsWith ("/")) name = name.substring (1); if (name.endsWith (SUFFIX)) name = name.substring (0, name.length () - SUFFIX.length ()); Properties result = null; InputStream in = null; try { if (loader == null) loader = ClassLoader.getSystemClassLoader (); if (LOAD_AS_RESOURCE_BUNDLE) { name = name.replace ('/', '.'); // Throws MissingResourceException on lookup failures: final ResourceBundle rb = ResourceBundle.getBundle (name, Locale.getDefault (), loader); result = new Properties (); for (Enumeration keys = rb.getKeys (); keys.hasMoreElements ();) { final String key = (String) keys.nextElement (); final String value = rb.getString (key); result.put (key, value); } } else { name = name.replace ('.', '/'); if (! name.endsWith (SUFFIX)) name = name.concat (SUFFIX); // Returns null on lookup failures: in = loader.getResourceAsStream (name); if (in != null) { result = new Properties (); result.load (in); // Can throw IOException } } } catch (Exception e) { result = null; } finally { if (in != null) try { in.close (); } catch (Throwable ignore) {} } if (THROW_ON_LOAD_FAILURE && (result == null)) { throw new IllegalArgumentException ("could not load [" + name + "]"+ " as " + (LOAD_AS_RESOURCE_BUNDLE ? "a resource bundle" : "a classloader resource")); } return result; } public static Properties loadProperties (final String name) { return loadProperties (name, Thread.currentThread ().getContextClassLoader ()); } private static final boolean THROW_ON_LOAD_FAILURE = true; private static final boolean LOAD_AS_RESOURCE_BUNDLE = true; // if true localization and caching is used. private static final String SUFFIX = ".properties"; }

Monday, March 17, 2008

Creating a web service in MyEclipse 5 or 6 and deploying to Tomcat application server.

Creating a web service in MyEclipse 5 or 6 and deploying to Tomcat application server.
Well, I can't say it is difficult, but I think the link below that points to a MyEclipse tutorial is great. It gave me everything I needed to get started.
The tutorial is also in the documentation included in MyEclipse 6 (maybe 5 also, I don't know). The tutorial is called "MyEclipse Code-First Web Services Tutorial" if you want to search the included doc for it. It is the same either way. Your choice. ;)
Enjoy!

Tuesday, February 27, 2007

Java Application Server start and stop manually

Starting the Java Application Server that comes with Java Studio Enterprise 8 by running the start-domain is not recommended due to parameters that are expected by the batch file. Consequently the batch file hangs. To properly start the Java Application Server, open a command prompt and type: Asadmin start-domain

To properly stop the Java Application Server, open a command prompt and type: Asadmin stop-domain

Tuesday, January 16, 2007

Java Web Service Exception Handling

This assumes that you are using JAX-RPC which uses SOAP 1.1 to implement a web service, and that the web service is being consumed by .Net (1.1 or 2.0). If you are using WS 2.0 there are some different options.

 

While it is possible to just throw an plain old Exception in your java web service, it will show up your .Net client as a exception. The problem is determining what exception you have catch once you get it. In this situation we need a code that can be used in the client to identify the exception without try to rely on the message of the exception since this is typically semi user-friendly text. Exception class doesn’t have the concept of a code to identify the exception. The good news is SOAPFaultException has the concept of a code and it also allows you to attach any other data that may be useful.

 

Creating a SOAPFaultException can be a little messy, so I recommend created a convenience method to encapsulate this. In case this isn’t clear, this goes in your java web service.

 

// Creates and returns a SOAPFaultException. Throws one if there is an error creating

// Should be Client or Server

// If it is Client then message should NOT be resent without change

// i.e. Server.Fault or Client.Fault

public SOAPFaultException NewException(String faultType, String errorCode, String errorMessage, String webServiceOperationName) throws SOAPException

{

 

       // The faultcode element provides an algorithmic mechanism for identifying the fault.

//SOAP defines a small set of SOAP fault codes covering basic SOAP faults.

       QName faultCode = new QName("http://FrontLineWebService/" + webServiceOperationName, faultType + "." + errorCode);

 

       // The faultstring provides a human-readable description of the SOAP fault and is not intended for algorithmic processing.

       String faultString = errorMessage;

 

       // The faultactor element provides information about which SOAP node on the SOAP message path caused the fault to happen.

//It indicates the source of the fault.

       String faultActor = "http://FrontLineWebService/" + webServiceOperationName;

 

       // The detail element is intended for carrying application specific error information related to the SOAP Body.

       Detail faultDetail = SOAPFactory.newInstance().createDetail();

 

       // Define what the body of the email should be in terms of XML. You can put whatever data you want here

       faultDetail.addChildElement("ErrorMessage").addTextNode(errorMessage);

       faultDetail.addChildElement("ErrorCode").addTextNode(errorCode);

 

       // for more info on params: see http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383507

       throw new SOAPFaultException(faultCode, faultString, faultActor, faultDetail);

 

}

 

Here is an example of how you would throw the SOAPFaultException in your web service.

 

 

public void testMethod() throws Exception, SOAPFaultException, java.rmi.RemoteException

{

 

       throw NewException("Server.Fault", "TEST_CODE", "This is only a test description in English", "testMethod");

}

 

 

Now that you can easily throw a SOAPFaultException (SoapException in .Net), you are all set. Here is an example of catching it in your .Net client.

 

using System.Web.Services.Protocols;

...

 

try

{

    MyWS ws = new MyWS();

 

    ws.testMethod(new testMethod());

   

}

catch (SoapException ex)

{

    if (ex.Code.Name == "Server.Fault.TEST_CODE")

    {

        // do stuff here

    }  

}