Thursday, October 24, 2013

Installing Solr 4.5 as a Windows service

I've recently had the good fortune to use the Solr search platform for a project. One thing, though, that is not straight forward is how to install Solr as a Windows service. There are several articles on the internet, but I couldn't find any that worked with Solr 4.5. So I gathered information from a number of out-dated articles and came up with the following approach, which has worked fine for me:

  1. Download Solr 4.5 (http://lucene.apache.org/solr/)
  2. Download the Non-Sucking Service Manager NSSM (http://nssm.cc/)
  3. Create a folder to be used for the Solr service (e.g. D:\solr). From now on this will be called <solrdir>
  4. From the example folder of the Solr package copy the following files and folders to <solrdir>:
    1. etc
    2. lib
    3. logs
    4. solr
    5. webapps
    6. start.jar
  5. In a command prompt go to <solrdir> and run the command java -jar start.jar to check that the Solr installation works. If it works then just stop it again.
  6. From the NSSM package copy nssm.exe to <solrdir>
  7. In a command prompt go to <solrdir> and run the command below. Note: it may be necessary to replace back-slashes in <solrdir> with forward-slashes.
    nssm.exe install Solr C:\Windows\System32\java.exe "-Dsolr.solr.home=<solrdir>/solr -Djetty.home=<solrdir>/ -Djetty.logs=<solrdir>/logs/ -cp <solrdir>/lib/*.jar;<solrdir>/start.jar -jar <solrdir>/start.jar"
To remove the service run the following command:
nssm.exe remove Solr

For convenience I've created the following batch script, which performs the nssm.exe install part. The script should be placed in <solrdir>.
@echo off
set currentdir=%~dp0
set parameters=-Dsolr.solr.home=""%currentdir%solr"" -Djetty.home=""%currentdir%"" -Djetty.logs=""%currentdir%logs/"" -cp ""%currentdir%lib/*.jar;%currentdir%start.jar"" -jar ""%currentdir%start.jar""

REM replace back-slashes with forward-slashes
set javaWeirdnessParameters=%parameters:\=/%

nssm.exe install Solr C:\Windows\System32\java.exe "%javaWeirdnessParameters%"

Wednesday, March 23, 2011

NServiceBus and SQLite in .NET 4.0

I've just used the better part of a day trying to figure out how to get System.Data.SQLite.dll to work with NServiceBus in a .NET 4.0 project in VS2010.


The problem is if you just set it up like normal, you'll get the following error:
"Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information."

This is because there's no .NET 4.0 version of the dll released yet (2011-03-23). There's only a .NET 2.0 version and it contains unmanaged code. So what we must do it to explicitly tell the project that it is ok to use the dll by adding some configuration. This solution is documented in many articles on the Internet, but I could not get it to work with NServiceBus - or rather the NServiceBus.Host.exe to be precise which actually makes all the difference.

As mentioned in various articles on the Internet I put the configuration in app.config of the project and then exactly nothing happened. After much frustration I stumbled over this article:
http://tech.dir.groups.yahoo.com/group/nservicebus/message/8951

The trick is to put the configuration in the NServiceBus.Host.exe.config file like this:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- This is needed in order to get System.Data.SQLite to work in .NET 4.0, at least
until a .NET 4.0 compatible version of System.Data.SQLite is released. -->
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
</configuration>

NOTE: you must also ensure that the "Copy to Output Directory" setting for the config file is set to one of the copy options.

This did the trick.

Friday, March 18, 2011

Fuzzy text in VS2010

So I finally moved to Visual Studio 2010. This is a very nice IDE, but one thing that annoys me is that the text (code) seems fuzzy compared to VS2008. My eyes don't like it.


I googled it and there were several suggestions of using the free Visual Studio theme editor "Visual Studio Color Theme Editor":

However, this only allows for changing the theme of everything around the code area - not the code area itself.

Then I found some articles about the fonts used in VS2010 and it turned out that it uses a font called Consolas for the code whereas VS2008 uses Courier New. So I simply changed Consolas to Courier New in VS2010 and my eyes were instantly happy :-)

Tuesday, October 26, 2010

Transforming XML containing namespaces

Whenever I get the task of transforming some XML to some other XML using XSL and namespaces are involved it is always a struggle for some reason. And I've experienced that I'm not the only one having problems with that.

Most examples of XML transformation that I stumble over on the web deals with transforming XML to HTML and almost never use namespaces. Therefore I've decided to write a little article where I describe how to transform from XML to XML including the usage of namespaces.

The task
I want to transform an XML document containing information about CDs from one format into another using XSL. The source XML will use one namespace and the output XML another.

Source XML document
This is the source XML document that I want to transform. Notice that it uses the "http://schemas.maze-dev.blogspot.com/2010/catalog" namespace.

<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog xmlns="http://schemas.maze-dev.blogspot.com/2010/catalog">
<name>Absolute Whatever Vol. 1</name>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<country>USA</country>
<company>RCA</company>
<price>9.90</price>
<year>1982</year>
</cd>
</catalog>
XSL transformation document
This is the transformation document. The xmlns:xsl="http://www.w3.org/1999/XSL/Transform" defines the standard 'xsl' transform namespace. However, the interesting part is xmlns="http://schemas.maze-dev.blogspot.com/2010/archive" which defines the output namespace (i.e. namespace in output XML) and xmlns:cat="http://schemas.maze-dev.blogspot.com/2010/catalog" which defines the namespace 'cat' used to reference elements in the input XML. The attribute exclude-result-prefixes="cat" states that the 'cat' namespace should not be included in the output XML.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://schemas.maze-dev.blogspot.com/2010/archive"
xmlns:cat="http://schemas.maze-dev.blogspot.com/2010/catalog" exclude-result-prefixes="cat">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/">
<archive>
<xsl:apply-templates select="cat:catalog"/>
</archive>
</xsl:template>
<xsl:template match="cat:catalog">
<name><xsl:value-of select="cat:name"/></name>
<xsl:apply-templates select="cat:cd"/>
</xsl:template>
<xsl:template match="cat:cd">
<album>
<title>Title: <xsl:value-of select="cat:title"/></title>
<artist>Artist: <xsl:value-of select="cat:artist"/></artist>
</album>
</xsl:template>
</xsl:stylesheet>
Output XML document
This is the output XML document. Notice that it uses the "http://schemas.maze-dev.blogspot.com/2010/archive" namespace that was specified in the XSL document.

<?xml version="1.0" encoding="UTF-8"?>
<archive xmlns="http://schemas.maze-dev.blogspot.com/2010/archive">
<name>Absolute Whatever Vol. 1</name>
<album>
<title>Title: Empire Burlesque</title>
<artist>Artist: Bob Dylan</artist>
</album>
<album>
<title>Title: Hide your heart</title>
<artist>Artist: Bonnie Tyler</artist>
</album>
<album>
<title>Title: Greatest Hits</title>
<artist>Artist: Dolly Parton</artist>
</album>
</archive>
So basically, that's it. Hope somebody finds it useful.

Monday, October 25, 2010

"Specified Cast is Not Valid" - LinqToSql error on submit

While working on a project using LinqToSql I got the error "Specified Cast is Not Valid" when calling SubmitChanges.

After searching a bit on the web I found out the error is due to a problem in .NET Framework 3.5. Luckily there is a hotfix that addresses this problem. Here are some articles about the problem:




The first article is very specific:
"When database contains two tables, both with automatic integer primary keys, and a relationship between a unique char field in one and a non-unique char field in the other, inserting new rows into the second table fails on submit with InvalidCastException."

This is a bit too specific as to the usage of int and char, because the error occurs in other situations as well which can be seen in the second article. Here the error occurs using GUID and int. And to top that up, in my own situation the error occurred using int and long.

The third article has a link from which to download a hotfix to solve the problem.

If you want to obtain the hotfix in a more official way you can use this link:

Monday, June 14, 2010

DataGridView with Typed DataSet

I was recently working on an assignment creating a Windows Forms application with a DataGridView being fed data from some object collection, and NOT (as is the case with virtually all samples that can be found on the Internet) using the SqlDataAdapter.

Even though this is a pretty simple task once you've found out how to do it, I did find it a bit tricky to get to work. So here's how it can be done.

  1. Start Visual Studio and create a new Windows Forms application.
  2. Add a DataSet to the project by right-clicking on the project and selecting Add -> New Item and selecting the DataSet template naming it ContactDataSet. This should bring up the DataSet designer.
  3. Add a new DataTable to the designer area using the Toolbox. Name it Contact and add some columns to table (see details such as column data types in the sample code)
  4. Add a new class to the project and name it Address:

    public class Address
    {
    public long Id { get; set; }
    public string Street { get; set; }
    public int HouseNumber { get; set; }
    public string PostalCode { get; set; }
    public string PostalDistrict { get; set; }
    }

  5. Add another new class to the project and name it Contact:

    public class Contact
    {
    public long Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int? Age { get; set; }
    public Address Address { get; set; }
    }


  6. In Form1.cs add this private member:

    private ContactDataSet _contactDataSet = new ContactDataSet();


  7. Also in Form1.cs add this method for generating a collection of test data:

    private IList<Contact> GenerateData()
    {
    IList<Contact> contactList = new List<Contact>();
    contactList.Add(new Contact
    {
    Id = 1,
    FirstName = "Peter",
    LastName = "Jackson",
    Age = 53,
    Address = new Address
    {
    Street = "Long Road",
    HouseNumber = 173,
    PostalCode = "12345",
    PostalDistrict = "Fake Town"
    }
    });
    contactList.Add(new Contact
    {
    Id = 2,
    FirstName = "Pavlov",
    LastName = "Ivanowich",
    Address = new Address
    {
    Street = "Park Avenue",
    HouseNumber = 1011,
    PostalCode = "98765",
    PostalDistrict = "Imaginaryville"
    }
    });
    contactList.Add(new Contact
    {
    Id = 3,
    FirstName = "Onslow",
    LastName = "Bucket",
    });
    return contactList;
    }

  8. Furthermore, add this method for filling the DataSet with data from a collection:

    private void FillDataSet(IList<Contact> contactList)
    {
    foreach (Contact contact in contactList)
    {
    ContactDataSet.ContactRow row = _contactDataSet.Contact.NewContactRow();
    row.FirstName = contact.FirstName;
    row.LastName = contact.LastName;
    if (contact.Age.HasValue)
    {
    row.Age = contact.Age.Value;
    }
    if (contact.Address != null)
    {
    row.Street = contact.Address.Street;
    row.HouseNumber = contact.Address.HouseNumber;
    row.PostalCode = contact.Address.PostalCode;
    row.PostalDistrict = contact.Address.PostalDistrict;
    }
    _contactDataSet.Contact.AddContactRow(row);
    }

    // OK, so we have filled in the data we want to start up with.
    // We must now call AcceptChanges so that this data isn't change-tracked.
    _contactDataSet.AcceptChanges();
    }

  9. Go to the Form Designer and add a BindingSource and name it contactBindingSource. Set the ContactDataSet as DataSource and Contact as DataMember.
  10. In the Form Designer also add a DataGridView and set contactBindingSource as DataSource.
  11. In the Form1.cs add this code in the Load event handler:

    IList<Contact> contactList = GenerateData();
    FillDataSet(contactList);

    contactBindingSource.DataSource = _contactDataSet;

  12. Run the application. The DataGridView should now display the test data.