Use a Spring InitBinder to Resolve Type Mismatch and Bind Exceptions in POST from Spring Framework MVC Forms to Controller Actions

As a follow up to the previous article on binding entities and their children to form objects in Spring Framework it’s important to know how to submit the values of form objects in a standard form back to the controller. In the previous example we had a “Parent” entity, with a dropdown “select” on the form where you could choose one of the available “Children” objects. We also had a “Description” textbox that allowed the user to type in a suitable description.

The actual form, a very simple interface with both these elements and a submit button looks like:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>

<c:url var="saveParentUrl" value="/parent/save" />
<form:form modelAttribute="parent" method="POST" action="${saveParentUrl}">
Description:  <form:input path="description"/>
Child:  <form:select path="child" id="child" items="${children}" itemValue="id" itemLabel="name"/>
<input type="submit" value="Save Parent" />
</form:form>

In this example, when the user clicks on the “Save Parent” button the form will be posted to the “parent” controller action “saveParent” at “/parent/save” where we can save the updated “Parent” object.

Now the problem is that the POST header only contains text strings, not a full description of the actual “Child” object we have chosen for “Parent”. When you try to save the “Parent” object in the controller action the result is an exception,  ”org.springframework.validation.BeanPropertyBindingResult”. The error message itself tells you exactly what is happening:

default message [Failed to convert property value of type 'java.lang.String[]‘ to required type ‘models.Child’ for property ‘Child’; nested exception is java.lang.IllegalStateException: Cannot convert value of type ] to required type [models.Child] for property ‘Child’: no matching editors or conversion strategy found]

The message tells you that Spring can’t automatically convert from the POST string value of the “child” select option (actually “id” in our case) to an actual “Child” object. We need to convert this string to a “Child” object in order to save the updated “Parent” entity. This is easily achieved using an “InitBinder” method within the controller class and a “@Validated” annotation on the “save” action’s “Parent” “@ModelAttribute” argument.

The code for the controller action looks like the following. Note that in this example I am using a “parentService” to pull the protocol to be updated from a “Parent” stored in session. It is likely that you will have something different or even pass in the id as part of the POST:

// ParentController.java
@RequestMapping(value = "/save", method=RequestMethod.GET)
public ModelAndView saveParent (@Validated @ModelAttribute("parent") Parent formparent, HttpServletRequest request )
{
    ...
    // find parent to update in database using parent service
    Parent parent = parentService.findById(sessionparent.getId());

    // set parent description and Child based on user input and save
    parent.setDescription(formparent.getDescription());
    parent.setChild(formparent.getChild());
    parentService.saveParent(parent);
    ...
}

The “formparent” is the “Parent” object passed in POST and mapped by Spring using the “@ModelAttribute” annotation. The “InitBinder” required to map the “Child” id passed in as part of POST to an actual “Child” object is:

@InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
    binder.registerCustomEditor(Child.class, "child", new PropertyEditorSupport() {
    @Override
    public void setAsText(String text) {
        Child ch = childService.findById(Long.parseLong(text));
        setValue(ch);
    }
    });
}

What this does is register a binder for the “child” object when it is passed from the form to Spring that converts the value to an actual object. By overriding the “SetAsText” function I use a “childService” service to retrieve the correct “Child” entity based on the text value passed in POST. Spring can now understand and convert the POST values to actual objects and the “Parent” entity can be safely modified by the “saveParent” controller action mapped to “/parent/save”. More information and some alternative methods are available at Develop and Conquer and Empire5.

Quickly add a Virtual Host to Apache Tomcat to Map URLs to Java Web Applications

Adding a virtual host to Apache Tomcat is really easy and just involves pointing the address at the right directory. This means you can map web addresses pointing at your server IP to Java web applications running on Apache Tomcat. This assumes you already have a hostname such as “websiteaddress.com” pointing to your server with an IP (for example) of 1.2.3.4.

Just modify you server.xml file (in our Ubuntu Server tomcat7 setup in “/usr/share/tomcat7/conf/server.xml”) to include the following at the end of the file in the “Engine” element after the “Host” element for localhost:


      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>

      <Host name="websiteaddress.com" appBase="/usr/share/tomcat7/webapps/myapplication">
        <Context path="" docBase="."/>
      </Host>

