Thursday, April 5, 2012

How to Improve SyntaxHighlighter

SyntaxHighlighter is a javascript library that I use on this blog to colorize and display my code snippets. SyntaxHighlighter is a great tool and the author deserves a lot of respect, but the default "select all" and "copy and paste" functionality can be somewhat frustrating. For the last few days I've been troubleshooting various issues with the "select all" and "copy and paste" functionality. Depending on the configuration and the browser, sometimes it copies the text with non-breaking spaces which causes errors in certain IDEs. Other times it copies the text with the wrong indentation or with no line breaks at all.

Even the fact that you can quickly select all of the text is not immediately apparent; SyntaxHighlighter contains a "hidden" feature that allows double clicking the text to select all of it (of course the average user would never know this functionality existed). Finally, when it does select all of the text it copies it from its native, colorized form into a simple, monochrome textarea control that is created dynamically using some javascript magic. The performance can be a little slow, it may cause the view to randomly jump or scroll in Internet Explorer, and the transition just looks a little tacky.

Below is a script you can embed in the header section of your website or blog that will correct some of these problems. It changes the the default behavior of the "select all" operation and it also adds a button to the top-right corner that is clearly labelled "Select All" (you can still double click the text too).

If you use this script for Blogger, be sure to replace any ampersands with & and any less than signs with < and any greater than signs with >. If you don't do this you will get a cryptic error when you try to upload the html. Blogger requires these characters to be escaped since it uses XML.

<!-- jQuery -->
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js' type='text/javascript'></script>

<!-- Syntax Highlighter Additions START -->
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' rel='stylesheet' type='text/css'/>
<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeEclipse.css' rel='stylesheet' type='text/css'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'></script>

<style>
.syntaxwrapper
{
 position: relative;
}

.syntaxwrapper .toolbar
{
 position: absolute;
 z-index: 10;
 right: -7px;
 top: -17px;
 width: 65px;
 height: 20px;
 line-height: 20px;
 text-align: center; 
 color: #777;
 font-family: arial;
 font-size: 12px;
 cursor: pointer;
}
</style>

<script language='javascript' type='text/javascript'>

 function selectAll(element)
 {
     if (window.getSelection && document.createRange)
     {
         var selection = window.getSelection();
         var range = document.createRange();
         range.selectNodeContents(element);
         selection.removeAllRanges();
         selection.addRange(range);
     }
     else if (document.body.createTextRange)
     {
         var range = document.body.createTextRange();
         range.moveToElementText(element);
         range.select();
     }
 }

 function customizeSyntaxHighlighter()
 {
     var syntax = $('.syntaxhighlighter');
     if (syntax.length == 0)
     {
         setTimeout(function() { customizeSyntaxHighlighter(); }, 100);
         return;
     }

     syntax.each(function(index)
     {
         var selector = $(this);
         var wrapper = $(document.createElement('div')).addClass('syntaxwrapper');
         var toolbar = $(document.createElement('div')).addClass('toolbar').html('Select All');
         var code = selector.find('.code').first();

         selector.wrap(wrapper).before(toolbar);

         toolbar.get(0).onclick = code.get(0).ondblclick =
         function()
         {
             selectAll(code.get(0));
             syntax.scrollLeft(0);
             return false;
         };

         code.find('.line').each(
         function(index)
         {
             //this hack seems to fix all versions of IE
             this.appendChild(document.createTextNode('\r'));
         });
     });
 }

 SyntaxHighlighter.config.bloggerMode = true;
 SyntaxHighlighter.defaults.toolbar = false;
 SyntaxHighlighter.defaults['quick-code'] = false;
 SyntaxHighlighter.all();
 customizeSyntaxHighlighter();
 
</script>
<!-- Syntax Highlighter Additions END -->

Here is the SyntaxHighlighter website:

http://alexgorbatchev.com/SyntaxHighlighter/

Tuesday, March 27, 2012

SNMP Introduction: Communicating with your Router

