<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ad Astra Systems</title>
	<atom:link href="http://adastrasystems.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://adastrasystems.com</link>
	<description>News from Ad Astra Systems</description>
	<lastBuildDate>Mon, 30 Aug 2010 20:09:16 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Cross-browser methods to read local files</title>
		<link>http://adastrasystems.com/2010/08/25/cross-browser-methods-to-read-local-files/</link>
		<comments>http://adastrasystems.com/2010/08/25/cross-browser-methods-to-read-local-files/#comments</comments>
		<pubDate>Wed, 25 Aug 2010 18:39:31 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[Actionscript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://adastrasystems.com/?p=351</guid>
		<description><![CDATA[I&#8217;ve been working on a web project where we need to open and read the content of local files from their file system. Now, we could just go the traditional route and upload the file to our backend server, and do the processing there. However, these files can be big, like 15MB big in one [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been working on a web project where we need to open and read the content of local files from their file system. Now, we could just go the traditional route and upload the file to our backend server, and do the processing there. However, these files can be big, like 15MB big in one example and processing them can take some hefty CPU effort.</p>
<p>Generally, when I design a web-based project with some pretty extensive logic I like to push as much processing up to the client as possible &#8211; which generally isn&#8217;t much work for the client, but if we had thousands of users hitting the server doing that same work on the server then it would be a lot of work for the server. </p>
<h3>Pure Javascript (kinda)</h3>
<p>Turns out you can read the content of local files in Javascript, but it only works for IE and Firefox. For example;</p>
<div style="width:750px; scroll:auto">
<pre name="code" class="html" >
<form action="/" method="post" onsubmit="readFileContents(); return false;">
<fieldset>
<legend>Read File</legend>
<input type="file" name="local_file" id="local_file">
<input type="submit" value="Read Contents">
	</fieldset>
</form>
<fieldset>
    File Name: <span id='fileName'></span> <br/>
    File Size: <span id='fileSize'></span> <br/>
    <br/><br/>
<div id='fileContents'></div>
</fieldset>
</pre>
</div>
<div style="width:750px; scroll:auto">
<pre name="code" class="js" >
function readFileContents() {

    var temp = document.getElementById("local_file");
    var file = temp.files[0];
    var contents = "";

	document.getElementById("fileName").innerHTML = file.fileName;
	document.getElementById("fileSize").innerHTML = file.fileSize;

	// Try using the firefox method first.
    if (file.getAsBinary) {
        contents = file.getAsText("utf8");
        // Or; file.getAsBinary()
    }
    else {
        // try the IE method instead
	    try {
	        var fso = new ActiveXObject("Scripting.FileSystemObject");
	        var handle = fso.OpenTextFile(file.fileName, 1);
	        contents = fh.ReadAll();
	        fh.Close();
	    }
	    catch (Exception) {
	        alert('Counld not read file using IE method!!');
	    }

    }

    document.getElementById("fileContents").innerHTML = contents;

    return false;
}
</pre>
</div>
<h3>Flash (Flash Player 10+)</h3>
<p>Now, Firefox and IE does cover <em>most</em> people out there so we could just be done right here. But&#8230;.. that&#8217;s not everyone, so whats the alternative? Well, Flash or a Java applet would be the way to go &#8211; but I decided the Flash route was the best. Ever since Flash Player 10 came out, a new method was added to the FileReference object called load() that allows us to read a local file (methods to write to a local file were also added, but that&#8217;s just getting scary) . If you&#8217;ve ever used flash to handle file uploads, this should be pretty familiar to you. If not, its pretty straightforward as you can see from the ActionScript 3 code below. </p>
<div style="width:750px; scroll:auto">
<pre name="code" class="java" >

package
{
    import flash.display.Sprite;
    import flash.events.*;
    import flash.net.FileFilter;
    import flash.net.FileReference;
    import flash.net.URLRequest;
    import flash.utils.ByteArray;
	import flash.external.ExternalInterface;

    public class LocalFileReader extends Sprite
    {
        private var fileRef:FileReference; 

        public function LocalFileReader()
        {
			ExternalInterface.call('LocalFileReader.onMessage','LocalFileReader...');
            fileRef = new FileReference();
            fileRef.addEventListener(Event.SELECT, onFileSelected);
            fileRef.addEventListener(Event.CANCEL, onCancel);
            fileRef.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
            fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
            var textTypeFilter:FileFilter = new FileFilter("Data Files (*.txt, *.rtf, *.xml. *.csv)", "*.txt;*.rtf; *.xml; *.csv");
			ExternalInterface.call('LocalFileReader.onMessage','Calling browse...');

			try {
				fileRef.browse([textTypeFilter]);
			}
			catch (error:SecurityError) {
				ExternalInterface.call('LocalFileReader.onError','Security error...'+error.toString());
			}
			catch (error:Error) {
				ExternalInterface.call('LocalFileReader.onError','Error...'+error.toString());
			}		

        } 

        public function onFileSelected(evt:Event):void
        {
            fileRef.addEventListener(ProgressEvent.PROGRESS, onProgress);
            fileRef.addEventListener(Event.COMPLETE, onComplete);
			ExternalInterface.call("LocalFileReader.onFileSelected", fileRef.name, fileRef.size, fileRef.type);
            fileRef.load();
        } 

        public function onProgress(evt:ProgressEvent):void
        {
			ExternalInterface.call("LocalFileReader.onProgress", evt.bytesLoaded, evt.bytesTotal);
        } 

        public function onComplete(evt:Event):void
        {
			// Get the data from the file as a ByteArray
			var data:ByteArray = fileRef.data;

			ExternalInterface.call("LocalFileReader.onLoaded", data.readUTFBytes(data.bytesAvailable));
        } 

        public function onCancel(evt:Event):void
        {
            trace("The browse request was canceled by the user.");
        } 

        public function onIOError(evt:IOErrorEvent):void
        {
			ExternalInterface.call("LocalFileReader.onError", "There was an IO Error.");
        }

        public function onSecurityError(evt:Event):void
        {
			ExternalInterface.call("LocalFileReader.onError", "There was a security error.");

        }
    }
}
</pre>
</div>
<h3>Javascript love</h3>
<p>As you can see from this code, we&#8217;re passing events and ultimately the file contents back to Javascript using Flash&#8217;s ExternalInterface method, and in this case we&#8217;re using a static Javascript class called LocalFileReader with the methods that you see above &#8211; this keeps everything nice and neat. We should probably also do some checks within flash to make sure the ExternalItnerface is available before we make these calls using ExternalInterface.available, but we didn&#8217;t in this simple example. Besides, if that interface wasn&#8217;t available we&#8217;d be in bigger trouble anyhow.</p>
<h3>But wait&#8230; this seems sooo insecure</h3>
<p>Now, the only caveat here is that this code has to be launched from a user action within flash, such as a button click. If you try to call this externally using Javascript you&#8217;ll get a security violation, which makes sense when you think about it. So, I just create a simple button and then instantiate the LocalFileReader class and bob&#8217;s your uncle, it all works!</p>
<div style="width:750px; scroll:auto">
<pre name="code" class="java" >

// Requires a Button component instance on the Stage with an instance name of "browseButton".
browseButton.addEventListener(MouseEvent.CLICK, clickHandler);

function clickHandler(eventObj:MouseEvent):void {
    var fr = new LocalFileReader();
}
</pre>
</div>
<h3>LocalFileReader.js</h3>
<p>The Javascript class that handles the other side can be seen here, its not doing anything with the data, but just in case you want to have a look;</p>
<div style="width:750px; scroll:auto">
<pre name="code" class="js" >
var LocalFileReader = {

	// ////////////////////////////////////////////////////////////////////////

	paint : function(targetDiv){

		var flash_url = defines.code_url + "flash/LocalFileReader.swf";

		var flashvars = {};
		var params = {
		  bgcolor: "#ffffff",
		  quality: "high",
		  menu: "false",
		  allowScriptAccess: "sameDomain"
		};
		var attributes = {
		  id: "localFileReader",
		  name: "localFileReader"
		};

		swfobject.embedSWF(flash_url, targetDiv, "80", "22", "10.0.0","expressInstall.swf", flashvars, params, attributes);

	},

	// ////////////////////////////////////////////////////////////////////////

	onFileSelected : function(name, size, type){
		LocalFileReader.onMessage(name + " " + size + "kb " + type);
	},

	onProgress : function(bytes, total){
		LocalFileReader.onMessage(bytes + " / " + total);
	},

	onLoaded : function(content){
		$('#fileContents').html(content);
	},

	// ////////////////////////////////////////////////////////////////////////

	onMessage : function(msg){
		$('#msg').append(msg + "");
	},

	onError : function(msg){
		$('#msg').append("<span style='color:red'>"+msg + "</span>");
	}

	// ////////////////////////////////////////////////////////////////////////

}
</pre>
</div>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2010/08/25/cross-browser-methods-to-read-local-files/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SimplePHP</title>
		<link>http://adastrasystems.com/2010/08/06/simplephp/</link>
		<comments>http://adastrasystems.com/2010/08/06/simplephp/#comments</comments>
		<pubDate>Fri, 06 Aug 2010 16:20:30 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://adastrasystems.com/?p=324</guid>
		<description><![CDATA[As promised, I&#8217;ve just released the &#8216;SimplePHP&#8217; code on github for all to use and play with. You can check it out at 
git@github.com:adastra/SimplePHP.git.
Currently, this mini-framework consists of the following classes;
Session Manager

This allows you to abstract Session management, particularly useful if you want to do secure your sessions by using a database or extend this [...]]]></description>
			<content:encoded><![CDATA[<p>As promised, I&#8217;ve just released the &#8216;SimplePHP&#8217; code on github for all to use and play with. You can check it out at </p>
<p><em>git@github.com:adastra/SimplePHP.git</em>.</p>
<p>Currently, this mini-framework consists of the following classes;</p>
<p><strong>Session Manager<br />
</strong></p>
<p>This allows you to abstract Session management, particularly useful if you want to do secure your sessions by using a database or extend this class to do even more interesting things!</p>
<p><strong>Usage:</strong></p>
<div style="width:750px; scroll:auto">
<pre name="code" class="php" >
// Initialize the session, and use php's standard session management
Session::init(false);

// ... or, initialize the session, and use the a database to store the session data
Session::init(true);

// If we're using a database, then you must have a table with the following fields;
//
// id varchar(32) {primary key}
// access int (10)
// data text

// Set a session variable
Session::set('session_variable', 5);

// Get a session variable
$val = Session::get('session_variable');

// Clear a session variable
Session::clear('session_variable');

// Check to see if a session variable exists
if (Session::exists('session_variable')){
Logger::debug("Session variable exists!");
}
</pre>
</div>
<p><strong>Database Manager</strong></p>
<p>This allows you to abstract database management, it is inspired by the wordpress database class but uses a static class to hold the database object rather than passing a database object around.</p>
<p><strong>Usage:</strong></p>
<div style="width:750px; scroll:auto">
<pre name="code" class="php" >
// Set your database credentials, I typically do this in a file called 'settings.php.sticky'
// as this is server-specific and I don't include this file in the repo. You can also
// just add the credentials into the DatabaseManager class and dispense with these defines.
define("database_user", "dbuser");
define("database_pass", "dbpass");
define("database_name", "dbname");
define("database_host", "localhost");
define("database_verbose", false); // If set to true, the database will log activity using the Logger

// Though you don't need to setup or explicitly create the connection (the class will check for connection and connect if
// needed when you call any of its methods.
DatabaseManager::connect();

// Prepare a sql statement
$sql = DatabaseManager::prepare("SELECT id FROM sometable WHERE textField = %s AND numericField = %d",  $string, $number );

// Get a single variable
$id = DatabaseManager::getResults($sql);

// Get a result returned in a associative array
$sql = DatabaseManager::prepare("SELECT * FROM sometable WHERE textField = %s AND numericField = %d",  $string, $number );
$data = DatabaseManager::getResults($sql);

// Do a insert, and get the inserted row id
$id = DatabaseManager::insert($sql);

// Do an update
DatabaseManager::update($sql);

// Do a generic sql query and return the result object
DatabaseManager::submitQuery($sql);

// You can manually close the connection
DatabaseManager::close();
</pre>
</div>
<p><strong>Logger</strong></p>
<p>The logger class makes php logging simple, and the output includes a stack track indicating the function/method/class and line number of the last 2 calls on the stack. Handy for tracking down bugs! You can also have the Logger echo the log to HTML, which gives a nice color coded display of the log to the browser.</p>
<p><strong>Usage:</strong></p>
<div style="width:750px; scroll:auto">
<pre name="code" class="php" >
// Tell the logger to catch system errors
Logger::catchSysErrors();

// To the logger to echo the log as HTML
Logger::echoLog();

// Set the logger level
Logger::setLevelDebug();
Logger::setLevelInfo();
Logger::setLevelWarning();
Logger::setLevelError();
Logger::setLevelFatal();

// Or set the level using the Logger constants
Logger::setLevel(Logger::$DEBUG);
Logger::setLevel(Logger::$INFO);
Logger::setLevel(Logger::$WARNING);
Logger::setLevel(Logger::$ERROR);
Logger::setLevel(Logger::$FATAL);

// Log a message
Logger::debug("some message");
Logger::warning("some message");
Logger::warn("some message");
Logger::error("some message");
Logger::info("some message");
Logger::fatal("some message");
</pre>
</div>
<p><strong>Comms Manager</strong></p>
<p>This class allows you to sanitize parameters sent from the user to the backend using Ajax or simple POST/GET methods. It also allows for the sending of JSON messages back to the server. The class can be initialized which simply detects if the users browser is IE6 or not, as this contains crappy support for gzip, and if so turns gzipping off. This will require  Chris Schuld (http://chrisschuld.com/) excellent browser detection class which is included as part of this repo, or you can get the latest at http://chrisschuld.com/projects/browser-php-detecting-a-users-browser-from-php.</p>
<p><strong>Usage:</strong></p>
<div style="width:750px; scroll:auto">
<pre name="code" class="php" >
// (Optional) detect if the browser is IE6, if so turn off gzipping of any data sent back to the user's browser.
// Or you can just set CommandHelper::$ZIP_MESSAGE yourself to turn gzipping on or off.
CommandHelper::init();

// Validation examples;

// To validate a $POST or $GET para called 'myParaName' that is required and you expect to be numeric call;
$para = CommandHelper::getPara('myParaName', true, CommandHelper::$PARA_TYPE_NUMERIC);

// To validate a $POST or $GET para called 'myParaName' that is required and you expect to be a string call;
$para = CommandHelper::getPara('myParaName', true, CommandHelper::$PARA_TYPE_STRING);

// To validate a $POST or $GET para called 'myParaName' that is NOT required and you expect to be a json encoded object call;
$para = CommandHelper::getPara('myParaName', false, CommandHelper::$PARA_TYPE_JSON);

// If validation fails, the class will send a JSON message in the form
// {"result":"fail","data":"Validation failure: Parameter not set, expecting 'cmd'"}

// An example of sending a message back to the user, from an php object, which is sent as JSON;

$data = array(4);

$data['stat1'] = DatabaseManager::getVar("SELECT COUNT(stat1) AS no FROM statTable");
$data['stat2'] = DatabaseManager::getVar("SELECT COUNT(stat2) AS no FROM statTable");
$data['stat3'] = DatabaseManager::getVar("SELECT COUNT(stat3) AS no FROM statTable");
$data['stat4'] = DatabaseManager::getVar("SELECT COUNT(stat4) AS no FROM statTable");

$msg['cmd'] = 'getStats';
$msg['result'] = 'ok';
$msg['data'] = $data;

CommandHelper::sendMessage($msg);

// Or you can send any string that you'd like
CommandHelper::sendTextMessage("Undefined command!");

// There are also pre-defined error messages that can be sent;

// Check that a user is logged in, and that they have access to this site
if (!SecurityUtils::isLoggedInForSite($site_id)){
CommandHelper::sendAuthorizationFailMessage("You are not authorized for this site!");
die();
}
</pre>
</div>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2010/08/06/simplephp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Opensource release&#8230;</title>
		<link>http://adastrasystems.com/2010/05/19/opensource-release/</link>
		<comments>http://adastrasystems.com/2010/05/19/opensource-release/#comments</comments>
		<pubDate>Wed, 19 May 2010 14:35:31 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://adastrasystems.com/?p=318</guid>
		<description><![CDATA[Well, after my last rant/blog post I decided that its time to release some of our software as open source. When we build a site, I tend to prefer using a lean html/css front end and use Ajax to interact with the server. This can make for some fresh, dynamic sites and keeps page load [...]]]></description>
			<content:encoded><![CDATA[<p>Well, after my last rant/blog post I decided that its time to release some of our software as open source. When we build a site, I tend to prefer using a lean html/css front end and use Ajax to interact with the server. This can make for some fresh, dynamic sites and keeps page load times to a minimum. On the backed we try to keep things light, as this is obviously where you&#8217;re going to hit scalability issues so the more work that can be done on the client side the better.</p>
<p>On the backed you only really need a handful of classes, a basic controller to handle incoming Ajax request which then process these request, which generally just require database reads or writes. Each controller is generally different for each project, but follows the same principles and requires the same, simple, supporting machinery; database manager, session manager, logging and security/validation.</p>
<p>These classes give you a minimal layer of abstraction, making it easy to change infrastructure without making any code changes. </p>
<p>You could say this constitutes a php framework, albeit a modest one. But, its not really, as these are just helper classes that merely extend the base php functionality.</p>
<p>In the coming weeks, I&#8217;ll release all of these classes under GPL for anyone to play with, and welcome feedback. </p>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2010/05/19/opensource-release/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Symfony and php frameworks</title>
		<link>http://adastrasystems.com/2010/05/19/symfony-and-php-frameworks/</link>
		<comments>http://adastrasystems.com/2010/05/19/symfony-and-php-frameworks/#comments</comments>
		<pubDate>Wed, 19 May 2010 14:10:46 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[Opinion]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://adastrasystems.com/?p=303</guid>
		<description><![CDATA[So, I&#8217;ve just spend the last week getting to grips with the php framework symfony. Now, I understand the perceived benefit of using a framework to help speed up your development and save you from re-inventing the wheel. 
I&#8217;ve been a developer for over a decade, and I&#8217;ve also been a manager too and now [...]]]></description>
			<content:encoded><![CDATA[<p>So, I&#8217;ve just spend the last week getting to grips with the php framework <a href='http://www.symfony-project.org/'>symfony</a>. Now, I understand the perceived benefit of using a framework to help speed up your development and save you from re-inventing the wheel. </p>
<p>I&#8217;ve been a developer for over a decade, and I&#8217;ve also been a manager too and now I run my own company. I always look for ways to save money by reducing the development overhead and leveraging pre-existing tech. But, I have <b>never</b> worked a project where a php framework has saved time or money. The possible exception is the wordpress framework, but that is because sites that are built from wordpress need a blog and a cms. That is a lot of overhead to build it out yourself, so it makes sense. But, it still takes longer to build out basic pages in wordpress then it would to hand code them in html/css with some ajax goodness. But, in the case of a simple site with NO cms and NO blog, then why the hell use a php framework? And wordpress is slooow. It takes some love to get a wordpress install running nice and fast.</p>
<p>I also build out server farms for my clients. So, I&#8217;m pretty clued up on how to build out fast sites on small budgets. Having 100% of a site rendered in php with lots of MySQL reads is definitely a bad start, I know there are plenty of ways to cache php and MySQL. Put a basic html/css site on a nginx server and watch it scream.</p>
<p>Anyway, back to symfony. Its just way too complex for what you need, its hard to maintain and not simple to upgrade. Ever been handed a symfony project from another developer? Yeah, its not all that nice. And multiple config files in multiple locations for a single website is also not a good selling point for a framework. </p>
<p>The number one rule in engineering is KEEP IT SIMPLE. Symfony violates that rule in so many ways, it adds complexity where complexity was not needed, and results in slower page loads. I like to keep it lean and mean. </p>
<p>I&#8217;ve spent so much of my career working with engineers who violate that number one rule on a daily basis, the often heard excuse is &#8220;<i>I&#8217;m building re-usable code</i>&#8221; or &#8220;<i>this will make it faster to build next time</i>&#8220;. Yeah, right. I met a highly successful VP at ITT, and he had a rule, &#8220;<i>Write once, throw away</i>&#8220;. This sounds crazy, but I believe him that its probably cheaper in the long run. Watch this video of a presentation by Ken Schwaber (the inventor of Agile project management), its interesting what he has say about the growth of infrastructure code and its effect on productivity.</p>
<p><br/></p>
<p><object width="640" height="505"><param name="movie" value="http://www.youtube.com/v/IyNPeTn8fpo&#038;hl=en_US&#038;fs=1&#038;color1=0x2b405b&#038;color2=0x6b8ab6"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/IyNPeTn8fpo&#038;hl=en_US&#038;fs=1&#038;color1=0x2b405b&#038;color2=0x6b8ab6" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="505"></embed></object></p>
<p><br/></p>
<p>So, what frameworks do I like for web development? Well, I love <a href='http://jquery.com/'>jQuery</a> for one. This massively simplifies your code, and with the wealth of plugins life is made easy. And, the syntax is easy to read for the next developer who works on your code. It follows the KIS principle. Symfony does not. </p>
<p>I&#8217;m sure lots of people disagree with me on this, this is obviously just my opinion based on my own personal experience. But, figured it was worth sharing.</p>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2010/05/19/symfony-and-php-frameworks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nginx &amp; Wordpress MU</title>
		<link>http://adastrasystems.com/2010/04/04/nginx-wordpress-mu/</link>
		<comments>http://adastrasystems.com/2010/04/04/nginx-wordpress-mu/#comments</comments>
		<pubDate>Mon, 05 Apr 2010 05:21:42 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[Products]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://adastrasystems.com/?p=239</guid>
		<description><![CDATA[We&#8217;ve been working on a huge, soon to announced project, that involves Wordpress MU. I&#8217;ve been working for months on this, and I&#8217;ve really got stuck into the deepest and darkest parts of Wordpress MU. I&#8217;ve got to say, I&#8217;m a fan of Wordpress. The code is nice to work with, its well documented (for [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve been working on a huge, soon to announced project, that involves Wordpress MU. I&#8217;ve been working for months on this, and I&#8217;ve really got stuck into the deepest and darkest parts of Wordpress MU. I&#8217;ve got to say, I&#8217;m a fan of Wordpress. The code is nice to work with, its well documented (for the most part) and it just works. As opposed to some of the other open source CMS and blog engines out there.</p>
<p>But, I just kept hitting performance bottle necks with it. Which, is to be expected when you&#8217;re serving heavy php pages with lots of MySQL reads. I went down the well traveled road of installing various caching plugins (WP Super Cache was my favorite), and they made a huge difference &#8211; but still, performance not quite where I felt it should be. This project is for a very high traffic site, so its really worth squeezing every last percentage of performance out of your code.</p>
<p>I was about to roll out some kind of reverse caching proxy, such as <a href='http://en.wikipedia.org/wiki/Squid_%28software%29'>Squid</a> or a server cluster when I started to debate changing out Apache for something lighter and faster. I&#8217;d heard good things about <a href='http://en.wikipedia.org/wiki/Lighttpd'>lighthttp</a> and <a href='http://wiki.nginx.org'>Nginx</a>. I was a little nervous, as this is a live production site, but the performance gains could be worth it. When I learned that wordpress.com uses Nginx, that sealed it for me. Nginx it is then!</p>
<p>So, after much trepidation, I pulled the trigger and started to rebuild the app on a new server running Nginx (a LEMP stack, as opposed to LAMP). It took me a long and arduous 2 days to get everything back up and running (on a dev server first, customers didn&#8217;t experience any downtime), but man, first impressions blew my mind. It is way faster, especially for serving images. I intend to write a detailed post about the setup, but for now here is a graph that shows a quick comparison. The site is monitored by Site24&#215;7, and the following graph shows the ping times for a given page. This page had very simple content, so I wasn&#8217;t expecting much. But, see for yourself. Its a big difference, note that the site was cached before and after the change it was not cached. Once I roll out caching on the new nginx server then it will be fair comparison, i.e. this graph makes it looks like nginx is slower then it really <i>could</i> be.</p>
<p><img src='http://adastrasystems.com/wp-content/uploads/2010/04/Screen-shot-2010-04-04-at-11.13.26-PM.png'/></p>
<p>Some quick lessons learned, the hard way. </p>
<p>PHP sessions did not work, I spent hours trying to get them to work under nginx but then just decided it was time to roll out a MySQL based session system, its more secure and portable and only took about 2 hours to get up and running on the live site. I&#8217;ll post a how-to article soon.</p>
<p>If images aren&#8217;t being served correctly it could be because FastCGI php cache&#8217;s directory needs to be owned by the same user that Nginx is running as. Obvious I know, but still, not so obvious at the time. Well, lots of things aren&#8217;t obvious at 2 in the morning!</p>
<p>I promise I&#8217;ll post more details shortly.</p>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2010/04/04/nginx-wordpress-mu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>eTasking &amp; Agile Development</title>
		<link>http://adastrasystems.com/2010/04/04/etasking-agile-development/</link>
		<comments>http://adastrasystems.com/2010/04/04/etasking-agile-development/#comments</comments>
		<pubDate>Mon, 05 Apr 2010 05:05:39 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[Products]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[etasking]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://adastrasystems.com/?p=277</guid>
		<description><![CDATA[
We&#8217;ve worked on a few projects over the years where we&#8217;ve adopted agile project planning practices. As a result, we&#8217;ve used a few of the tools that are out there, such as Rally, and just haven&#8217;t been that impressed (not to mention the cost!). It seems to me that the beauty of Agile is that [...]]]></description>
			<content:encoded><![CDATA[<p><img src='http://adastrasystems.com/wp-content/uploads/2010/04/Screen-shot-2010-04-05-at-4.31.39-PM.png' alt='eTasking, agile project planning made simple'/></p>
<p>We&#8217;ve worked on a few projects over the years where we&#8217;ve adopted agile project planning practices. As a result, we&#8217;ve used a few of the tools that are out there, such as Rally, and just haven&#8217;t been that impressed (not to mention the cost!). It seems to me that the beauty of Agile is that its simple, and I feel like the tools that go along with it should be too. I&#8217;m a big fan of 7 Signal&#8217;s Basecamp, and use it a lot. Its good for what it is, but its not a project planing tool (which is fair, as its not supposed to be).</p>
<p>When we run agile, most of the actual day-to-day planning activity focuses around a white board, with basically a bunch of post-it notes that we physically move around. So, why is it that none of these project planning tools for Agile seem to be as simple? In my experience, they seemed to get bogged down by trying to give us too many features.</p>
<p>So, at Ad Astra we developed our own tool, trying to keep it as simple as possible. And, finally, we&#8217;ve decided to release it into the wild as a free service with the hope that other people will find it useful. We&#8217;re going to open source the code, if we have any interest from the community to do so. So, if you&#8217;re interested, send us an email and we&#8217;ll get you hooked up with the code repo. Its Beta software, and a work in progress &#8211; when we have free time!</p>
<p>Check it out at <a href='http://www.etasking.com'>etasking.com</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2010/04/04/etasking-agile-development/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Electronic Arts&#8217; Spore Islands</title>
		<link>http://adastrasystems.com/2009/12/04/ea-spore/</link>
		<comments>http://adastrasystems.com/2009/12/04/ea-spore/#comments</comments>
		<pubDate>Fri, 04 Dec 2009 21:37:31 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[Games]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[facebook]]></category>

		<guid isPermaLink="false">http://adastrasystems.com/?p=230</guid>
		<description><![CDATA[
Its been out for a few months now, but we just wanted to announce the official release of Electronic Arts&#8217; latest game in the Spore series, Spore Islands for Facebook. 

We had the privilege of assisting Area/Code in its development. We were the lead php and AI developer for this game, it was a lot [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://adastrasystems.com/wp-content/uploads/2010/04/spore-islands.jpg" alt="" title="spore-islands" width="620" height="447" class="alignnone size-full wp-image-220" /></p>
<p>Its been out for a few months now, but we just wanted to announce the official release of Electronic Arts&#8217; latest game in the Spore series, Spore Islands for Facebook. </p>
<p><img src="http://adastrasystems.com/wp-content/uploads/2010/04/spore-islands-01.jpg" alt="" title="spore-islands-01" width="440" height="319" class="alignnone size-full wp-image-219" /></p>
<p>We had the privilege of assisting <a href='http://areacodeinc.com/'>Area/Code</a> in its development. We were the lead php and AI developer for this game, it was a lot of fun and a technically challenging project. We have a lot of experience building websites that support high traffic, but supporting a game as advanced as this with as much back-end logic as this required, was always going to be a challenge. But, we rose to the challenge and we&#8217;re extremely proud to have been a part of this great game series!</p>
<p>Try it out on Facebook:</p>
<p>http://apps.facebook.com/sporeislands/</p>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2009/12/04/ea-spore/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP Logger with stacktrace</title>
		<link>http://adastrasystems.com/2009/11/17/php-logger-with-stacktrace/</link>
		<comments>http://adastrasystems.com/2009/11/17/php-logger-with-stacktrace/#comments</comments>
		<pubDate>Wed, 18 Nov 2009 05:41:34 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[php]]></category>
		<category><![CDATA[logging]]></category>

		<guid isPermaLink="false">http://www.adastrasystems.com/?p=154</guid>
		<description><![CDATA[Logging in php isn&#8217;t fun, lets face it. There are quite a few php loggers out there, but we&#8217;ve built our own over the years and we felt why not share this with the community? So, why did we build our own? Well, firstly I personally like log files that are actually readable and secondly [...]]]></description>
			<content:encoded><![CDATA[<p>Logging in php isn&#8217;t fun, lets face it. There are quite a few php loggers out there, but we&#8217;ve built our own over the years and we felt why not share this with the community? So, why did we build our own? Well, firstly I personally like log files that are actually readable and secondly I really need a stack trace, I need to know not only what part of the code fell over, but what called that piece of code? And, then what called <i>that</i> piece of code. Sometimes I like to use the system log file, and just have a terminal open (yeah, we&#8217;re a Mac shop here!) and just watch the log using <code>tail -f php_errors.log</code>. But, sometimes I like to view the log in html for small test scripts. And, another thing is that sometimes I want to setup the logger to email me any fatal errors, this is very handy as sometimes these can go under the radar especially on really busy sites.</p>
<p>Our Logger class is static, which is great for speed and means there&#8217;s only ever one instantiation floating around. It also initializes itself, so you don&#8217;t need to worry.</p>
<div style="width:750px; scroll:auto">
<pre name="code" class="php" >

require_once("Logger.class.php");

// This tells the logger to echo html
Logger::echoLog();

// Tells the Logger to catch and log system errors
Logger::catchSysErrors();

// This tells the logger to email you fatal errors. The email will contain the error
// message as well as a dump of the POST, GET and SESSION variables
Logger::setupEmail($email, $projectName, $logLevel = self::$FATAL)  

// You can set the logging level (the default is debug)
// Level can be (in order);
// Logger::$DEBUG, Logger::$INFO, Logger::$ERROR, Logger::FATAL
// e.g., if you set the level to ERROR, only ERROR and FATAL messages would be outputted
// e.g., if you set the level to INFO, only INFO, ERROR &#038; FATAL messages would be outputted
Logger::setLevel(Logger::$DEBUG);

// To use, call any of these
Logger::fatal("Some error message");
Logger::error("Some error message");
Logger::debug("Some error message");
Logger::info("Some error message");

// You can also dump a variable, array or object's contents with
Logger::dump($variable);
</pre>
</div>
<p>Here is the Logger class (apologies that the indenting is a little screwy); </p>
<div style="width:750px; scroll:auto">
<pre name="code" class="php" >
/**
* Logging class, use for all trace commands
*
*/
class Logger {

	public static $DEBUG	 	= 0;
	public static $INFO 		= 1;
	public static $WARNING 		= 2;
	public static $ERROR	 	= 3;
	public static $FATAL	 	= 4;

	private static $debugLevel = 0;

	private static $echoLog = false;

	private static $email		= "dev@adastrasystems.com";
	private static $project     = "Adastra Project";
	private static $sendEmail   = true;
	private static $emailLevel  = 4;

	// //////////////////////////////////////////////////////////////////////////////////////

	/**
	* Class constructor
	*/
	public function __construct(){
		self::init();
	}

	// //////////////////////////////////////////////////////////////////////////////////////

	public static function echoLog(){
		self::$echoLog = true;
	}

    // //////////////////////////////////////////////////////////////////////////////////////

    /**
     * Let the Logger catch any system errors
     */
    public static function catchSysErrors(){
        set_error_handler("Logger::sysErrorHandler");
    }

	// //////////////////////////////////////////////////////////////////////////////////////

	/**
	*
	*/
	private static function trace($level, $msg){

		if ($level < self::$debugLevel){
			return;
		}

		$bt = debug_backtrace();

		$class = "";
		$function = "";

		// get class, function called by caller of caller of caller
		if (isset($bt[2]['class'])){
			$class = $bt[2]['class'];
			$function = "." . $bt[2]['function'];
		}

		// get file, line where call to caller of caller was made
		$file = $bt[1]['file'];
		$line = $bt[1]['line'];

		$file_name = basename($file);

		if (self::$echoLog){

			switch($level){
				case self::$DEBUG: 	$levMsg = "<span style='color:009900'>debug</span>"; break;
				case self::$INFO:  	$levMsg = "<span style='color:0000FF'>info</span>"; break;
				case self::$WARNING:$levMsg = "<span style='color:FF6633'>warning</span>"; break;
				case self::$ERROR: 	$levMsg = "<span style='color:FF0101'>error</span>"; break;
				case self::$FATAL: 	$levMsg = "<span style='color:FF0000'><b>fatal</b></span>"; break;
			}

			$msg = "[$levMsg <span style='color:#000099'>$class$function</span>] <b>$msg</b>";
			$msg .= "<span style='color: #444; font-style: italic;'> on line $line of $file_name</span>";
			if (isset($bt[2]['file'])){
				$fname = basename($bt[2]['file']);
				$msg .= "<span style='color: #889; font-style: italic;'>, called from line ".$bt[2]['line']." of $fname</span>";
			}
			$msg .= "\n";

			echo $msg;

			flush();

		}
		else {

			// Get IP address....

			// Translate level into text...
			$levMsg = "????";
			switch($level){
				case self::$DEBUG: 	$levMsg = "DEBUG"; break;
				case self::$INFO:  	$levMsg = "INFO"; break;
				case self::$WARNING:$levMsg = "WARNING"; break;
				case self::$ERROR: 	$levMsg = "ERROR"; break;
				case self::$FATAL: 	$levMsg = "FATAL"; break;
			}

			$msg = "[$levMsg] $class$function $msg";
			$msg .= " {on line $line of $file_name";
			if (isset($bt[2]['file'])){
				$fname = basename($bt[2]['file']);
				$msg .= ", called from line ".$bt[2]['line']." of $fname";
			}
			if (isset($bt[3]['file'])){
				$fname = basename($bt[3]['file']);
				$msg .= ", called from line ".$bt[3]['line']." of $fname";
			}
			$msg .= "}";

			error_log($msg);
		}

        if (self::$sendEmail){
			self::sendEmail($msg, $level);
        }		

	}

	// //////////////////////////////////////////////////////////////////////////////////////

	private static function sendEmail($msg, $level){

		if ($level < self::$emailLevel){
			return;
		}

		$headers = 'From: ' . self::$email;
		$message = $msg . "\n\nPOST VARIABLES \n\n" . print_r($_POST, true) . "\nGET VARIABLES\n\n" . print_r($_GET, true) . "\nSESSION VARIABLES\n\n" . print_r($_SESSION, true);
		mail(self::$email, self::$project, $message, $headers);
   	}

	// //////////////////////////////////////////////////////////////////////////////////////

    /**
     * Use to replace system error handler, if desired. Use Logger::catchSysErrors() to
     * activate
     */
    public static function sysErrorHandler($errno, $errstr, $errfile, $errline){

        switch ($errno) {
            case E_USER_ERROR:
                $levMsg = "SYS_ERROR";
                break;

            case E_USER_WARNING:
                $levMsg = "SYS_WARNING";
                break;

            case E_USER_NOTICE:
                $levMsg = "SYS_NOTICE";
                break;

            default:
                $levMsg = "SYS_UNKNOWN";
               break;
        }

        if (self::$echoLog){

			switch($errno){
				case E_USER_NOTICE:  $levMsg = "<span style='color:0000FF'>info</span>"; break;
				case E_USER_WARNING:$levMsg = "<span style='color:FF6633'>warning</span>"; break;
				case E_USER_ERROR: 	$levMsg = "<span style='color:FF0101'>error</span>"; break;
				default: $levMsg = "<span style='color:009900'>debug</span>"; break;
			}

			$msg = "[$levMsg <span style='color:#000099'>#$errno</span>] <b>$errstr</b>";
			$msg .= "<span style='color: #444; font-style: italic;'> on line $errline of ".basename($errfile)."</span>";
			$msg .= "\n";

			echo $msg;

			flush();

        }
        else {

            $msg = "[$levMsg] No: $errno Msg: $errstr";
            $msg .= " {on line $errline of ".basename($errfile)."}";

            error_log($msg);
        }

        /* Don't execute PHP internal error handler */
        return true;
    }

    // //////////////////////////////////////////////////////////////////////////////////////

	public static function setupEmail($email, $projectName, $logLevel = self::$FATAL) {
		self::$sendEmail = true;
		self::$emailLevel = $logLevel;
		self::$project = $projectName;
	}

    // //////////////////////////////////////////////////////////////////////////////////////

	public static function setLevel($newLevel) { $debugLevel = $newLevel; }

	public static function setLevelDebug() { self::$debugLevel = self::$DEBUG; }
	public static function setLevelInfo() { self::$debugLevel = self::$INFO; }
	public static function setLevelWarning() { self::$debugLevel = self::$WARNING; }
	public static function setLevelError() { self::$debugLevel = self::$ERROR; }
	public static function setLevelFatal() { self::$debugLevel = self::$FATAL; }

	// //////////////////////////////////////////////////////////////////////////////////////

	public static function dump($var) { self::debug(print_r($var, true)); }
	public static function debug($msg){ self::trace(self::$DEBUG, $msg);	}
	public static function warning($msg){ self::trace(self::$WARNING, $msg);	}
	public static function warn($msg){ self::trace(self::$WARNING, $msg);	}
	public static function error($msg){ self::trace(self::$ERROR, $msg);	}
	public static function info($msg){  self::trace(self::$INFO, $msg);	}
	public static function fatal($msg){ self::trace(self::$FATAL, $msg);	die(); }

	// //////////////////////////////////////////////////////////////////////////////////////
}
</pre>
</div>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2009/11/17/php-logger-with-stacktrace/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Open access to scientific papers</title>
		<link>http://adastrasystems.com/2009/10/09/open-access-to-scientific-papers/</link>
		<comments>http://adastrasystems.com/2009/10/09/open-access-to-scientific-papers/#comments</comments>
		<pubDate>Sat, 10 Oct 2009 01:19:54 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[Opinion]]></category>

		<guid isPermaLink="false">http://www.adastrasystems.com/?p=173</guid>
		<description><![CDATA[Ok, so this is more of a rant than anything &#8211; but I think its so wrong that the vast majority of scientific papers are not freely available online. Lets face it, the vast majority of research going on at Universities world-wide is paid for by governments. If that government is a democracy, then that [...]]]></description>
			<content:encoded><![CDATA[<p>Ok, so this is more of a rant than anything &#8211; but I think its so wrong that the vast majority of scientific papers are not freely available online. Lets face it, the vast majority of research going on at Universities world-wide is paid for by governments. If that government is a democracy, then that work was paid for by tax payers. So, why is it that when I try to find papers online I have to pay for them. Take for example www.sciencedirect.com, they charge $32 for <i>each</i> paper! And, you can&#8217;t preview the paper so you have no idea if its useful or relevant (the abstract really doesn&#8217;t cut it). Typically, if you want to survey a field of study you might need to reviews tens if not hundreds of papers. This is simply not tenable for a small company such as ours. The internet was perhaps the greatest technological achievement in history, its about time that scientific information flowed as freely as mp3&#8217;s, porn and youtube videos!</p>
<p>So, to the sciencedirect.com&#8217;s of the world &#8211; either open your electronic vaults to the world, or at the very least lower your prices to a sensible amount for the good of us all. Imagine the world where even the hobbyist and bedroom inventor can access the world&#8217;s combined scientific knowledge, <b>and</b> contribute to it. Perhaps, that is what they are afraid of!?</p>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2009/10/09/open-access-to-scientific-papers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Setting up Netbeans to develop on the Blackberry</title>
		<link>http://adastrasystems.com/2009/10/05/developing-for-the-blackberry-in-netbeans/</link>
		<comments>http://adastrasystems.com/2009/10/05/developing-for-the-blackberry-in-netbeans/#comments</comments>
		<pubDate>Tue, 06 Oct 2009 05:47:45 +0000</pubDate>
		<dc:creator>Mike Pritchard</dc:creator>
				<category><![CDATA[Mobile phone dev]]></category>
		<category><![CDATA[blackberry]]></category>
		<category><![CDATA[J2ME]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.adastrasystems.com/?p=57</guid>
		<description><![CDATA[We&#8217;ve been working lately on a BlackBerry application, currently targeted for the 8900 Curve. Its been over a year since I last did any work on the Blackberry, so I dusted of the JDE from RIM and started to setup a fresh install of Netbeans (6.7.1) to start cranking away, how hard can it be [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve been working lately on a BlackBerry application, currently targeted for the 8900 Curve. Its been over a year since I last did any work on the Blackberry, so I dusted of the JDE from RIM and started to setup a fresh install of Netbeans (6.7.1) to start cranking away, how hard can it be to get things setup? Right? So, several frustrating hours later and digging up some old projects, I finally figured it out. I thought I&#8217;d post how to do it here to save someone else the pain!</p>
<p>Ok, so I&#8217;m going to assume you already have installed Netbeans along with the Mobility pack.</p>
<p>The first step is to download the JDE that you need from <a href="http://na.blackberry.com/eng/developers/javaappdev/javadevenv.jsp">here</a>, the version you need will depend on your phone. For the 8900 its version 4.6.1. So, I hear you ask, how do I know which one? Grab your Blackberry and hit Options-&gt;About and you&#8217;ll see the version number, this corresponds to the JDE version you&#8217;ll need. My 8900 gives a a version number of 4.6.1.250, hence I used the version 4.6.1 of the JDE. Note that on the about page, it also gives a Platform version number, for me this is 4.2.0.113 &#8211; ignore this.</p>
<p>Then, once you&#8217;ve installed the JDE check out <a href="http://www.netbeans.org/kb/55/blackberry.html">this article</a> over at netbeans. Although this is old, this basically works. So, follow these steps but ignore the last step where it asks you to cut and paste some stuff into your build.xml file. This part doesn&#8217;t work, at least not for me. Specifically, it would build but it would not generate the alx and cod file that you&#8217;ll need to actually run the application!</p>
<p>So, this was the tricky part &#8211; well, kinda. Cut and paste the following into the bottom of you build.xml file, just above the <code></project></code> tag;</p>
<div style="width:700px; scroll:auto">
<pre name="code" class="html:nogutter:xml" >
<property name="rim.blackberry.home" location="C:\Program Files\Research In Motion\BlackBerry JDE 4.6.1"/>
<property name="rim.blackberry.emulator" value="8900"/>
<property name="rim.blackberry.jdwp.port" value="8000"/>

<target name="post-jar">
    <exec os="Windows NT Windows 95 Windows 98 Windows 2000 Windows XP" dir="${dist.root.dir}"
              executable="${rim.blackberry.home}/bin/rapc.exe" failonerror="true" resolveExecutable="true">
        <arg value="import=${rim.blackberry.home}/lib/net_rim_api.jar"/>
        <arg value="codename=${name}"/>
        <arg value="-midlet"/>
        <arg value="jad=${dist.jad}"/>
        <arg value="${dist.jar}"/>
    </exec>
    <copy file="${name}.alx" todir="${dist.root.dir}"/>
</target>

<target name="run" depends="init,jar">
    <copy todir="${rim.blackberry.home}/simulator" verbose="true">
        <fileset dir="${dist.root.dir}">
            <include name="**/${name}.*"/>
        </fileset>
    </copy>
    <exec os="Windows NT Windows 95 Windows 98 Windows 2000 Windows XP"
        dir="${rim.blackberry.home}/simulator"
        executable="${rim.blackberry.home}/simulator/${rim.blackberry.emulator}.bat"
        failonerror="true"
        resolveExecutable="true"/>
</target>

<target name="debug" depends="init,jar">
    <copy todir="${rim.blackberry.home}/simulator" verbose="true">
        <fileset dir="${dist.root.dir}">
            <include name="**/${name}.*"/>
         </fileset>
    </copy>
    <delete file="${preprocessed.dir}/.timestamp"/>
<parallel>
<property name="jpda.port" value="${rim.blackberry.jdwp.port}"/>
        <java jar="${rim.blackberry.home}/bin/JDWP.jar" fork="true" dir="${rim.blackberry.home}/bin">
            <jvmarg value="-Xmx128M"/>
        </java>

        <sequential>
            <sleep seconds="5"/>
            <antcall target="nbdebug"/>
        </sequential>
    </parallel>
</target>

<target name="post-clean">
    <echo>Post clean</echo>
    <delete>
        <fileset dir="${rim.blackberry.home}/simulator">
            <include name="**/${name}.*"/>
        </fileset>
    </delete>
</target>
</pre>
</div>
<p>Change the first line to point to the location of the JDE that you installed. The next step is to add a <i>alx</i> file, this is needed to generate the cod file. We can create one manually and place in the same directory as your build file. Its name will need to be the the name of your midlet. You can find this out, or set this, by looking at your project properties. Under Application Descriptor you should see a MIDLet-Name, this will be the name of you <i>alx</i> file (but give it an alx extension). Don&#8217;t worry, if you get the name or file location wrong, you&#8217;ll get an error message when you try to build the app, this will tell you the expected location and name that Netbeans was expecting, which I found useful as I got it wrong a couple of times myself <img src='http://adastrasystems.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>You&#8217;ll also need to change the target phone, see where it says <i>rim.blackberry.emulator</i>, for me the target was the 8900 phone, hence the 8900 for its value.</p>
<p>Once you create this file, paste the following into it (but replace MyApp and MyCompany with your app name and company name, its only really important to get the name of the .cod file correct) ;</p>
<div style="width:700px">
<pre name="code" class="html:nogutter:xml" >
<loader version="1.0">
    <application id="MyApp">
        <name >
        </name>

        <description >
        </description>

        <version >
        </version>

        <vendor >
            MyCompany
        </vendor>

        <copyright >
            Copyright (c) 2009 MyCompany
        </copyright>

        <fileset Java="1.6">
            <directory >
                MyCompany
            </directory>

            <files >
                MyApp.cod
            </files>
        </fileset>

    </application>

</loader>
</pre>
</div>
<p>And, that should be it (I hope!). Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://adastrasystems.com/2009/10/05/developing-for-the-blackberry-in-netbeans/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