Note here that the web address is http://websiteaddress.com and is being resolved to the application at “/usr/share/tomcat7/webapps/myapplication”. More information about this “Host” element is available at the Apache Tomcat website.

Binding Entities and Their Children to Java JSP Form Elements in Spring Framework MVC

Binding objects to JSP form elements is actually quite easy in Spring Framework using the “modelAttribute” and “path” tags. Our scenario was the use of two Hibernate POJOs comprised of a parent entity “Parent” and a single “Child” entity which needed to be displayed on screen with appropriate form elements (“input” and “select”). The entities were annotated for use as part of a larger application but the basic structure was:

// Parent.java
public class Parent{
  private Int id
  private String description
  private Child child
  ...
}

// Child.java
public class Child{
  private Int id
  private String name
  ...
}

To display these objects on screen as part of a form in Spring Framework MVC we used the following “ParentController” controller with a “showparent” action. The action uses a “parentService.getParent(id)” method (not shown) to retrieve a single parent entity based on “id” and attach it to the form using a Map “model” and a returned “ModelAndView”. A “childService” is used to get the list of every possible child entity (also not shown) and this is also added to the “ModelAndView”:

// ParentController.java
@RequestMapping(value = "/showparent", method=RequestMethod.GET)
public ModelAndView protocolList(HttpServletRequest request)
{
	...
	Map<String, Object> model = new HashMap<String, Object>();
	model.put ( "children", childService.getAll());
	model.put ( "parent", parentService.getParent(id));
	return new ModelAndView ( "showparent", model );
}

The form itself is very simple and uses Spring binding to automatically create and populate input fields:

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<form:form modelAttribute="parent" >
Description:  <form:input path="description"/>
Child:  <form:select path="child" id="child" items="${children}" itemValue="id" itemLabel="name"/>
</form:form>

Spring Framework uses the “modelAttribute” and “path” tags to determine what is set on the form. In this case the forms “modelAttribute” is “parent” which means that the elements in the form are properties of the “parent” entity. The “description” is shown as the contents of a simple textbox input using “form:input”. The interesting part is the “form:select” which automatically populates the select with option objects based on the set of “child” objects in “children” and selects the correct “child” option for the “parent” based on the “itemValue”, “id” in this case.

Submitting changes to “parent” entities is covered in my more recent post.

Extracting and Copying Mail and Calendar Appointments from a Corrupted Microsoft Windows Live Mail Installation (Calendar EDB/ESE Database Files)

In order to install another one of the Live series of packages (Movie Maker) on Windows 7 a colleague updated their Window Live Mail package as part of the install process. The install process then hung and crippled the Mail program, stopping it from booting with a useless “Windows Live Mail has stopped working” message.