"Simple Network Management Protocol (SNMP) is an Internet-standard protocol for managing devices on IP networks. Devices that typically support SNMP include routers, switches, servers, workstations, printers, modem racks, and more. It is used mostly in network management systems to monitor network-attached devices for conditions that warrant administrative attention." - Wikipedia

Below is a Java code snippet which uses the 3rd party library snmp4j to communicate with a router. In this example we obtain the name of the router and the current data usage. Before executing the code confirm that your router supports SNMP and that it is enabled. Also verify that the SNMP read and write community is set to "public". These are configurable values that can be updated from within your router's administrative application.

Note that the OID variables contain long and cryptic numeric values. These values can be obtained using a free SNMP browser such as iReasoning's MIB Browser. When using an SNMP browser you must supply your router's IP address and SNMP port (usually 161) to connect to your router. Once connected you can browse through the different properties as a tree structure and obtain any of the numeric values for the OIDs.

import java.io.*;
import org.snmp4j.*;
import org.snmp4j.mp.*;
import org.snmp4j.smi.*;
import org.snmp4j.transport.*;

public class SnmpClient
{
 public static final OID sysNameOID = new OID(".1.3.6.1.2.1.1.5.0");
 public static final OID ifNumberOID = new OID(".1.3.6.1.2.1.2.1.0");
 public static final OID ifOutOctetsOID = new OID(".1.3.6.1.2.1.2.2.1.16.1");
 public static final OID ifInOctetsOID = new OID(".1.3.6.1.2.1.2.2.1.10.1");
 
 private String address;
 private Snmp snmp;
 
 public static void main(String[] arg)
 {
  try
  {
   //Create a client with the router's IP Address and SNMP port (161 is default)
   SnmpClient client = new SnmpClient("udp:192.168.0.1/161");
   
   //Get the router's name
   String name = client.getAsString(sysNameOID);
   
   //Display the router's name
   System.out.println("ROUTER'S NAME: " + name);
   
   //Update the router's name (sysName is one of the few properties that are writable)
   client.setAsString(sysNameOID, "TEST");
   
   //I had to read this property before reading the other data usage properties
   client.getAsInt(ifNumberOID);
   
   //Get total number of bytes sent since last router reboot
   int bytesSent = client.getAsInt(ifOutOctetsOID);
   
   //Get total number of bytes received since last router reboot
   int bytesReceived = client.getAsInt(ifInOctetsOID);
   
   //Calculate total number of bytes sent and received
   int bytesTotal = bytesSent + bytesReceived;
   
   //Display combined total data usage in megabytes
   System.out.println("DATA USAGE: " + (bytesTotal / 1024 / 1024) + " MB");
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
 }
 
 public SnmpClient(String address) throws IOException
 {
  this.address = address;
  start();
 }

 private void start() throws IOException
 {
  DefaultUdpTransportMapping transport = new DefaultUdpTransportMapping();
  snmp = new Snmp(transport);
  transport.listen();
 }
 
 public void stop() throws IOException
 {
  snmp.close();
 }
 
 private Target createTarget()
 {
  Address targetAddress = GenericAddress.parse(address);
  CommunityTarget target = new CommunityTarget();
  target.setCommunity(new OctetString("public"));
  target.setAddress(targetAddress);
  target.setRetries(3);
  target.setTimeout(1500);
  target.setVersion(SnmpConstants.version1);
  return target;
 }
 
 private PDU createGetPDU(OID oid)
 {
  PDU pdu = new PDU();
  pdu.setType(PDU.GET);
  pdu.add(new VariableBinding(oid));   
  return pdu;
 }
 
 public Variable get(OID oid) throws IOException
 {
  PDU response = snmp.send(createGetPDU(oid), createTarget()).getResponse();
  if (response != null && response.size() > 0)
  {
   return response.get(0).getVariable();
  }
  throw new IOException("Unable to obtain response from server");
 }
 
 public String getAsString(OID oid) throws IOException
 {
  return get(oid).toString();
 }
 
 public int getAsInt(OID oid) throws IOException
 {
  return get(oid).toInt();
 }
 
 private PDU createSetPDU(OID oid, Variable value)
 {
  PDU pdu = new PDU();
  pdu.setType(PDU.SET);
  pdu.add(new VariableBinding(oid, value));
  return pdu;
 } 
 
 public void set(OID oid, Variable value) throws IOException
 {
  snmp.send(createSetPDU(oid, value), createTarget());
 }
 
 public void setAsString(OID oid, String value) throws IOException
 {
  set(oid, new OctetString(value));
 }
 
 public void setAsInt(OID oid, int value) throws IOException
 {
  set(oid, new Integer32(value));
 }
}

Monday, March 19, 2012

Search SQL Server Column Names

Below is a short T-SQL code snippet that demonstrates how to search for some text in all the column names in all the tables in a SQL Server database.

declare @SEARCHTEXT varchar(256)
set @SEARCHTEXT = 'TEXT YOU WANT TO SEARCH FOR'
select t.table_name,
c.column_name,
c.data_type,
c.character_maximum_length
from information_schema.tables t
inner join information_schema.columns c
on c.table_name = t.table_name
where c.column_name like '%' + @SEARCHTEXT + '%'
order by t.table_name, c.column_name

Search SQL Server Stored Procedures and Functions

Below is a short T-SQL code snippet that demonstrates how to search for some text in all the stored procedures and functions in a SQL Server database.

DECLARE @SEARCHTEXT VARCHAR(256)
SET @SEARCHTEXT = 'TEXT YOU WANT TO SEARCH FOR'
SELECT ROUTINE_NAME
FROM INFORMATION_SCHEMA.ROUTINES
WHERE OBJECT_DEFINITION(OBJECT_ID(ROUTINE_NAME)) LIKE '%' + @SEARCHTEXT + '%'
ORDER BY ROUTINE_NAME

Wednesday, February 22, 2012

Network Reachability Test for .NET

The code below is used to test if a specific server and port is reachable. This code uses a socket to connect to the server and doesn't wait for a response so it is quicker than using a full WebClient request, etc. This code will return true if the server was reachable and false if it was not (within the provided timeout).

public class NetworkReachability
{
 public static bool IsReachable(string url, int timeout)
 {
  return IsReachable(new Uri(url), timeout);
 }

