Showing posts with label got ya. Show all posts
Showing posts with label got ya. Show all posts

Saturday, July 12, 2014

Jetty

Lately I've had three different issues with Jetty. Two of them were those kind of issues that are not directly related to the problem. The third proved to be a bit more tricky.

The first one was the typical example of "correlation does not equal causation". I started getting a weird error trying to run a Scala based web application in Jetty. And I kept received the following error "Could not create symlink /tmp". I thought it could be related to some kind of permissions issue. In the end the problem was more basic: I'd exhausted all the free space and nothing could be written to disk (and jetty couldn't create a symlink).

The second one was one of those Doh! moments. My web application didn't respond to any request. But it didn't show any error in the logs. I'm quite ashamed of putting this in writing but the issue was that the web application was paused in a breakpoint while debugging.

The third one was a bit strange. The web application obviously has static files. Some of the Javascript files were served correctly, other Javascript files weren't. The files not served were always the same. I didn't detect any specific pattern. But the fact was that Chrome would receive net::ERR_EMPTY_RESPONSE, and the Jetty was logging an exception NoSuchMethodError in javax.servlet.http.HttpServletRequest.isAsyncStarted  

The typical answer relates the problem to having several versions of the servlet api. But I somehow didn't pay too much attention because [sarcasm on] everybody knows servlet-api.2.5 and javax.servlet-api-3.1.0 are not different versions of the same API [sarcasm off] (indeed they ARE different versions of the servlet API)

Thursday, April 24, 2014

PDOException

Small and uninteresting post. But I've been dealing with an issue for hours and maybe somebody can benefit from this in the future.

While trying to index using Sphinx, I was getting the following error

[PDOException]                                  
  SQLSTATE[HY000] [2002] No such file or directory


The issue was not related to Sphinx, but to a related connection between PHP/Symphony2 and MySQL. Unfortunately the PHP web application and MySQL were working together and that made the issue more difficult to track.

Most of the replies around the Internet involve file sockets and the like. 

The real solution is not to connect to "localhost", but to 127.0.0.1

Yeah. I know localhost and 127.0.0.1 should be the same. Apparently it is a Mac weirdness. Beats me!!!

Wednesday, March 12, 2014

Tipping point

I'm happy. 

I was developing a feature that allows to export from my showcase project Whendoigo to TripIt

I was using a gem from TripIt. A gem is kind of a library for Ruby (sort of jar for Java). The gem simply encapsulated the API from TripIt. But it was failing and apparently everything on my project was correct. So this time it didn't took me too long to start debugging the actual gem's code.

The exception had traces with ActiveRecord references. ActiveRecord is an ORM, (think Hibernate or iBatis for Java). It's part of Rails. And the gem shouldn't have any dealing with ActiveRecord

The code that was failing was a bit difficult to understand. It uses one of the more powerful features in Ruby called meta-programming, but not with the more typical ways. Trying to put into words, it was creating a class with a dynamic name on the fly, and then instantiating an object of that class. 

What happens when the name it is trying to use is already being used in a different part of the application? To avoid that issue, the gem was creating the names with a namespace. 

And that should have been all.

But it wasn't. Lots of debugging and some restructuring later I found the problem. 

The names were being cached and when asking for the names it was asking for the names with the namespace... or without it. 

Two lines of code. A pull request. It took me a while. 

Not many people would appreciate the reason. 

But I'm really happy.


Thursday, February 27, 2014

I was sure it was my fault...

... but it wasn't.