As they were using POP3 to download mail and not synchronising with the calendar server very often they were worried about losing everything, including all their very important calendar appointments. First step was to try System Restore, which didn’t work (all the system restore points we tried came back with the same error in Windows Live Mail. Next we had to look at manually moving and editing files.

We decided that the next best thing would be to set up Windows Live Mail on another machine before manually copying the data from the corrupted machine over to the new one. You can alternatively just make backups of your data and try uninstalling/re-installing Windows Live Mail again.

Mail retrieval from the existing install is easy as all you need to do is copy some physical files across from the USERNAME C:\Users directory. Just move the subfolders (containing mail) from “C:\Users\USENAME\AppData\Local\Microsoft\Windows Live Mail” to the other machine with a good copy of Windows Live Mail. You will need to set up your email retrieval settings again but your old mail should just appear in all the correct folders.

Retrieval of a corrupted calendar is a lot more tricky and needs some free third party tools in order to work. Despite the availability of several (very poor) tools I couldn’t find a way of extracting the calendar data in a format that could be easily imported into the working Windows Live Mail program. Most tools simply refused to open the data file. As a result I had to extract the data and my colleague had to manually enter the appointments again (which is still better than losing everything). I went a step further and wrote a little PHP script to display the data more easily so they didn’t have as hard time of it.

By far the most success I had was with NirSoft ESEDatabaseView which could open the corrupted Live Mail Calendar database file where all other programs failed. The “WLCalendarStore.edb” file containing the database of calendar appointments was found at “C:\Users\USERNAME\AppData\Local\Microsoft\Windows Live Mail\Calendars\DBStore\WLCalendarStore.edb”. I downloaded ESEDatabaseView and ran the executable from the zip. Then I opened the corrupted “WLCalendarStore.edb” and selected the “calendarItem” table from the dropdown. Now this is great, but everything is in HEX format and needs to be converted to normal text!

I first extracted the HEX encoded CalendarItem data to CSV (click on an item in the list of CalendarItem, ctrl-A, ctrl-S then Save as Type “Comma Delimited Text File (*.csv)”). It’s up to you how best to convert this HEX information from the output CSV but I used the following PHP script to convert and display the easy to understand “ServerIcal” column which was output as column 17 in the CSV. Note that I have two parts to this PHP, one part that exports nicely human readable data and the other that just outputs the raw iCal style data:


<?php

// helper function to convert hex value to string
function hex2str($hex) {
    for($i=0;$i<strlen($hex);$i+=2) $str .= chr(hexdec(substr($hex,$i,2)));
    return $str;
}

// show appointments as easy to read HTML
echo "<h1>Appointments</h1>";

if (($handle = fopen("calendaritem.csv", "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000000, ",")) !== FALSE) {

			// get the ServerIcal column
			$ical = $data[17];

			// strip out all '00' and ' ' strings from the output
			$ical = str_replace(" 00 ","",$ical);
			$ical = str_replace(" ","",$ical);

			// convert to string
			$line = hex2str($ical);

			// tidy up HTML to make it easily human readable
			$line = substr($line,strpos($line,"DTSTART"));
			$line = substr($line,0,strpos($line,"UID"));
			$line = str_replace("SUMMARY:","SUMMARY: <strong>",$line);
			$line = str_replace(PHP_EOL,"</strong>" .PHP_EOL,$line);
			$line = str_replace("DTSTART;VALUE=DATE:","DTSTART;VALUE=DATE: <strong>",$line);
			$line = str_replace(" DTEND;","</strong>  DTEND;",$line);
			$line = str_replace("DTEND;VALUE=DATE:","DTEND;VALUE=DATE: <strong>",$line);
			$line = str_replace("  SUMMARY: ","</strong>   SUMMARY: ",$line);
			$line = str_replace("DTSTART;VALUE=DATE:","Start Date:",$line);
			$line = str_replace("DTEND;VALUE=DATE:","End Date:",$line);

			// output as HTML
			echo "<br/>$line";
        }

    fclose($handle);
}

// show raw appointment data
echo "<h1>Raw Data</h1>";

if (($handle = fopen("calendaritem.csv", "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000000, ",")) !== FALSE) {

			// get the ServerIcal column
            $ical = $data[17];

			// strip out all '00' and ' ' strings from the output
			$ical = str_replace(" 00 ","",$ical);
			$ical = str_replace(" ","",$ical);

			// convert to string
			$line = hex2str($ical);

			// output as HTML
			echo "<br/><br/>$line";
        }

    fclose($handle);
}

?>

The output as HTML was easily readable enough that my colleague could manually enter all the appointments again.

Obviously I would much prefer to output from the database to something that could be directly imported but this functionality isn’t available in Windows Live Mail and the database was so corrupted we couldn’t even just copy it over to the new machine. My colleague is now looking at some of the many alternatives to POP3 and manual sync of calendar items using Windows Live Mail.

Quickly and Safely Move a Microsoft SQL Server Database (MDF and LDF Files) to a New Physical Location (Including Setting Read Write Access)

To move a Microsoft SQL Server database to a new physical location you need to detach, copy, reattach and set permissions for the database MDF and log LDF files associated with the database. I needed to do it as the drive on which each type of database required file (MDF/LDF) was located was to be separately backed up, as per the site backup regulations. I was using SQL Server 2008 R2.

Microsoft’s recommended way of doing this is to use SQL Management Studio. Create a query and detach your database “mydb” by entering and running the following:

use master
go
sp_detach_db ‘mydb’
go

Now copy the MDF and LDF files to their new location and reattach (locations and file names are for this example only):

use master
go
sp_attach_db ‘mydb’,'E:\DATA\mydb.mdf’,'F:\DATA\mydb_log.ldf’
go

You can check the basic properties of the database (and that the file locations have been correctly set) using:

use mydb
go
sp_helpfile
go

The final thing I needed to do was to set the file/folder permissions so that the database could go from read-only to read/write. I first set the folder permissions for my two new folders (“E:\DATA\” and “F:\DATA\” as above). To do this I needed to add the following user to the security settings with full control:

sqlservermssqluser$COMPUTERNAME$mssqlserver

This didn’t actually set the files (server permissions error) so I set the permissions for the “E:\DATA\mydb.mdf” and ”F:\DATA\mydb_log.ldf” files individually the same way.

Now open up another query window and type the following to enable read/write access to the database:

use master
go
alter database mydb set read_write with no_wait
go

Now your database is set up exactly as it was previously, only the associated files have moved physical location.

Quick Subversion (SVN) Server Setup on Ubuntu Server 12.04

Setting up an Apache Subversion (SVN) server for access using svn:// with client applications like TortoiseSVN is actually pretty simple. The official Ubuntu Documentation covers a lot more than this simple setup but this is enough to get something up and running quickly without worrying about WebDAV or HTML access. The odyniec.net tutorial is also really useful and provides the init.d startup script I used to make the SVN server run at boot.

The steps are; install subversion, create the repository directories, set access control, set subversion to run at boot.

To install subversion in ubuntu just run:

sudo apt-get install subversion

Now create a directory to hold your subversion repositories, in my case I used “/home/svn”:

sudo mkdir /home/svn

Create a repository folder, for example “svnrepo1″, within this directory:

sudo mkdir /home/svn/svnrepo1

Now you can use the “svnadmin” program that comes as part of the subversion package to create a SVN repository within this folder:

sudo svnadmin create /home/svn/svnrepo1

The configuration file for the repository is created as ”/home/svn/svnrepo1/conf/svnserve.conf” and contains the option to enable password protection as well as a lot of other useful settings. The important lines to uncomment to force password access are:

anon-access = none
auth-access = write

By setting “anon-access” to “none” you force people to enter passwords on connecting to the SVN. Now set up password protected access by uncommenting the following:

password-db = passwd

Settings “password-db” to “passwd” means the list of users and passwords in the “/home/svn/svnrepo1/conf/passwd” file will be used to check if someone has access. In a lot of cases it makes sense to keep this “passwd” file somewhere else so it can be used for all your repositories. In my case I set it to:

password-db = /home/svn/passwd

Just make sure to set the passwd file to be only readable by root:

sudo chmod 600 /home/svn/passwd

The “passwd” file is actually a very simple text file and looks something like:

[users]
harry = harrypassword
sally = sallypassword

Once the SVN server is configured and a repository set up as above you can run the SVN server using:

svnserve -d –foreground -r /home/svn

To make sure the SVN server starts at boot you need to set up init.d. Do this by creating and editing a file “/etc/init.d/svnserve” (I use “nano” to do my text editing on the command line):

sudo nano /etc/init.d/svnserve

Now paste in the contents of the odyniec.net init.d script. This script covers everything you need to start, stop and restart the “svnserve” program at boot so your SVN server can listen to all svn:// connections. There are alternatives to using this script, but this works and is simple to set up. Make sure you change the line with “DAEMON_ARGS” to point to the right place of “/home/svn”:

DAEMON_ARGS=”-d -r /home/svn”

Now tell Ubuntu to update its startup routine to include this new script:

sudo update-rc.d svnserve defaults

Reboot the server to make sure everything is working as expected.

You can now start, stop or restart the automatically booted SVN server using the following commands:

sudo /etc/init.d/svnserve start
sudo /etc/init.d/svnserve stop
sudo /etc/init.d/svnserve restart

Connecting to your SVN server can be done using something like TortoiseSVN and the URL you use to connect to the “svnrepo1″ repository you just set up is:

svn://your.serv.er.ip/svnrepo1

There is a lot more you can do with the SVN configuration, such as adding group support etc, but this is the quickest way to set up a standard SVN server on Ubuntu to accept svn:// connections using “svnserve”.

“unblock” an entire directory of files (rather than individually) when copying files between NTFS locations in Windows Server

We copied a few hundred files between a Windows Server 2003 machine and a Windows Server 2008 machine in order to migrate a ASP.NET web application. There are a few hoops to jump through as once it was set up I instantly hit:

Request for the permission of type ‘System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b27b5d561934e089′ failed. (C:\inetpub\wwwroot\WebApp\web.config line 90)

This was related to a “<httpModules>” element in the “Web.config” file.

On searching for similar problems I found a blog post on MSDN which stated that this could be due to a DLL file that needed to be unblocked (right click, “Unblock” button) after copying from another location. Unfortunately, we couldn’t go through and unblock each individual file to try and get round this as there were so many.

The solution (thanks to superuser/StackExchange) is to zip up all the files into one compressed archive before transferring them. Then the NTFS flagging of “unsafe” files that need to be unblocked only includes the one file, simple!

This solved our System.Web.AspNetHostingPermission error straight away, implying that the blocking of files by NTFS for security reasons can affect ASP.NET migration.

Simple and secure MySQL database backup to gzip using mysqldump in Linux

As part of a larger daily backup cron job script I needed to quickly backup my MySQL databases to individual compressed “gzip” .GZ files. The command to do this is very easy, just run the command and pipe it to “gzip”:

mysqldump -u USERNAME -pPASSWORD DATABASENAME | gzip > OUTPUTFILE.gz

This requires you to actually put in the USERNAME and PASSWORD on the command line, which is obviously a bad idea due to logging of commands and other security reasons.

The MySQL recommended way of doing this is to instead use a separate file containing the login details. You use “mysqldump” with the argument “–defaults-extra-file” and specify the location of a configuration file such as “/root/mysqldetails.cnf”. It is a good idea to create this file and “chown” as root and “chmod” it to be “0400″ which will make it read-only by the “root” user.

chown root:root /root/mysqldetails.cnf
chmod 0400 /root/mysqldetails.cnf

The file itself is a very simple text file and just looks something like:

[client]
host = localhost
user = USERNAME
password = PASSWORD

So now this file has been created and the permissions set correctly, the mysqldump command looks like:

mysqldump –defaults-extra-file=/root/mysqldetails.cnf DATABASENAME | gzip > OUTPUTFILE.gz

The result is OUTPUTFILE.gz which is a compressed copy of your DATABASENAME database, without showing anyone the username and password required to access the database. The “mysqldump” command is very useful and more information can be found in the MySQL documentation.

Encrypt a USB drive in linux and automatically mount it on startup using a keyfile and dm_crypt

The easiest way of doing this is to use dm_crypt‘s “cryptsetup” on your USB drive, create a keyfile then set the options in “/etc/fstab” and “/etc/crypttab”. By using a keyfile you can get the drive to automatically mount without having to type in your encryption password. I was doing this on a bare install of CentOS 6.3 but the steps should be similar on other distros with “cryptsetup” installed.

I needed to back up some important (and confidential) files to a USB portable drive that I wanted to encrypt with full disk encryption. You can do this in a variety of ways but the method here was the easiest I found. More information can be found at Brad’s Blog and HowtoForge.

Encrypting and mounting your USB drive

First you need to physically plug in your USB drive to the machine and then unmount it if it automatically mounts. I performed all the commands here using the root user. In my case, when I plugged in the USB drive it was found as “/dev/sdb” and automatically mounted by CentOS. To unmount:

umount /dev/sdb

Now the USB drive needs to be formatted using “cryptsetup” and the “luksFormat” command:

cryptsetup luksFormat /dev/sdb

The tool will give you a warning about overwriting data, which you need to confirm by typing an uppercase “YES”. You then type in and confirm your LUKS passphrase, which will be used to unlock the drive in future. This passphrase is also used later when creating the keyfile.

Now you can create a device mapper for the drive using “cryptsetup” and the “luksOpen” command. I called my mapper “secretvol” in this example so the drive will be mapped to “/dev/mapper/secretvol”. You will be prompted for the passphrase:

cryptsetup luksOpen /dev/sdb secretvol

Now before you can mount your newly mapped device you need to format the file system (I used ext3):

mkfs.ext3 /dev/mapper/secretvol

Now you can mount the USB drive. Make sure you have created the mount point (in my case “/mnt/encrypteddrive”) first then mount it with:

mkdir /mnt/encrypteddrive
mount /dev/mapper/secretvol /mnt/encrypteddrive

To test this all works properly reboot your machine before unlocking and mounting your USB drive manually (requiring entry of the passphrase):

cryptsetup luksOpen /dev/sdb secretvol
mount /dev/mapper/secretvol /mnt/encrypteddrive

To unmount and lock the drive by closing the device mapper with the “luksClose” command:

umount /dev/mapper/secretvol
cryptsetup luksClose secretvol

Creating a keyfile to avoid entering your passphrase manually

A keyfile is good as it means you can unlock your USB drive without having to manually type the passphrase. To create a keyfile “/root/keyfile” for your device using “cryptsetup” and the “luksAddKey” command enter the following (you will need to enter your passphrase). The first command creates a random 4096 byte file, the second makes it read only to root and the third stores your passphrase in the keyfile using “luksAddKey”:

dd if=/dev/urandom of=/root/keyfile bs=1024 count=4
chmod 0400 /root/keyfile
cryptsetup luksAddKey /dev/sdb /root/keyfile

Now you can unlock your previously created drive without manually entering the passphrase using:

cryptsetup luksOpen –key-file /root/keyfile /dev/sdb secretvol

And mount with:

mount /dev/mapper/secretvol /mnt/encrypteddrive

Automatically unlock and mount your encrypted USB drive at system startup

Now that you have a keyfile you can set up your linux install to automatically unlock and mount the USB drive by editing a couple of files.

Edit your “/etc/crypttab” file:

nano /etc/crypttab

Add the line below to add the “/dev/mapper/secretvol” device:

secretvol /dev/sdb /root/keyfile luks

NOTE: You can also use the UUID of your drive in “/etc/crypttab” to make sure that the right disk as detected by the kernel is used. In cases where you may be adding or removing disks this is really important as you may have “sdb” or “sdc” or “sdX” depending on what order the disks are detected by your linux install. To find the right UUID type:

ls -l /dev/disk/by-uuid

Which in my case told me that my UUID for “sdb” (my USB drive) was “6858274d-2370-4377-9426-d786c3e7a410″. The line in “/etc/crypttab” that you should use in this case to add “/dev/mapper/secretvol” is:

secretvol /dev/disk/by-uuid/6858274d-2370-4377-9426-d786c3e7a410 /root/keyfile luks

Now edit your “/etc/fstab” file:

nano /etc/fstab

Add the line below to automatically mount the device to “/mnt/encrypteddrive”:

/dev/mapper/secretvol /mnt/encrypteddrive ext3 defaults 0 2

Now to test this, reboot your machine and navigate to “/mnt/encrypteddrive” where your USB drive will be mounted automatically for you. Easy!

Run .bat batch and .cmd files as scheduled tasks in Windows with a local user (avoid the “Could not start” error)

Running scheduled tasks as a local user means you can lock down user permissions and avoid giving broad admin rights to your local users. I have a scheduled task that needed to be run by a local user by running a .cmd (.bat works as well) batch file every day.

I created the local user with a password and added the scheduled task to run my .cmd file every day at 4am. When adding the scheduled task I put in the correct user details and password and then tried to run it, which failed with a “could not start” error.

The reason for this is that by default, new local users do not have read and execute permissions on “cmd.exe” which is used by Windows task scheduler to start .cmd and .bat files in scheduled tasks. The fix is to navigate to your “system32″ directory (probably “c:\windows\system32″) and right click on the “cmd.exe” application, go to the security tab and add your new local user with “Read & Execute” permissions.

Once the security settings for “cmd.exe” are set to allow your local user to run it, the task scheduler will now allow your .cmd/.bat scheduled task to run with that local user and everything will work fine.