mikejuniper.com

April 9, 2010

Google Maps Extender for the JS API and SSL

Filed under: Uncategorized — mike @ 2:56 pm

I recently needed to enable SSL on an ArcGIS Server map service that I was consuming with the Google Maps Extender for the Javascript API. This was not as straightforward as I expected so I thought I’d post about some of the pitfalls that I ran into. Note that I did this on the ArcGIS Server 10 pre-release but previous versions should be similar. I’ll do this in the form of a checklist and I’m just gonna hit the high points. Lots more information can be found at the links provided or you can post a comment and I can elaborate:

  1. Before you do anything, change the ‘Clear Cache Options’ for ArcGIS Server to periodic and set the interval to something very short like 1 minute. That way you won’t have to remember to clear the cache every time you make a change. Don’t forget to change this setting to something more reasonable when you’re all done.
  2. For this project I was planning to implement token based security too and was just gonna take it one step at a time so I would know why stuff broke when it inevitable did. So the first step was to implement ESRI’s proxy page. The instructions here are very good.
  3. Then you’ll want to configure the Google Maps Extender to use the proxy. You do that with javascript like this:
        esri.arcgis.gmaps.Config.proxyUrl = 'proxy.ashx';
        esri.arcgis.gmaps.Config.alwaysUseProxy = true;
  4. Here’s where I ran into my first problem. My dynamic map service layer wasn’t coming through. I fired up Firebug and saw the problem: esri.arcgis.gmaps.DynamicMapServiceLayer do an export map request then they do a ‘Get’ for the map image using the url returned from the export map request. The url for the map image pointed to the arcgisoutput directory which was not configured in my proxy. There are a couple of ways you could fix this problem but I hit it with a hammer: I just configured the proxy to proxy all requests to that hostname. And it worked!
  5. Get an SSL certificate for your ArcGIS Server. ESRI’s instructions here were helpful. I used a self signed certificate since this is for development/ testing.
  6. Next you need to setup your map service to require ssl. You do this in ArcCatalog at the folder level. So open ArcCatalog, connect to the server, right click on the folder that contains your map service, select ‘Properties’, check the ‘Require Encrypted Access’ box, and click ‘OK’.
  7. Now you can change the url’s in your proxy config and your javascript to point to the https url.
  8. Here’s where I ran into problems. The webserver where the proxy was running was not accepting the certificate of the ArcGIS Server. I know what to do, I thought, so I tried to hit the page in IE from the webserver and, as expected, got a message saying the certificate wasn’t trusted. So I went to the page anyway then viewed the certificate. Then I installed the certificate. But when I ran the app again, the certificate was still refused. Finally, I found this post which pointed out that I had just installed the certificate for the logged on user. Follow the link for instructions on how to install it for the local machine.
  9. But then it still didn’t work. So I fired up Firebug and saw what was happening: Remember above where I talked about how Dynamic Map Service Layers work? Well what was happening now was that the output directory url that was being returned by the export map request wasn’t an ssl url so since my proxy was only configured with the https url, it wouldn’t proxy the map image requests. I didn’t find any documentation on this (I didn’t look very hard) but I fixed it by adding another output directory just for ssl and configuring my map service to use it. You do this in ArcCatalog: Right click on your connection to the ArcGIS Server, click ‘Server Properties’, and go to the ‘Directories’ tab. Select ‘Output Directory’ from the ‘Directory Type’ list and then click add. Now give it the path to the directory you want to use (I created an arcgisoutputssl directory right next to the arcgisoutput one) and give it a virtual directory(I used https://<hostname>/arcgisoutputssl) and click ‘OK’ and ‘OK’.
  10. Now at this point you might be thinking that you’re done as I was. But it still didn’t work. I was getting 404’s from the map image requests. I assumed since the AGS post install creates those virtual directories that when I added an output directory it would create its virtual directory for me. But it doesn’t, so go ahead and create your virtual directory and don’t forget to set it to require ssl.

That’s it! Hope this helps somebody.

jQuery rocks

Filed under: Uncategorized — mike @ 2:03 pm

I’ve previously posted a bunch on dojo, which is a very fine javascript library. But in the last year or so I’ve been using jQuery a bunch and I’ve got to say it is great. The dom selection and manipulation is more full featured and easier to use than dojo’s. One thing that dojo has going for it is the classes and inheritance (dojo.declare, dojo.provide, dojo.require) but on a lot of projects, you just don’t need that. It’s interesting that both libraries recently released version 1.4 and at that version they both seem to have adopted some features of the other which I think is great.

Expect to see some posts on jQuery from me in the future. If you haven’t tried jQuery yet, you should.

August 7, 2009

Asp.Net MVC + dojo dialog update

Filed under: MVC, dojo — mike @ 9:09 am

I’ve made some changes to the showDialog function I discussed in this post. I’ve made it more generic – it now takes dialogId, href, title, queryString, and onDownloadend (a function). That way I can use it to open any dialog in my application. I’m also now not destroying the dialog if it already exists, I’m just resetting the onDownloadEnd and title properties and using my custom setTitle method:

//extend dojo dialog to have a setTitle method
dojo.extend(dijit.Dialog, {
  setTitle: function(/*string*/title)
  {
     this.titleNode.innerHTML = title || "";
  }
});

Finally, I’m using the href property instead of setHref which is deprecated. Here’s the new showDialog function:

showDialog: function(dialogId, href, title, onDownloadEnd)
{
  //first check if it's there so we don't create a duplicate
  var dialog = dijit.byId(dialogId);
  if (dialog)
  { //it already exists, reset a few properties
    dialog.setTitle(title);
    dialog.onDownloadEnd = onDownloadEnd;
    dialog.href = href;
  }
  else
  { //it doesn't exist yet, so create it
    dialog = new dijit.Dialog({
      refreshOnShow: true,
      id: dialogId,
      href: href,
      title: title,
      onDownloadEnd: onDownloadEnd,
      onDownloadError: dojo.hitch(this, '_dialogDownloadError', dialogId)
    });
  }

  //show it
  dialog.show();
}

August 5, 2009

HtmlHelper methods and dijit widgets

Filed under: .NET, MVC, dojo — mike @ 3:46 pm

This is certainly not earth-shattering but it’s pretty cool. Asp.Net MVC HtmlHelper methods return a string of html and are used to render html elements. They are cool because you can use them to change the way the control is rendered at runtime based on what you’ve got in ViewData or your model. What’s even cooler is that you can pass html attributes (including custom ones like dojoType) to the HtmlAttributes parameter. This allows you to use HtmlHelper methods to render dijit widgets like so:

<%=Html.TextBox("search_StreetAddress",
  Model.Criteria.StreetAddress,
  new { dojoType = "dijit.form.TextBox", trim = "true" })%>

<%= Html.DropDownList("CityId", Model.CitySelectList,
  new { dojoType = "dijit.form.FilteringSelect" })%>

<%= Html.TextBox("Year", Model.Year,
  new { dojoType = "dijit.form.NumberTextBox",
  constraints = "{min:1900, max:2500, pattern:'0000'}",
  required = "true",
  rangeMessage = "A 4 digit year is required",
  invalidMessage = "A 4 digit year is required" })%>

Using dijit.form.DateTextBox with Asp.Net

Filed under: .NET, dojo — mike @ 2:42 pm

Since there’s no date literal in javascript, there’s no standard way of serializing/ deserializing dates. MS Ajax uses \/Date(<ticks>)\/ where ticks is the number of milliseconds since midnight 01/01/1970. It’s a reasonable approach – unambiguous and easy to transform in both directions. For a while I was transforming this date object into a date before sticking it into a dojo DateTextBox, and transforming it back before sending it up to the server. But then I got smart and extended dijit.form.DateTextBox to handle this itself. Here’s the code:

dojo.provide('mjuniper.widgets.DateTextBox');
dojo.require('dijit.form.DateTextBox');

/*A dojo datetextbox that handles sending dates
to and recieving dates from asp.net*/
dojo.declare('mjuniper.widgets.DateTextBox',
  dijit.form.DateTextBox,
  {
      // prevent parser from trying to convert to Date object
      value: "",

      postMixInProperties: function()
      {
        this.inherited(arguments);

      //if it's null, return
      if (!this.value)
      {
        return;
      }

      if (this.value.indexOf('/Date(') > -1)
      {
        //extract the ticks from the json object
        var ticks =
          this.value.substring(this.value.indexOf('(') + 1);
        var endChar = (ticks.indexOf('-') === -1) ? ')' : '-';
        ticks = ticks.substring(0, ticks.indexOf(endChar));
        //instantiate a date
        this.value = new Date(parseInt(ticks));
        //if it's invalid, set value to null
        if (this.value == 'Invalid Date') { this.value = null; }
      }
    },

    /*override the serialize method to write
    back to the server in proper format*/
    serialize: function(dateObject, options)
   {
      return '\/Date(' + dateObject.getTime() + ')\/';
    },

    /*override setValue so we can set it with the
    json date object we get from .net*/
    setValue: function(dateString)
    {
      if (dateString.indexOf('/Date(') > -1)
    {
    //extract the ticks from the json object
    var ticks =
      dateString.substring(dateString.indexOf('(') + 1);
    var endChar = (ticks.indexOf('-') === -1) ? ')' : '-';
    ticks = ticks.substring(0, ticks.indexOf(endChar));

    arguments[0] = new Date(parseInt(ticks))
    this.inherited(arguments);
    }
  }
});

There are obviously a number of ways I could have handled extracting the ticks from the json string (including a regex, but then I would have had two problems). This one is not very elegant but it’s relatively easy to understand and I was able to get it working quickly.

My dojo top ten (part 3: hitch)

Filed under: dojo — mike @ 12:46 pm

I think I will call my series of posts dealing with dojo.query and NodeList part two of my dojo top ten and move on to part three.

I’ve come to rely heavily on dojo.hitch but at first I didn’t understand it at all. And I haven’t found many good explanations of it online (except this one) so I’m going to try my hand at a simple concise explanation of hitch.

Here we go: hitch returns a function in which ‘this’ will be whatever is specified as the first argument to hitch.

I’m now using it elsewhere but hitch is particularly useful with callback functions. So here’s an example:

dojo.xhrGet({
  url: 'http://mywebservice.com/helloworld',
  load: helloworldCallback

So we’re calling a webservice and specifying the callback function as helloworldCallback. This is all well and good but if you try to use ‘this’ in helloworldCallback, say to call other functions in the same module, it may not be what you expect it to be. Enter hitch:

dojo.xhrGet({
  url: 'http://mywebservice.com/helloworld',
  load: dojo.hitch(this, 'helloworldCallback')

By using hitch in the above example, we guarantee that when we hit the callback function, ‘this’ will refer to what we expect it to (in this case the module that contains both helloworldCallback and the function that does our webservice call).

dojo.query – putting it all together

Filed under: .NET, MVC, dojo — mike @ 11:24 am

So I guess this will be the finale to my series of posts on dojo.query. I’m using dojo.query to validate data entered into dojo dialogs. In this post, I discussed getting dialog content from an Asp.Net MVC controller method. In the onDownloadEnd function, I do any setup of the dialog that is necessary including something like this:

var onValueChanged =
        dojo.hitch(this, this.dialogValueChanged, dialogId);
//gotta be keyup -
        //validation is out of sync if you use keydown or keypress
dojo.query('input[type="text"], textarea', dialogId)
        .connect('onkeyup', onValueChanged);
dojo.query('select', dialogId)
        .connect('onchange', onValueChanged);
dojo.query('.dijit', dialogId).widgets()
        .connect('onChange', onValueChanged);

This query’s for input elements in the dialog and connects each element’s appropriate method to the function where I do the validation (dialogValueChanged). Note the use of the widgets method discussed in this post.

Here’s the validation function:

dialogValueChanged: function(dialogId)
{
  //check if everything is valid
  var valid = dojo.query('[widgetid], [widgetId]', dialogId)
    .widgets().every(function(widget)
    {
      if (widget.isValid)
      {
        return widget.isValid();
      }
      //if it is not a widget
      //or does not have an isValid method, return true
      return true;
    });                                

    //enable/ disable the save button based on whether it's valid
    var action = (valid) ? 'removeAttr' : 'attr';
    dojo.query('.dialogSaveBtn', dialogId)
	  [action]('disabled', 'disabled');
}

Here we’re querying for widgets and using our previously discussed widgets method to get the actual widgets (not just the dom nodes). Then we use the every method of NodeList to call the isValid method on each widget. The every method will return false if any of the widgets’ isValid methods returned false. Then we use the value returned from every to set our ‘action’ variable and use query again to enable or disable our save button(s) by adding or removing the disabled attribute. Note the use of the removeAttr method we added to NodeList in this post. Note also that this all depends on all the input elements on the dialog being dijit widgets. This mechanism can be easily extended to handle non-dijit elements too.

April 16, 2009

More dojo array processing goodness

Filed under: dojo — mike @ 8:12 am

I use dojo.forEach everywhere. One thing that I occasionally need that dojo.forEach can’t do (as far as I know) is break out of the loop early. Thanks to Eugene Lazutkin I realized that I can just use dojo.some or dojo.every to break early. By the way, you should read that whole article – there’s lots of good stuff there.

Let’s say that you’ve got an array of objects and you need to know the index of one of the objects in the array based on a property value. Consider these two functions that will both accomplish this:

GetArrayItemIndex: function(ary, item, fieldName)
{
    var i = -1, result;
    dojo.forEach(ary, function(aryItem)
    {
        i++;
        if (aryItem[fieldName] === item[fieldName]) { result = i; }
    });

    return result;
}

GetArrayItemIndex2: function(ary, item, fieldName)
{
    //using some instead of forEach to break early
    var i = -1;
    dojo.some(ary, function(aryItem)
    {
        i++;
        return (aryItem[fieldName] === item[fieldName]);
  });

      return i;
}

GetArrayItemIndex uses dojo.forEach so it will go through every item in the array even if the item we’re looking for is at index 0. GetArrayItemIndex2 uses dojo.some to short-circuit the loop when we locate the item we’re looking for. I’ve been wondering about this for a while – I’m glad I finally googled it today and discovered Eugene’s post.

March 27, 2009

Fun with dojo.io.iframe.send

Filed under: dojo — mike @ 3:06 pm

One of my projects has had functionality to export a map image for some time. To get the map image, I just call (javascript) window.open and point the url to my http handler that will return the map image (with additional querystring parameters that specify the map parameters). This was working fine but now the customer wants to include lots of other markup on the map, including stuff they’ve drawn on the map with the nifty drawing tools I’ve provided them. This presented a bit of a problem because if I continued to just use window.open, the querystring was gonna get really long. So I realized I’d have to post the map parameters to my handler. But as far as I know, there’s no way to post data when you call window.open. So what to do?

The first thing I thought of was that I could post the parameters using dojo.xhrPost and then prepare the image and return the url to it. Then I’d call window.open with the url returned from the first request. I didn’t really like that. For one thing, it would be two requests, but for another, I’d have to keep that map image around in some manner.

So I decided to use dojo.io.iframe.send. This ended up working very well but I did run into a few issues that I thought I’d share.

One thing that I didn’t realize at first is that dojo expects the response to be an html document. And if you want to return anything other than an html document (json or text for example), you must wrap it in an html textarea. Dojo will look inside the first textarea in the response document and return to your handle, load, or error functions whatever it finds there in the format you specified in handleAs (defaulting to text if you did not specify one).

Another thing I ran into was that my dojo.io.iframe.send call would only work once per page load. I’m not exactly sure why that is. I found a suggestion that you set a timeout on the __IoArgs object passed to iframe.send. That worked in the sense that I could call it again after the timeout expired but I have no idea how long this operation is going to take so it’s hard to know what to set the timeout to. My solution was to keep a reference to the dojo.Deferred object returned by iframe.send and call cancel on it before trying to send another request like so:

if (this.getMapImageDeferred)
{
  this.getMapImageDeferred.cancel();
}


This seems to work fine but I do have to check in my errBack function whether the request was canceled:

_mapImageCallbackError: function(response, ioArgs)
{
  if (response.message === 'Deferred Cancelled')
  {
    return response;
  }
    // handle the error...
},


The last thing I ran into really had me stumped. I was setting method = “post” in the __IoArgs object passed to iframe.send but it was always using get. I read a few things here and there that kind of seemed to suggest that if I passed a form with its method set to ‘post’ into __IoArgs that might do the trick. And it did! So here’s what I’m doing now:

//create a form with method=post
var form = document.createElement('form');
dojo.attr(form, 'method', 'post');
document.body.appendChild(form);

//create my postdata
var content = {
  /*set a bunch of parameters here that
  will be posted to the http handler*/
};

//make the request
this.getMapImageDeferred = dojo.io.iframe.send({
  url: path + 'map.mapimage.aspx',
  form: form,
  content: content,
  error: dojo.hitch(this, this._mapImageCallbackError)
});

//get rid of the form created above
document.body.removeChild(form);


I’d be interested to know if anyone else has run into these issues and how you resolved them.

March 19, 2009

Re-throwing exceptions in .Net

Filed under: .NET — mike @ 1:35 pm

I knew that I read somewhere that there was a very important difference between throw; and throw ex; when you catch an exception and want to re throw it. But I couldn’t remember what the difference was. Thanks to this post I now know. If you use throw ex, the stack trace info will be overriden. So it is almost always preferable to use just throw.

Older Posts »

Powered by WordPress