In the last week or so, I've been hitting against a wall because of two different bugs. I was sure that both bugs were my fault. It took me ages (of a scarce spare time) trying different things, searching for similar problems, until in both cases I finally came to an issue in different tools. As I haven't found too much information, I've decided to put it in writing.


  • First, trying to use Boostrap (a framework for presentation: sort of a CSS template), and using the grid system (a way to organize things in columns, rows and cells), I wasn't able to get things in the same row and they appeared stacked. The issue turned out to be I was using an old Bootstrap version (3.0.0rc1). Upgrading to a newer version (3.1.1) solved the problem
  • Second, again using Bootstrap and its icons (glyphs as they call it), I couldn't help but notice that the icons didn't load properly until the mouse was over the icon (hover). The usual answer on the internet for not loading icons in Bootstrap is "check the class" (it doesn't help that class name for glyphs have changed from Bootstrap 2 to Bootstrap 3). The second most typical answer is related to Firefox (I was using Chrome). Finally I managed to remember that the icons in Bootstrap 3 are fonts. Which led me to know there is a nasty bug hitting Chrome stable with the following title "External Font not rendering until forced repaint". Ouch.


By the way. "It must be my fault" is much better than "it's not my fault". But biases are not good, whether one way or another.

Tuesday, November 26, 2013

Firmware upgrade for Xperia U in Linux (II)

[Long post]

Edited (08th-Jun-2014): Updated the firmware download site.

In my previous post I explained that one of the things that made the installing process more difficult was the lack of a general picture of the process. There are lots of pages explaining recipes to install, but I find it difficult to follow recipes if I don't manage to understand the underlying logic.

The following is my visualization of the whole process (this is less than a week of knowledge, so I cannot say I'm an expert: take it with a grain of salt). You can get a more accurate picture of the Android architecture in other pages. And you can get the process in detail from XDA.

First, components

As far as I understand there are three components in an Android system


  1. The bootloader. This is a piece of software that usually loads the system, enables Android recovery image and image upgrades. Sometimes it is called the kernel, so I understand it is the underlying component of the Android
  2. The second one is called the system. It would include shared libraries and components. When people says Android is Java, I understand that it is probably related to this set.
  3. And the third one is called Gapps. Gapps it is not a component in its own. It's a package with Google applications. For licence reasons, you cannot distribute them attached to other components, so you almost always will find a package with Gapps.


OK. Now to the process. Again. You'll probably void the warranty and you could brick your phone. If you're not comfortable installing your own operating system at your computer, DON'T EVEN BOTHER reading further. If you are reading this, you have Linux at home and that's means that you are more than average Joe, but do it at tour own risk.

Second, the modes. Fastboot and the like


"Fastboot??? What the h***ll is that. Gonzalo, you told me this was going to be easy".

I nearly bricked a BQ Maxwell tablet upgrading to the official firmware, so, please, don't think this is easy.

Fastboot is a mode in the phone that allows to unlock and flash the bootloader. In the Xperia U you get that mode turning the phone off, then pressing the Volume up button and while pressing, connecting the USB cable to the computer. But you should already know if you had read the Sony document as I told you. The light in the phone should be blue.

There is also another mode called Flash mode. "Fastboot mode and Flash mode are different? Is there no other F-word?". To enter Flash mode, shutdown the mobile, disconnect the cable from the computer, press the Volume down, connect to the computer and release the volume down. The light in the phone should be green.

 Recovery mode. It is a mode that allows to do some powerful things from the phone. To enter that mode, power off the phone, then power on and after you see the SONY logo, press repeatedly volume down button (-) until you see recovery. While in the previous two modes the screen is powered off, in this mode, you can navigate the menus. To navigate in the recovery mode, you use volume up and volume down, and select with the Power button.

You will sometimes read about Normal mode. Normal mode is the phone on as usual, sometimes with the the debug options activated.

Updating. One twist.

After reading about components and modes, the first thing to do it would seem to update the bootloader. But...

...you cannot update the kernel directly because to avoid people messing around, kernels tend to be locked down by the manufacturer. Besides, sometimes you buy phones locked by the telco company (so that you get it cheaper, but you cannot change the company).

So before you update the kernel, you need to unlock (or root, as it sometimes called) the bootloader.

Unlocking the bootloader

I must confess I'm pleasantly surprised with Sony about the unlocking process. It has a very informative page with a step-to-step guide. Unfortunately, it doesn't work with Linux, but you should read it in detail because it is almost there. You'll need to install Android SDK. You'll need to follow most of the steps.. Also, you will need a code, you're going to get by email from that page. So go read, click next and fill the details with your email and IMEI, because you do really need the code.

When going for Linux, you're going to follow the same steps. BUT. Instead of the steps related to the USB drivers (i.e step 9), you need UDEV rules. UDEV is the way Linux uses to recognize new components, in particular USB devices. You will need the USB ID for your phone version, both in usual mode, in Fastboot mode and in Flash mode.



Back to the UDEV rules, these are the ones I got for Xperia U ST25i.



But you should get yours using the tool lsusb.

After you have the correct rules (and you have restarted the UDEV with "sudo service udev restart") connected in fastboot mode, you will be able to follow steps in the Sony Page and unlock the bootloader. And then to the next step

Flashing the kernel

This part should be easy but it gets a bit tricky. You are going to use a tool called Flashtool. But the tricky part is that Flashtool needs libusb-dev. From what I've been able to understand, that library suffered some fork, so you'll end up asking how the h*ll should you install that library. And if you've got a 64 bits linux version, things are not going to be easier. You may end up screwing the whole system because ia32-libs (don't follow that route).

Good thing about the Internet is that you are not alone. So in this page, you'll get to ease the problematic steps. Basically, compile libusb-dev from source and modify Flashtool.

And then flahsing the kernel should be easy. Flashtool allows lots of things, but I only flashed the kernel  with the firmware Flashtool came with. Once connected, I think you can flash either in Fast boot and in flash mode.

Flashing the system and Gapps

The last part is very easy. First download the system and Gapps. If you're going to pick the latest versions, do it from Maclaw. The download is a bit unintuitive (click Sony Xperia U, click CianogenMod in the right, and then download both files). Edited: If you want Android 4.4.3, see this post. The better place is from XDA. Once downloaded, copy them to the phone in normal mode.

And reboot in Recovery mode. In Recovery mode you should do a backup. Just in case.

Then follow the process "Install zip->Choose zip from /storage/sdcard0 and select the folder where you copied the file" to load first the system file. And then do the same process with the Gapps file.

And then wipe data/factory reset, followed by wipe cache partition and followed by "advanced->Wipe dalvik cache"

And then "reboot system now"

One last note


One of the things I struggled with was reading about a sdcard "Where is the SDCARD in an Xperia U?".

I understand it is a sort of internal storage. Anyway, there is no socket to put a SDCARD inside, so forget about it.




Sunday, November 24, 2013

Firmware upgrade for Xperia U in Linux (I)

About a year ago I received some wonderful presents from a group of friends :D. One of them was an Xperia U.

Sony Xperia U (ST25i) has a stock Android 2.3, which a year ago was already a bit old. Fortunately, Sony had already published Android 4.04 for the Xperia U, and the same day I received, I installed ICS (Android is famous for putting sweet names to its versions).

The phone worked quite well. But new Android versions came, and it was obvious Xperia U wouldn't get an updated.

So I decided to explore the Android unofficial update scene. And I ended up installing a custom firmware. And I don't regret it.

I have the new shiny Android KitKat 4.4. The phone works really great. Fluid. Visually more pleasant. No crapware (why do they force to install apps nobody uses?).

It still has some issues pending (at this moment, Bluetooth does not work yet), but for me it is no longer a matter of testing a Custom Firmware. It is much much better than the official Sony 4.0.4 version. Besides, the scene for the Xperia is very active and vital (yesterday I installed a 22nd November version that didn't have wifi working, and this morning I installed another version where wifi works)

The real process is very easy, but I had some issues getting to there. Because of two things:


  1. I didn't even know the most basic terminology about the process (not that I am an expert know, you know) and I couldn't manage to get a mental picture of it.
  2. Most of the pages are Microsoft Windows based. I have Linux at home. Linux Mint 64bits, to get things more complicated (as we'll see). 


So in my next post I'll try to explain the process in a sort of newbie post. HOWEVER. Word of caution. You'll probably void the warranty and you could brick your phone. So this is not something for the faint of heart.

UPDATED: Link to the detailed post.

Monday, September 24, 2012

OptionalDataException while reading JMS message

We've been dealing with another bug in JMS code. The symptom was blocked queues and the following exception:

javax.jms.JMSException: getObject
          [...] 
Caused by: java.io.OptionalDataException
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1325)
      at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
      at java.util.HashMap.readObject(HashMap.java:1066)

In trying to reproduce people in the team introduced a non-serializable object, but he got an exception at sending the message (Caused by: javax.jms.JMSException: setObject [...] java.io.NotSerializableException). In a similar way, trying to send an int, he got a NotSerializableException. And trying to send some class and then changing the serialVersionUID, he got an error at receiving, but a different error (java.io.InvalidClassException).

Everything in Internet pointed to a thread-unsafe error, so we looked at the code and found this


        mensaje = receptorMensaje.receive(timeOut*1000);
        [...]
        mensaje.acknowledge();


with several lines of code between the receive and the acknowledge, and without synchronization

Under very heavy load, it was easy to have a thread execute the receive, a different thread execute the receive and then the first thread acknowledge. But by then both threads have the same object and if the first thread changed its copy, the Exception would be triggered.

We extract a method this code and synchronized.

Saturday, September 22, 2012

Weird IE9 bug

This last two weeks I have dealt with a weird bug in IE9.

  • The problem appeared in a legacy web application. 
  • The issue reported by the user was a combo that didn't drop down. 
  • The problem appeared in different pages, but only for some combination of values.
  • The issue appeared after installing IE9 from IE7 in a Windows Vista. 
  • The user could reproduce the issue (for a certain combination of values).
  • We (both devs and ops) couldn't reproduce in a standard machine even tough at work final users deal with a very standard machine (machine setups are pretty standarized at work). 
  • When we finally were able to reproduce in a standard machine, it only failed the first time a user logged in (second time, or even IE close rendered the bug irreproducible).
  • We finally were able to pinpoint the problem to a very large option (as in 400 characters).
  • And now I've been able to reproduce it with a reduced version of the page even in my setup (Vista64+IE9). See the gist. Save to a file, launch it with IE9 and try to change the option.

We trimmed down to 200 chars (I guess even 150 chars wouldn't be viewable).

UPDATE (2012-09-30): Tested on IE10 in Windows8 and seems to work fine.

Wednesday, August 15, 2012

Savon.rb and sequences

As I wrote in my last post, I've recently learned it is important to be able to show what you've been doing. So I'm trying to increase the posts on a number of small problems I find in my daily job, and in my hobby projects. This one appeared in my daily job.

I was developing a small interface between two applications via a SOAP web service. Actually, we are using Redmine for development, and Remedy for the whole organization, and we didn't want to fill the information twice.

I was using Savon.rb for creating a client in Redmine that would invoke a SOAP web service in Remedy. The code was something like this:

          response=client.request "Resolver" do
            soap.header={:AuthenticationInfo=>{
                 :userName=>ws_user,
                 :password=>ws_pwd,
            }
            soap.body = {
                "Status"=>"Resolved",
                "Resolution"=> message ,
                "Status_Reason"=>"Client Action Required",
                "Incident_Number"=>code,
                "Status_Reason2"=>"Client Action Required",
                "AssigneeLoginID"=>user,
            }
          end


However, I was getting a SOAP error

ARERR [8962] Unexpected element encountered in the input XML document

The problem lies in Ruby hashes not preserving the order and Savon sending XML sequences in incorrect order. I just updated the call to something like this

          response=client.request "Resolver" do
            soap.header={:AuthenticationInfo=>{
                 :userName=>ws_user,
                 :password=>ws_pwd,
                 :order! => [:userName, :password]}             }
            soap.body = {
                "Status"=>"Resolved",
                "Resolution"=>message,
                "Status_Reason"=>"Client Action Required",
                "Incident_Number"=>code,
                "Status_Reason2"=>"Client Action Required",
                "AssigneeLoginID"=>user,
                :order! => ["Status", "Resolution", "Status_Reason","Incident_Number", "Status_Reason2", "AssigneeLoginID"]             }
          end


and it solved the issue. I expected Savon to take care of this, but anyway, I understand this is solved in Ruby 1.9x.

Sunday, July 1, 2012

Xerces, Xalan and Saxon, more recipes

In a previous post I proposed some tips for trying to manage and control the hell of XML and XPATH parsers. However, as one reader pointed, those tips sometimes are not enough. What Simon explained may not seem obvious, but sadly for us (I came across a different problem with the same underlying cause), it is.

In my previous post I explained how to make Java choose a class for a parser. But, unfortunately that's not enough when different libraries provide that class. And that happens to be the case with some classes that are both in Xalan an Saxon (Simon's case) and some Xerces classes (both in server and WEB-INF/lib in my case). 

Simon needs Xalan classes for the general case, but he also needs Saxon for some specific (maybe XPATH2) parsing. He is able to set the parser for the general case, and initialize the specific parser for the XPATH 2 case. But having Saxon jar in the classpath makes the classloader load Saxon classes before  Xalan classes. 

What can we do in that case? It's not easy to answer, and sometimes there's not even an answer. Some tips:

  1. Play with the classpath general mechanism loading order. For example JRE/lib/ext are usually loaded before WEB-INF/lib, so maybe Xalan would be placed in JRE/lib/ext and Saxon in WEB-INF/lib. This tip could have it's drawbacks in the booting of the server and in other applications sharing the same JVM, because JRE/lib/ext gets precedence from server specific mechanisms. Besides, operations people gets very nervous about placing anything in the JRE/lib/ext (not without reason).
  2. Use some mechanism specific of the server. In my case (Oracle WebLogic Server), there were several
    1. Use $DOMAIN/lib folder instead of JRE/lib/ext (ops people get more comfortable, and it reduces the applications affected
    2. Classloader-structure in weblogic-applicaiton.xml.

  3. Play with classloaders. This page from OWLS is not very server specific and should give some hints.
  4. In the worst case, I guess one could try to use its one specific packaging.
Hope this helps. I solved my case but for the general case it will depend on what are we allowed to do.

Tuesday, January 31, 2012

Xerces and Xalan: Recipes for the win

OK. I have explained why Xerces and Xalan are a source of problems, and I have explained why you shouldn't use System.setProperty to resolve them. In this post I wan't to give a few recipes to resolve Xerces and Xalan problems.

  1. Under no circumstances use System.setProperty anywhere in your application. I know I have already dedicated a post, but I wanted to stress it once more. Sometimes you'll need some specific parser (OC4J had a strange bug that needed a specific parser, and Weblogic seems to have special needs too). If you need a specific parser, do it at container level (2nd  point), at application level (3rd point) or at the specific code (4th point).
  2. If you have to define any property do it at the start of the container or the server. For example, in Weblogic 10.3.3 in Windows, we had to edit the file startWeblogic.cmd at the domain path, inserting the following line

    set JAVA_OPTIONS=-Djavax.xml.soap.MessageFactory=weblogic.xml.saaj.MessageFactoryImpl

    Sometimes you don't need to do it at the container/server level. In Weblogic you can use something called XML registry that enable a different parser for a particular Document Type.
  3. Sometimes you can define the parser at the application level. For example, in Weblogic you can define a specific weblogic-application.xml with the following text:
    <?xml version = '1.0' encoding = 'UTF-8'?>
    <weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/javaee_5.xsd
    http://xmlns.oracle.com/weblogic/weblogic-application
    http://xmlns.oracle.com/weblogic/weblogic-application/1.1/weblogic-application.xsd"
    xmlns="http://xmlns.oracle.com/weblogic/weblogic-application">
    <xml>
    <parser-factory>
    <saxparser-factory>org.apache.xerces.jaxp.SAXParserFactoryImpl</saxparser-factory>
    <document-builder-factory>org.apache.xerces.jaxp.DocumentBuilderFactoryImpl</document-builder-factory>
    <transformer-factory>net.sf.saxon.TransformerFactoryImpl</transformer-factory>
    </parser-factory>
    </xml>
    </weblogic-application>
  4. When somebody needs a specific property for a factory and is not possible to modify the container or the application, the solution is not to do a System.setProperty (see 1st bullet), but to instantiantiate the specific factory. For exmaple, instead of doing

    import net.sf.saxon.TransformerFactoryImpl;
    System.setProperty("javax.xml.transform.TransformerFactory",
          "net.sf.saxon.TransformerFactoryImpl")
    TransformerFactory tf= TransformerFactory.newInstance();


    you should do

    import net.sf.saxon.TransformerFactoryImpl;
    TransformerFactory tf=new TransformerFactoryImp();


  5. In the long run, it should be great to use mechanisms to prevent using System.setProperty like this link http://stackoverflow.com/a/4208150/54256
Update (2012-10-01): I see lots of visits to this page. Maybe you should visit 


too

Sunday, January 29, 2012

Things about Xerces, Xalan and Saxon I didn't know (III)

In my previous entry, I tried to explain why Xerces and Xalan are so complicated. In this entry I want to focus in System.setProperty. Because, as I said that is the usual solution you are going to find when you need a specific parser for whatever reason.

And that solution is wrong. Very wrong.

Because, if you choose to change the provider using System.setProperty, you are changing the provider for all the applications in the container/server. It doesn't matter that you change it, then instantiate, and then change to the original value. Because, when you're running in an application server, there are some chances of additional threads needing to instantiate and getting a different provider of the one they are trying to get. In fact, that same problem was reported to my group a couple of weeks ago. We tend to give support to applications that use our framework. And, as I say, we received a strange error. Sometimes one report was generated OK, and sometimes it failed with some exception (don't remember off hand). Same query, same data. Fortunately, the stacktrace came with some reference to xerces. Quick search and discovered the report failed when a different report changed the Property for DocumentBuilderFactory to an old sun implementation. What was even better: the old implementation seemed to be not particularly needed. Looked like a copy&paste.

I guess you'll be asking yourself what to do if you can't use System.setProperty. Too late for this blog post. Wait for the next.

EDIT: Stack trace

java.lang.ClassCastException: com.sun.org.apache.xerces.internal.util.XMLGrammarPoolImpl
            at org.apache.xerces.impl.xs.XMLSchemaLoader.reset(Unknown Source)
            at org.apache.xerces.impl.xs.XMLSchemaValidator.reset(Unknown Source)
            at org.apache.xerces.parsers.XML11Configuration.configurePipeline(Unknown Source)
            at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
            at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
            at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
            at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
            at org.apache.commons.digester.Digester.parse(Digester.java:1647)

Wednesday, January 18, 2012

Things about Xerces, Xalan and Saxon I didn't know (II)

I said in the previous post that Xalan and Xerces sometimes look like magic. So what are they and why are so complicated?.

  • Xerces is a Java library that helps parsing, validating and managing XML documents. You can parse XML documents in different ways. You can transform into a memory structure, building a big object, or you can even parse only the elements you need. There's a whole array of ways to manage XML documents and schemas, and Xerces helps with them. 
  • Xalan is a Java library that helps transforming (XSLT 1.0) and querying (XPATH 1.0) XML documents. 

If you've reached this point you'll be asking why the buzz about Xerces and Xalan. There are four things that make Xerces and Xalan special:

  • Java application servers usually manage their configuration with XML and they need some version of a parser.
  • Because of that, Xerces is probably one of more forked projects in the history. Every little server that worth its bucks have its own version of Xerces. Yes, you have one official version of Xerces. But your own server maybe has it own. And probably instead of org.apache.xerces.jaxp.DocumentBuilderFactoryImpl, they will have called something like com.sun.apache.xerces.jaxp.DocumentBuilderFactoryImpl.
  • Even if your server doesn't use its own version of Xerces, chances are high that it will be using classes of type javax.xml.parsers.DocumentBuilderFactory or  javax.xml.parsers.SAXParserFactory to parse xml documents. To get an instance of these classes you'll use a design pattern called factory that will give you a specific implementation of the class. And the specific implementation you'll get depends on... a system property.
  • If you need to parse an XML document, you'll have to use those classes too. And if for whatever reason you don't like the specific implementation your server (or even somebody in your own project) is using, then you're basically screwed. Because you'll use something that appears in 80% of the articles about the topic: System.setProperty.

Things about Xerces, Xalan and Saxon I didn't know (I)

These days I'm getting very proud of myself. I'm acting less as a project manager and more as a true Scrum Manager. I go to the scrum, listen, and a great deal of my work that day is trying to solve problems for the team. That's great because in order to remove the glitches of my team I have to learn a lot on very different topics.
One of those topics has been Xerces and Xalan.
Xerces and Xalan is one of the those things every developer has listened about authentic horror stories. When it goes OK, you don't worry. And when things go wrong, the usual tactic is to put different versions of those libraries in different places of the server, make some System.setProperty and hope the best.
That tactic usually work (somehow). But not always. And it looks like (dark) magic.

Friday, January 13, 2012

Notes to a newbie spring-mvc developer


  1. Activate log4j. Without it you're lost
  2. Look through the log to see if you're getting the RequestMapping through Spring.
  3. If you are not despite correct annotations, see this post. In particular if you've got Spring Security installed, for Goodness sake, don't forget these lines:
  4. <beans:bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <beans:bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    </beans:bean>
    </beans:bean>

  5. Just in case you decided to change the mappings, and you continue to see the old ones in the logs, clean and rebuild.

Monday, March 29, 2010

More Mantis problems

I have been fighting with PHP, Mantis and SOAP since my last post. Now I think I have discovered a bug when uploading via Mantis SOAP a binary file. It's great being able to debug a PHP application. I hope what I discovered is useful for more people. But I've discovered something quite shocking: I wasn't able to find any good answers to something that must be frequent. I was uploading a binary file sending nulls: 00 00 00 or when base64-encoded: AAAA (in hex 41 41 41 41). And I was getting after downloading "\0" (slash-zero) values (5C 30)

Tuesday, March 9, 2010

Mantis on SQL Server

[Technical post]
I've been busy last couple of days trying to make Mantis work with a Microsoft SQL Server database on a Windows Server 2003. Many people would ask why to use MSSQL instead of Mysql, a much better DBMS, not mention the OS election. I'm not planning to enter in a flame war. I wanted a free bug tracking system that worked with MSSQL. Candidates were Bugzilla, with higly experimental MSSQL support, BugTracker .NET, much settled on the platform but looking a bit amateur, and Mantis, the final election, which promised MSSQL support.

The fact is that it hasn't been a trivial task for me. I'm a Java (former VB.NET) developer, and I've had to fight a lot. Just in case someone else has the same problems, here I write what I've done [edited] to install Mantis 1.2.0:
  • Install PHP. Some tricks here. First. Use PHP 5.2. PHP 5.3 needs other configuration. Second. I installed in IIS as an ISAPI filter. Third. Install MSSQL extension in the PHP installation. Fourth. You need ntwdblib.dll in ...\system32 folder.

  • Unzip Mantis 1.2.0 and install as a new virtual project.

  • Link PHP with IIS:No tricks here. I followed this post in Spanish.

  • Create de Mantis SQL schema: The installation procedure of Mantis creates the original database (of an ancient version of Mantis) and in the same script upgrades the tables. I don't know the reason why it doesn't work in MSSQL but the fact is it doesn't work. So you have to create the SQL schema on hand. Here is my script:
    CREATE TABLE mantis_config_table (
    config_id VARCHAR(64) NOT NULL,
    project_id INT DEFAULT 0 NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    access_reqd INT DEFAULT 0,
    type INT DEFAULT 90,
    value TEXT NOT NULL,
    PRIMARY KEY (config_id, project_id, user_id)
    );

    CREATE INDEX idx_config ON mantis_config_table (config_id);

    CREATE TABLE mantis_bug_file_table (
    id INT IDENTITY(1,1) NOT NULL,
    bug_id INT DEFAULT 0 NOT NULL,
    title VARCHAR(250) DEFAULT '' NOT NULL,
    description VARCHAR(250) DEFAULT '' NOT NULL,
    diskfile VARCHAR(250) DEFAULT '' NOT NULL,
    filename VARCHAR(250) DEFAULT '' NOT NULL,
    folder VARCHAR(250) DEFAULT '' NOT NULL,
    filesize INT DEFAULT 0 NOT NULL,
    file_type VARCHAR(250) DEFAULT '' NOT NULL,
    date_added INT DEFAULT 1 NOT NULL,
    content IMAGE NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE INDEX idx_bug_file_bug_id ON mantis_bug_file_table (bug_id);

    ALTER TABLE mantis_bug_file_table ADD
    user_id INT DEFAULT 0 NOT NULL;


    CREATE TABLE mantis_bug_history_table (
    id INT IDENTITY(1,1) NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    bug_id INT DEFAULT 0 NOT NULL,
    date_modified INT DEFAULT 1 NOT NULL,
    field_name VARCHAR(32) DEFAULT '' NOT NULL,
    old_value VARCHAR(128) DEFAULT '' NOT NULL,
    new_value VARCHAR(128) DEFAULT '' NOT NULL,
    type SMALLINT DEFAULT 0 NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE INDEX idx_bug_history_bug_id ON mantis_bug_history_table (bug_id);

    CREATE INDEX idx_history_user_id ON mantis_bug_history_table (user_id);

    CREATE TABLE mantis_bug_monitor_table (
    user_id INT DEFAULT 0 NOT NULL,
    bug_id INT DEFAULT 0 NOT NULL,
    PRIMARY KEY (user_id, bug_id)
    );

    CREATE TABLE mantis_bug_relationship_table (
    id INT IDENTITY(1,1) NOT NULL,
    source_bug_id INT DEFAULT 0 NOT NULL,
    destination_bug_id INT DEFAULT 0 NOT NULL,
    relationship_type SMALLINT DEFAULT 0 NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE INDEX idx_relationship_source ON mantis_bug_relationship_table (source_bug_id);

    CREATE INDEX idx_relationship_destination ON mantis_bug_relationship_table (destination_bug_id);

    CREATE TABLE mantis_bug_table (
    id INT IDENTITY(1,1) NOT NULL,
    project_id INT DEFAULT 0 NOT NULL,
    reporter_id INT DEFAULT 0 NOT NULL,
    handler_id INT DEFAULT 0 NOT NULL,
    duplicate_id INT DEFAULT 0 NOT NULL,
    priority SMALLINT DEFAULT 30 NOT NULL,
    severity SMALLINT DEFAULT 50 NOT NULL,
    reproducibility SMALLINT DEFAULT 10 NOT NULL,
    status SMALLINT DEFAULT 10 NOT NULL,
    resolution SMALLINT DEFAULT 10 NOT NULL,
    projection SMALLINT DEFAULT 10 NOT NULL,
    eta SMALLINT DEFAULT 10 NOT NULL,
    bug_text_id INT DEFAULT 0 NOT NULL,
    os VARCHAR(32) DEFAULT '' NOT NULL,
    os_build VARCHAR(32) DEFAULT '' NOT NULL,
    platform VARCHAR(32) DEFAULT '' NOT NULL,
    version VARCHAR(64) DEFAULT '' NOT NULL,
    fixed_in_version VARCHAR(64) DEFAULT '' NOT NULL,
    build VARCHAR(32) DEFAULT '' NOT NULL,
    profile_id INT DEFAULT 0 NOT NULL,
    view_state SMALLINT DEFAULT 10 NOT NULL,
    summary VARCHAR(128) DEFAULT '' NOT NULL,
    sponsorship_total INT DEFAULT 0 NOT NULL,
    sticky BIT DEFAULT '0' NOT NULL,
    due_date INT DEFAULT 1 NOT NULL,
    date_submitted INT DEFAULT 1 NOT NULL,
    last_updated INT DEFAULT 1 NOT NULL,
    category_id INT DEFAULT 1 NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE INDEX idx_bug_sponsorship_total ON mantis_bug_table (sponsorship_total);

    CREATE INDEX idx_bug_fixed_in_version ON mantis_bug_table (fixed_in_version);

    CREATE INDEX idx_bug_status ON mantis_bug_table (status);

    CREATE INDEX idx_project ON mantis_bug_table (project_id);


    CREATE TABLE mantis_bug_text_table (
    id INT IDENTITY(1,1) NOT NULL,
    description TEXT NOT NULL,
    steps_to_reproduce TEXT NOT NULL,
    additional_information TEXT NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE TABLE mantis_bugnote_table (
    id INT IDENTITY(1,1) NOT NULL,
    bug_id INT DEFAULT 0 NOT NULL,
    reporter_id INT DEFAULT 0 NOT NULL,
    bugnote_text_id INT DEFAULT 0 NOT NULL,
    view_state SMALLINT DEFAULT 10 NOT NULL,
    date_submitted INT DEFAULT 1 NOT NULL,
    last_modified INT DEFAULT 1 NOT NULL,
    note_type INT DEFAULT 0,
    note_attr VARCHAR(250) DEFAULT '',
    PRIMARY KEY (id)
    );

    CREATE INDEX idx_bug ON mantis_bugnote_table (bug_id);

    CREATE INDEX idx_last_mod ON mantis_bugnote_table (last_modified);

    CREATE TABLE mantis_bugnote_text_table (
    id INT IDENTITY(1,1) NOT NULL,
    note TEXT NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE TABLE mantis_custom_field_project_table (
    field_id INT DEFAULT 0 NOT NULL,
    project_id INT DEFAULT 0 NOT NULL,
    sequence SMALLINT DEFAULT 0 NOT NULL,
    PRIMARY KEY (field_id, project_id)
    );

    CREATE TABLE mantis_custom_field_string_table (
    field_id INT DEFAULT 0 NOT NULL,
    bug_id INT DEFAULT 0 NOT NULL,
    value VARCHAR(255) DEFAULT '' NOT NULL,
    PRIMARY KEY (field_id, bug_id)
    );

    CREATE INDEX idx_custom_field_bug ON mantis_custom_field_string_table (bug_id);

    CREATE TABLE mantis_custom_field_table (
    id INT IDENTITY(1,1) NOT NULL,
    name VARCHAR(64) DEFAULT '' NOT NULL,
    type SMALLINT DEFAULT 0 NOT NULL,
    possible_values TEXT DEFAULT '' NOT NULL,
    default_value VARCHAR(255) DEFAULT '' NOT NULL,
    valid_regexp VARCHAR(255) DEFAULT '' NOT NULL,
    access_level_r SMALLINT DEFAULT 0 NOT NULL,
    access_level_rw SMALLINT DEFAULT 0 NOT NULL,
    length_min INT DEFAULT 0 NOT NULL,
    length_max INT DEFAULT 0 NOT NULL,
    require_report BIT DEFAULT '0' NOT NULL,
    require_update BIT DEFAULT '0' NOT NULL,
    display_report BIT DEFAULT '0' NOT NULL,
    display_update BIT DEFAULT '1' NOT NULL,
    require_resolved BIT DEFAULT '0' NOT NULL,
    display_resolved BIT DEFAULT '0' NOT NULL,
    display_closed BIT DEFAULT '0' NOT NULL,
    require_closed BIT DEFAULT '0' NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE INDEX idx_custom_field_name ON mantis_custom_field_table (name);

    ALTER TABLE mantis_custom_field_table ADD
    filter_by BIT DEFAULT '1' NOT NULL;



    CREATE TABLE mantis_filters_table (
    id INT IDENTITY(1,1) NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    project_id INT DEFAULT 0 NOT NULL,
    is_public BIT DEFAULT NULL,
    name VARCHAR(64) DEFAULT '' NOT NULL,
    filter_string TEXT NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE TABLE mantis_news_table (
    id INT IDENTITY(1,1) NOT NULL,
    project_id INT DEFAULT 0 NOT NULL,
    poster_id INT DEFAULT 0 NOT NULL,
    date_posted INT DEFAULT 1 NOT NULL,
    last_modified INT DEFAULT 1 NOT NULL,
    view_state SMALLINT DEFAULT 10 NOT NULL,
    announcement BIT DEFAULT '0' NOT NULL,
    headline VARCHAR(64) DEFAULT '' NOT NULL,
    body TEXT NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE TABLE mantis_project_category_table (
    project_id INT DEFAULT 0 NOT NULL,
    category VARCHAR(64) DEFAULT '' NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    PRIMARY KEY (project_id, category)
    );

    CREATE TABLE mantis_project_file_table (
    id INT IDENTITY(1,1) NOT NULL,
    project_id INT DEFAULT 0 NOT NULL,
    title VARCHAR(250) DEFAULT '' NOT NULL,
    description VARCHAR(250) DEFAULT '' NOT NULL,
    diskfile VARCHAR(250) DEFAULT '' NOT NULL,
    filename VARCHAR(250) DEFAULT '' NOT NULL,
    folder VARCHAR(250) DEFAULT '' NOT NULL,
    filesize INT DEFAULT 0 NOT NULL,
    file_type VARCHAR(250) DEFAULT '' NOT NULL,
    date_added INT DEFAULT 1 NOT NULL,
    content IMAGE NOT NULL,
    PRIMARY KEY (id)
    );

    ALTER TABLE mantis_project_file_table ADD
    user_id INT DEFAULT 0 NOT NULL;

    CREATE TABLE mantis_project_hierarchy_table (
    child_id INT NOT NULL,
    parent_id INT NOT NULL
    );

    CREATE TABLE mantis_project_table (
    id INT IDENTITY(1,1) NOT NULL,
    name VARCHAR(128) DEFAULT '' NOT NULL,
    status SMALLINT DEFAULT 10 NOT NULL,
    enabled BIT DEFAULT '1' NOT NULL,
    view_state SMALLINT DEFAULT 10 NOT NULL,
    access_min SMALLINT DEFAULT 10 NOT NULL,
    file_path VARCHAR(250) DEFAULT '' NOT NULL,
    description TEXT NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE INDEX idx_project_id ON mantis_project_table (id);

    CREATE UNIQUE INDEX idx_project_name ON mantis_project_table (name);

    CREATE INDEX idx_project_view ON mantis_project_table (view_state);

    CREATE TABLE mantis_project_user_list_table (
    project_id INT DEFAULT 0 NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    access_level SMALLINT DEFAULT 10 NOT NULL,
    PRIMARY KEY (project_id, user_id)
    );

    CREATE INDEX idx_project_user ON mantis_project_user_list_table (user_id);

    CREATE TABLE mantis_project_version_table (
    id INT IDENTITY(1,1) NOT NULL,
    project_id INT DEFAULT 0 NOT NULL,
    version VARCHAR(64) DEFAULT '' NOT NULL,
    date_order INT DEFAULT 1 NOT NULL,
    description TEXT NOT NULL,
    released BIT DEFAULT '1' NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE UNIQUE INDEX idx_project_version ON mantis_project_version_table (project_id, version);

    CREATE TABLE mantis_sponsorship_table (
    id INT IDENTITY(1,1) NOT NULL,
    bug_id INT DEFAULT 0 NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    amount INT DEFAULT 0 NOT NULL,
    logo VARCHAR(128) DEFAULT '' NOT NULL,
    url VARCHAR(128) DEFAULT '' NOT NULL,
    paid BIT DEFAULT '0' NOT NULL,
    date_submitted INT DEFAULT 1 NOT NULL,
    last_updated INT DEFAULT 1 NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE INDEX idx_sponsorship_bug_id ON mantis_sponsorship_table (bug_id);

    CREATE INDEX idx_sponsorship_user_id ON mantis_sponsorship_table (user_id);

    CREATE TABLE mantis_tokens_table (
    id INT IDENTITY(1,1) NOT NULL,
    owner INT NOT NULL,
    type INT NOT NULL,
    timestamp INT DEFAULT 1 NOT NULL,
    expiry INT DEFAULT 1 NOT NULL,
    value TEXT NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE TABLE mantis_user_pref_table (
    id INT IDENTITY(1,1) NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    project_id INT DEFAULT 0 NOT NULL,
    default_profile INT DEFAULT 0 NOT NULL,
    default_project INT DEFAULT 0 NOT NULL,
    refresh_delay INT DEFAULT 0 NOT NULL,
    redirect_delay INT DEFAULT 0 NOT NULL,
    bugnote_order VARCHAR(4) DEFAULT 'ASC' NOT NULL,
    email_on_new BIT DEFAULT '0' NOT NULL,
    email_on_assigned BIT DEFAULT '0' NOT NULL,
    email_on_feedback BIT DEFAULT '0' NOT NULL,
    email_on_resolved BIT DEFAULT '0' NOT NULL,
    email_on_closed BIT DEFAULT '0' NOT NULL,
    email_on_reopened BIT DEFAULT '0' NOT NULL,
    email_on_bugnote BIT DEFAULT '0' NOT NULL,
    email_on_status BIT DEFAULT '0' NOT NULL,
    email_on_priority BIT DEFAULT '0' NOT NULL,
    email_on_priority_min_severity SMALLINT DEFAULT 10 NOT NULL,
    email_on_status_min_severity SMALLINT DEFAULT 10 NOT NULL,
    email_on_bugnote_min_severity SMALLINT DEFAULT 10 NOT NULL,
    email_on_reopened_min_severity SMALLINT DEFAULT 10 NOT NULL,
    email_on_closed_min_severity SMALLINT DEFAULT 10 NOT NULL,
    email_on_resolved_min_severity SMALLINT DEFAULT 10 NOT NULL,
    email_on_feedback_min_severity SMALLINT DEFAULT 10 NOT NULL,
    email_on_assigned_min_severity SMALLINT DEFAULT 10 NOT NULL,
    email_on_new_min_severity SMALLINT DEFAULT 10 NOT NULL,
    email_bugnote_limit SMALLINT DEFAULT 0 NOT NULL,
    language VARCHAR(32) DEFAULT 'english' NOT NULL,
    PRIMARY KEY (id)
    );

    ALTER TABLE mantis_user_pref_table ADD
    timezone VARCHAR(32) DEFAULT '' NOT NULL;

    CREATE TABLE mantis_user_print_pref_table (
    user_id INT DEFAULT 0 NOT NULL,
    print_pref VARCHAR(27) DEFAULT '' NOT NULL,
    PRIMARY KEY (user_id)
    );

    CREATE TABLE mantis_user_profile_table (
    id INT IDENTITY(1,1) NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    platform VARCHAR(32) DEFAULT '' NOT NULL,
    os VARCHAR(32) DEFAULT '' NOT NULL,
    os_build VARCHAR(32) DEFAULT '' NOT NULL,
    description TEXT NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE TABLE mantis_user_table (
    id INT IDENTITY(1,1) NOT NULL,
    username VARCHAR(32) DEFAULT '' NOT NULL,
    realname VARCHAR(64) DEFAULT '' NOT NULL,
    email VARCHAR(64) DEFAULT '' NOT NULL,
    password VARCHAR(32) DEFAULT '' NOT NULL,
    date_created INT DEFAULT 1 NOT NULL,
    last_visit INT DEFAULT 1 NOT NULL,
    enabled BIT DEFAULT '1' NOT NULL,
    protected BIT DEFAULT '0' NOT NULL,
    access_level SMALLINT DEFAULT 10 NOT NULL,
    login_count INT DEFAULT 0 NOT NULL,
    lost_password_request_count SMALLINT DEFAULT 0 NOT NULL,
    failed_login_count SMALLINT DEFAULT 0 NOT NULL,
    cookie_string VARCHAR(64) DEFAULT '' NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE UNIQUE INDEX idx_user_cookie_string ON mantis_user_table (cookie_string);

    CREATE UNIQUE INDEX idx_user_username ON mantis_user_table (username);

    CREATE INDEX idx_enable ON mantis_user_table (enabled);

    CREATE INDEX idx_access ON mantis_user_table (access_level);

    INSERT INTO mantis_user_table(username, realname, email, password, date_created, last_visit, enabled, protected, access_level, login_count, lost_password_request_count, failed_login_count, cookie_string) VALUES
    ('administrator', '', 'root@localhost', '63a9f0ea7bb98050796b649e85481845', 1, 1, '1', '0', 90, 3, 0, 0, '43c90a58b766eca71a4eaec08120f1b51fec152d8f4d2c0cfec26390ed94f575');

    ALTER TABLE mantis_bug_history_table ALTER COLUMN old_value VARCHAR(255) NOT NULL;

    ALTER TABLE mantis_bug_history_table ALTER COLUMN new_value VARCHAR(255) NOT NULL;

    CREATE TABLE mantis_email_table (
    email_id INT IDENTITY(1,1) NOT NULL,
    email VARCHAR(64) DEFAULT '' NOT NULL,
    subject VARCHAR(250) DEFAULT '' NOT NULL,
    submitted INT DEFAULT 1 NOT NULL,
    metadata TEXT NOT NULL,
    body TEXT NOT NULL,
    PRIMARY KEY (email_id)
    );

    CREATE INDEX idx_email_id ON mantis_email_table (email_id);

    ALTER TABLE mantis_bug_table ADD
    target_version VARCHAR(64) DEFAULT '' NOT NULL;

    ALTER TABLE mantis_bugnote_table ADD
    time_tracking INT DEFAULT 0 NOT NULL;

    CREATE INDEX idx_diskfile ON mantis_bug_file_table (diskfile);

    ALTER TABLE mantis_user_print_pref_table ALTER COLUMN print_pref VARCHAR(64) NOT NULL;

    ALTER TABLE mantis_bug_history_table ALTER COLUMN field_name VARCHAR(64) NOT NULL;

    CREATE TABLE mantis_tag_table (
    id INT IDENTITY(1,1) NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    name VARCHAR(100) DEFAULT '' NOT NULL,
    description TEXT NOT NULL,
    date_created INT DEFAULT 1 NOT NULL,
    date_updated INT DEFAULT 1 NOT NULL,
    PRIMARY KEY (id, name)
    );

    CREATE TABLE mantis_bug_tag_table (
    bug_id INT DEFAULT 0 NOT NULL,
    tag_id INT DEFAULT 0 NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    date_attached INT DEFAULT 1 NOT NULL,
    PRIMARY KEY (bug_id, tag_id)
    );

    CREATE INDEX idx_typeowner ON mantis_tokens_table (type, owner);

    CREATE TABLE mantis_plugin_table (
    basename VARCHAR(40) NOT NULL,
    enabled BIT DEFAULT '0' NOT NULL,
    PRIMARY KEY (basename)
    );


    CREATE TABLE mantis_category_table (
    id INT IDENTITY(1,1) NOT NULL,
    project_id INT DEFAULT 0 NOT NULL,
    user_id INT DEFAULT 0 NOT NULL,
    name VARCHAR(128) DEFAULT '' NOT NULL,
    status INT DEFAULT 0 NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE UNIQUE INDEX idx_category_project_name ON mantis_category_table (project_id, name);

    INSERT INTO mantis_category_table
    ( project_id, user_id, name, status ) VALUES
    ( '0', '0', 'General', '0' ) ;


    DROP TABLE mantis_project_category_table;

    ALTER TABLE mantis_project_table ADD
    category_id INT DEFAULT 1 NOT NULL;

    INSERT INTO mantis_plugin_table
    ( basename, enabled ) VALUES
    ( 'MantisCoreFormatting', '1' );

    ALTER TABLE mantis_project_table ADD
    inherit_global INT DEFAULT 0 NOT NULL;

    ALTER TABLE mantis_project_hierarchy_table ADD
    inherit_parent INT DEFAULT 0 NOT NULL;

    ALTER TABLE mantis_plugin_table ADD
    protected BIT DEFAULT '0' NOT NULL,
    priority INT DEFAULT 3 NOT NULL;

    ALTER TABLE mantis_project_version_table ADD
    obsolete BIT DEFAULT '0' NOT NULL;

    CREATE TABLE mantis_bug_revision_table (
    id INT IDENTITY(1,1) NOT NULL,
    bug_id INT NOT NULL,
    bugnote_id INT DEFAULT 0 NOT NULL,
    user_id INT NOT NULL,
    timestamp INT DEFAULT 1 NOT NULL,
    type INT NOT NULL,
    value TEXT NOT NULL,
    PRIMARY KEY (id)
    );

    CREATE INDEX idx_bug_rev_id_time ON mantis_bug_revision_table (bug_id, timestamp);

    CREATE INDEX idx_bug_rev_type ON mantis_bug_revision_table (type);

    CREATE INDEX idx_project_hierarchy_child_id ON mantis_project_hierarchy_table (child_id);

    CREATE INDEX idx_project_hierarchy_parent_id ON mantis_project_hierarchy_table (parent_id);

    CREATE INDEX idx_tag_name ON mantis_tag_table (name);

    CREATE INDEX idx_bug_tag_tag_id ON mantis_bug_tag_table (tag_id);

    INSERT INTO mantis_config_table ( value, type, access_reqd, config_id, project_id, user_id ) VALUES ('182', 1, 90, 'database_version', 0, 0 );

  • Create a config_inc.php file: No tricks here. You have to enter database connection data. Mine is more complicated but I guess something like this could do the trick:
    <?php
    $g_hostname = '***';
    $g_db_type = 'mssql';
    $g_database_name = '***';
    $g_db_username = '***';
    $g_db_password = '***';
    ?>

  • The final trick: Don't ask me why (and don't ask me how I discovered), but the fact is that you have to edit the file ...\library\adodb\adodb.inc.php, and change the sentence
    define('ADODB_FETCH_ASSOC',2);
    to
    define('ADODB_FETCH_ASSOC',0);