 public static bool IsReachable(Uri uri, int timeout)
 {
  return IsReachable(uri.Host, uri.Port, timeout);
 }

 public static bool IsReachable(string host, int port, int timeout)
 {
  if (AppConfig.IsDebugCompile)
  {
   Thread.Sleep(250);
   return true;
  }

  try
  {
   using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
   {
    var handler = new ManualResetEvent(false);
    var e = new SocketAsyncEventArgs();
    e.RemoteEndPoint = new DnsEndPoint(host, port);
    e.Completed += delegate { handler.Set(); };
    if (!socket.ConnectAsync(e))
     handler.Set();

    return handler.WaitOne(timeout) &&
     e.ConnectByNameError == null &&
     socket.Connected;
   }
  }
  catch
  {
   return false;
  }
 }
}

Tuesday, February 21, 2012

Mono for Android Pros & Cons

Updated 5/4/2013

Recently, I had the opportunity to develop a project using Mono for Android. For those of you who are unfamiliar with Mono for Android, it enables you to develop Android apps using a .NET language like C# instead of Java. Overall, I've had a good experience using Mono for Android and I'm happy with the results. I've created a handful of apps using Mono for Android and they've all been successful.

The Mono for Android API closely mirrors the standard Android API. For example, nearly all of the same classes, methods, and fields are present in both APIs. One of the main differences is that Mono for Android utilizes properties and delegates which are non-existent in Java. I liked how closely the two APIs were related since this made it easy to switch back and forth between them and to use the wealth of documentation that already exists for the standard Android API.

Below is a list of pros and cons regarding Mono for Android:

PROS

  1. Share Code Between Platforms - If used in conjunction with the Mono Touch API and the Windows Phone API you can potentially reuse your business logic code for both the iPhone and Windows Phone platforms. This is possible since everything can be coded using a single, common programming language instead of 3 different programming languages. Please note that this only applies to business logic code though; presentation specific code will be different for each platform because of differences in the APIs. In my mind the ability to reuse code is the single greatest benefit in using Mono for Android.

  2. Use Your Existing .NET Skills - If you or the majority of your team members are knowledgeable with a .NET language but not with Java then using Mono for Android may be appropriate. However, I think this advantage is somewhat diminished by the fact that C# and Java are so similar. There is probably a greater benefit in using Mono Touch since this eliminates the need to learn Objective C which poses much more of a learning curve than Java.

  3. The C# Language - In my own opinion, the C# language has several advantages over Java (comparing only the core language and not other aspects such as frameworks, community, etc). LINQ, lamdas, and delegates are some of the features I love most about C#. These features aren't irreplaceable but they save time and effort. Perhaps Java's position will improve when version 8 is finally released.

CONS

  1. 3rd Party Android Libraries - In general it's more difficult (but not impossible) to use 3rd parties libraries with Mono for Android. Since most Android libraries are written in Java they must either be rewritten in C# or invoked with JNI. Mono for Android version 4.2+ includes a Binding project that can automatically wrap Java code in a JNI wrapper. The Binding tooling automates much of an otherwise difficult process. However, there is still a learning curve and manual adjustments are often necessary.

  2. Bugs - There is a significant amount of bugs in Mono for Android. Well, perhaps significant is too strong a word, but it is definitely more stable to use Java vs. Mono for Android. Two of the most serious bugs I encountered are: DateTime.Now returns time in the wrong time-zone (UTC), and using a SSL connection with WebClient causes an exception. (I believe these bugs are fixed now). Here is a full list of outstanding bugs.

  3. Debugging - Using the Mono for Android debugger can be a frustrating experience. It sometimes causes your app to crash or skips breakpoints for unknown reasons. Usually I resort to logging and avoid the debugger altogether. Xamarin has tried to fix the debugger but the problem has persisted for over a year now.

  4. Other Limitations - A list of other limitations is available here.

Personally, I'd lean more towards using Java and the standard Android API rather than using C# and the Mono for Android API. I say this because, first, I have a strong background in Java, and, second, using Java will likely result in less complications and limitations. However the ability to share the same code between Android, iPhone, and Windows Phone is very tempting and may be a deciding factor in some cases. At my current job I am part of a team of Microsoft developers, so using Mono for Android is typically the preferred method for creating apps within that environment.

Below is the Mono for Android website:

http://xamarin.com/monoforandroid

Tuesday, December 13, 2011

CSS3 Decoration for Internet Explorer 6 - 9

I recently discovered a javascript library named CSS3 PIE. PIE makes Internet Explorer 6-9 capable of rendering several of the most useful CSS3 decoration features. The list currently includes border-radius, box-shadow, border-image, multiple background images, and linear-gradient as background image. The property that interests me most though is border-radius, since I hate having to create new images every time I want rounded corners with a different radius.

To get started with PIE download the files from their website (css3pie.com). The download includes multiple files but in most cases all you really need is the PIE.htc file. Copy the PIE.htc file into your web project and then reference it using the CSS behavior property. The behavior property is proprietary to Internet Explorer so all other browsers will ignore it. PIE uses the standard CSS3 border-radius property so you will need to add that too, as well as the -moz and -webkit versions for Firefox, Chrome, and Safari. Below is an example:

<html>
<head>

<style>
.rounded
{
 behavior: url(pie.htc);
 border-radius: 10px;
 -moz-border-radius: 10px;
 -webkit-border-radius: 10px;
 background-color: red;
 color: white;
}
</style>

</head>

<body>

<div class="rounded">
 TESTING
</div>

</body>
</html>

After adding PIE you should have CSS3 decoration support for all the major browsers with the exception of Opera although personally I never really consider Opera much anyways.

Note: I've noticed that PIE's rendering of rounded corners is quite slow in Internet Explorer. Because of this, I've decided NOT to use PIE on my blog, so anyone who is unfortunate enough to use Internet Explorer version 8 or lower will see squared corners instead of rounded corners. Upgrade your browser or switch to a better one.