Can’t upload files in WordPress on XAMPP in Mac OS X?

by Pål Brattberg on March 24, 2010

I couldn’t, and I confess to trying a bunch of stupid things like chmod -R 777 or something stupid like that.

Here’s how folks:
sudo chown -R nobody:admin /Applications/XAMPP/xamppfiles/htdocs

Happy hacking!

Creating a REST-based API for PrestaShop, part 1

by Pål Brattberg on March 19, 2010

I’m currently helping a client with integrations between custom, in-house systems and their upcoming PrestaShop-based e-commerce site. In doing this job it’s clear they will need a two-way synchronization scheme for products/articles in PrestaShop (PS) (set stock levels, adjust base price, send orders, read order statuses, CRUD products etc).

Now, the default PS-way (from reading the forums and blog posts) seem to be either repetitively importing/exporting CSV-files, using WebHooks or going straight to the database. Neither of these options seem right for the task, so I started thinking on what would feel right.

What if PrestaShop exposed an API for handling products, categories, tags, orders etc? Wouldn’t that be nice? A simple, REST-based API would be even sweeter, but I’d settle for pretty much any variant of decent API. I’ve since used my Google-fu, but to no avail. Seems I cannot find examples of anyone doing something like this, despite the 50k registered community users in the forums and the reported 33k shops using PS.

Oh well, I’ll have to create my own API and deploy it as a configurable module for PrestaShop. This is the start of that journey.

Proposed API

I like clean, well-designed REST API’s, so the odds are low on this being my attempt at such an API. Here’s the proposed, minimal Article API:

URL POST GET PUT DELETE HEAD
/api/articles Create new Article List all Articles Replace all Articles with supplied set Delete all Articles List only Article ID’s and short name
/api/articles/ {id} Add new Article with defined ID Get Article Update Article Delete Article Get ID and short name of Article
/api/articles/ {id}/images Add new image to Article List all images for Article Replace all images with supplied set Delete all images for this Article List only image paths

This pattern is obviously extendable to tags, Manufacturers, Suppliers, Orders, Customers, Customer Groups, Stats etc.

Stay tuned for more on this as I get further!

Learning more

Here’s some great resources that helped me out in a big way, check them out!

Trek: Get Your API Right
Mailchimp’s nice API
Flickr’s really basic and nice API
Shopify has evolved their API in a great way
Wikipedia’s page on REST has some good content as well
HTTP-code reference in a non-painful way
Creating a REST API with PHP
Test-client for REST API (available for Mac as well)

Super Simple Quotes in WordPress

by Pål Brattberg on March 14, 2010

This is a long-overdue pointer to a puny WordPress-plugin I made a little while back for handling quotes very easily.

It’s called Super Simple Quotes, and I think it is! :)

Have a look and tell me what you think!

Find Java classes in the classpath

by Pål Brattberg on May 11, 2009

My brother Oskar needed to read some Java classes from disk, so I whipped up some code to show a possible way. Then I figured you might like it as well.

Here it is (and find the latest version at GitHub where you can submit your improved fork)

Ain’t java short and sweet? ;)


package com.palbrattberg.classpathtools;

import java.io.File;
import java.io.FilenameFilter;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Find classes in the classpath (reads JARs and classpath folders).
 *
 * @author Pål Brattberg, brattberg@gmail.com
 * @see http://gist.github.com/pal
 */
@SuppressWarnings("unchecked")
public class ClasspathInspector {
	static boolean DEBUG = false;

	public static List<Class> getAllKnownClasses() {
		List<Class> classFiles = new ArrayList<Class>();
		List<File> classLocations = getClassLocationsForCurrentClasspath();
		for (File file : classLocations) {
			classFiles.addAll(getClassesFromPath(file));
		}
		return classFiles;
	}

	public static List<Class> getMatchingClasses(Class interfaceOrSuperclass) {
		List<Class> matchingClasses = new ArrayList<Class>();
		List<Class> classes = getAllKnownClasses();
		log("checking %s classes", classes.size());
		for (Class clazz : classes) {
			if (interfaceOrSuperclass.isAssignableFrom(clazz)) {
				matchingClasses.add(clazz);
				log("class %s is assignable from %s", interfaceOrSuperclass, clazz);
			}
		}
		return matchingClasses;
	}

	public static List<Class> getMatchingClasses(String validPackagePrefix, Class interfaceOrSuperclass) {
		throw new IllegalStateException("Not yet implemented!");
	}

	public static List<Class> getMatchingClasses(String validPackagePrefix) {
		throw new IllegalStateException("Not yet implemented!");
	}

	private static Collection<? extends Class> getClassesFromPath(File path) {
		if (path.isDirectory()) {
			return getClassesFromDirectory(path);
		} else {
			return getClassesFromJarFile(path);
		}
	}

	private static String fromFileToClassName(final String fileName) {
		return fileName.substring(0, fileName.length() - 6).replaceAll("/|\\\\", "\\.");
	}

	private static List<Class> getClassesFromJarFile(File path) {
		List<Class> classes = new ArrayList<Class>();
		log("getClassesFromJarFile: Getting classes for %s", path);

		try {
			if (path.canRead()) {
				JarFile jar = new JarFile(path);
				Enumeration<JarEntry> en = jar.entries();
				while (en.hasMoreElements()) {
					JarEntry entry = en.nextElement();
					if (entry.getName().endsWith("class")) {
						String className = fromFileToClassName(entry.getName());
						log("\tgetClassesFromJarFile: found %s", className);
						loadClass(classes, className);
					}
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("Failed to read classes from jar file: " + path, e);
		}

		return classes;
	}

	private static List<Class> getClassesFromDirectory(File path) {
		List<Class> classes = new ArrayList<Class>();
		log("getClassesFromDirectory: Getting classes for " + path);

		// get jar files from top-level directory
		List<File> jarFiles = listFiles(path, new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(".jar");
			}
		}, false);
		for (File file : jarFiles) {
			classes.addAll(getClassesFromJarFile(file));
		}

		// get all class-files
		List<File> classFiles = listFiles(path, new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(".class");
			}
		}, true);

		// List<URL> urlList = new ArrayList<URL>();
		// List<String> classNameList = new ArrayList<String>();
		int substringBeginIndex = path.getAbsolutePath().length() + 1;
		for (File classfile : classFiles) {
			String className = classfile.getAbsolutePath().substring(substringBeginIndex);
			className = fromFileToClassName(className);
			log("Found class %s in path %s: ", className, path);
			loadClass(classes, className);
		}

		return classes;
	}

	private static List<File> listFiles(File directory, FilenameFilter filter, boolean recurse) {
		List<File> files = new ArrayList<File>();
		File[] entries = directory.listFiles();

		// Go over entries
		for (File entry : entries) {
			// If there is no filter or the filter accepts the
			// file / directory, add it to the list
			if (filter == null || filter.accept(directory, entry.getName())) {
				files.add(entry);
			}

			// If the file is a directory and the recurse flag
			// is set, recurse into the directory
			if (recurse && entry.isDirectory()) {
				files.addAll(listFiles(entry, filter, recurse));
			}
		}

		// Return collection of files
		return files;
	}

	public static List<File> getClassLocationsForCurrentClasspath() {
		List<File> urls = new ArrayList<File>();
		String javaClassPath = System.getProperty("java.class.path");
		if (javaClassPath != null) {
			for (String path : javaClassPath.split(File.pathSeparator)) {
				urls.add(new File(path));
			}
		}
		return urls;
	}

	// todo: this is only partial, probably
	public static URL normalize(URL url) throws MalformedURLException {
		String spec = url.getFile();

		// get url base - remove everything after ".jar!/??" , if exists
		final int i = spec.indexOf("!/");
		if (i != -1) {
			spec = spec.substring(0, spec.indexOf("!/"));
		}

		// uppercase windows drive
		url = new URL(url, spec);
		final String file = url.getFile();
		final int i1 = file.indexOf(':');
		if (i1 != -1) {
			String drive = file.substring(i1 - 1, 2).toUpperCase();
			url = new URL(url, file.substring(0, i1 - 1) + drive + file.substring(i1));
		}

		return url;
	}

	private static void log(String pattern, final Object... args) {
		if (DEBUG)
			System.out.printf(pattern + "\n", args);
	}

	private static void loadClass(List<Class> classes, String className) {
		try {
			Class claz = Class.forName(className, false, ClassLoader.getSystemClassLoader());
			classes.add(claz);
		} catch (ClassNotFoundException cnfe) {
			log("ClassNotFoundException: Could not load class %s: %s", className, cnfe);
		} catch (NoClassDefFoundError e) {
			log("NoClassDefFoundError: Could not load class %s: %s", className, e);
		}
	}

	public static void main(String[] args) {
		// find all classes in classpath
		List<Class> allClasses = ClasspathInspector.getAllKnownClasses();
		System.out.printf("There are %s classes available in the classpath\n", allClasses.size());

		// find all classes that implement/subclass an interface/superclass
		List<Class> serializableClasses = ClasspathInspector.getMatchingClasses(Serializable.class);
		for (Class clazz : serializableClasses) {
			System.out.printf("%s is Serializable\n", clazz);
		}
		System.out.printf("There are %s Serializable classes available in the classpath\n", serializableClasses.size());
	}
}

Gimme closures baby!

JUnit 4 @RunWith & Google App Engine problems

by Pål Brattberg on April 27, 2009

Quick heads-up if any of you are running the Google App Engine (GAE) for Java and doing test-driven development using JUnit 4+. I got a few problems from the DataNucleus enhancer in Eclipse.

Turns out that using the JUnit library supplied by Eclipse Ganymedes was no great idea. Using a standard junit 4+ jar worked much better as the supplied version seems to not contain the all-important org.junit.internal.runners.JUnit4ClassRunner class which I needed.

(I even managed to get an infinite loop from the enhancer, will see if I have time to boil it down to a simple, reproducible test case later)

Here’s a stacktrace for you googlers:


DataNucleus Enhancer (version 1.1.0) : Enhancement of classes

2009-apr-27 14:00:39 org.datanucleus.metadata.annotations.AnnotationManagerImpl getMetaDataForClass
VARNING: Class "bug.ShowBug" has annotations but there is no registered AnnotationReader. Please check your CLASSPATH and the annotations in the class for validity.

2009-apr-27 14:00:39 org.datanucleus.metadata.annotations.AnnotationManagerImpl getMetaDataForClass
VARNING: Class "bug.ShowBug" has annotations but there is no registered AnnotationReader. Please check your CLASSPATH and the annotations in the class for validity.

DataNucleus Enhancer completed with success for 0 classes. Timings : input=266 ms, enhance=0 ms, total=266 ms. Consult the log for full details
DataNucleus Enhancer completed and no classes were enhanced. Consult the log for full details

Cucumber, Maven and Java

by Pål Brattberg on December 15, 2008

Madaspeak has a nice writeup on how to use Cucumber from Maven.

I ran into some problems with downloaded gems being read-only and this caused troubles as files seem to be opened in read-write mode.

I get the following error:

C:\Documents and Settings\palbra\Desktop\cucumber_try>mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] Building cucumber_try
[INFO]    task-segment: [test]
[INFO]
[INFO] Preparing exec:java
[WARNING] Removing: java from forked lifecycle, to prevent recursive invocation.
[WARNING] Removing: java from forked lifecycle, to prevent recursive invocation.
[WARNING] Removing: java from forked lifecycle, to prevent recursive invocation.
[WARNING] Removing: java from forked lifecycle, to prevent recursive invocation.
[INFO] No goals needed for project - skipping
[INFO] [exec:java {execution: install-hoe-gem}]
JRuby limited openssl loaded. gem install jruby-openssl for full support.

http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL

ERROR:  While executing gem ... (Errno::EACCES)
Permission denied - Permission denied - C:/Documents and Settings/palbra/.jruby/lib/ruby/gems/1.8/gems/hoe-1.8.2/History.txt

Removing the read-only flag fixes the problem though.

Another problem I’ve bumped into was

'cmd' is not recognized as an internal or external command,
operable program or batch file.
rake aborted!
Command failed with status (1): ["C:/Documents and Settings/palbra/.jruby/b...]

(See full trace by running task with --trace)

 

I had to add C:\WINDOWS\system32 before the cmd call in C:\Documents and Settings\palbra\.jruby\bin\jruby.bat

Not so hard, but hopefully it helps someone out there.

Access Sourceforge.net Shell services using PuTTY on Windows

by Pål Brattberg on November 12, 2008

Shell services at SourceForge have been changing recently and it’s not entirely intuitive how to access your shell account (at least it wasn’t for me).

Here’s how to log in to your shell account for your project using the excellent PuTTY package in Windows.

  1. Download and install PuTTy
  2. Run plink shell.sourceforge.net create
  3. Enter your sourceforge username and projectname using the form: username,projectname
  4. Enter your sourceforge password
  5. Run putty
  6. Enter host name as shell.sourceforge.net
  7. Set Connection > SSH > Remote command to create
  8. Set Session > Saved Sessions to shell.sourceforge.net
  9. Click Save
  10. Double-click shell.sourceforge.net and enter username and projectname using the same  form again: username,projectname
  11. Enter your password
  12. Enjoy!
This will work for several projects, just change the project name in step 10.
To give SourceForge feedback on the new Shell changes see the SourceForge shell service survey.
The information above is pulled from the support request PuTTY accesses restricted shell – ID: 2175546

From Best to Good – Re-evaluate your practices again

by Pål Brattberg on June 5, 2008

When looking to improve your work environment or pushing to use a new method, it’s not uncommon to go looking for what might be considered Best Practice in that area. It’s as natural and common as it is problematic and dangerous.

First of all, the very notion of a Best Practice is that there is some way of performing a task or method that is superior to all other possible ways of performing that task or method. It is also suggested that the practice is superior in all aspects, meaning it delivers the most value and costs the least resources (if applicable).

So, what’s the problem with wanting to find, and ultimately putting to use, best practices? Wouldn’t it be a great thing if we could find more best practices and let all interested parties in the world share as many best practices as possible? In theory; yes. In practice; no thanks.

The problem with many things marketed as best practices is that it’s not. It is extraordinarily uncommon for a technique och method to be so absolutely, overwhelmingly better than all other ways in which you could perform a task that it could be declared a true and undisputed Best Practice. That’s not to say there aren’t any Best Practices, I’m believe there are at least a few lurking about in this great universe of ours, it’s just that they’re not so common as we would think.

Take the field I’m working in (and probably you as well, since you’re stuck reading this): IT. Software Development. What’s the best way to develop software? What’s the best way to handle source code? What’s the best way to make sure we stay on track, do the right thing and always deliver maximum business value for the least amount of resources while boosting the user and developer experience and maximizing shareholder profits? Please let me know the best practice for that! No really, please do!

The obvious answer is that IT is a way to complex field to have any large, powerful Best Practices that all can agree upon. If you don’t agree, tell me the best computer language the world agreed upon. Or the best web framework. The catch being here that different people and organizations have different best practices, which serves to make them anything but unequivocally best. Of course, there is no single best computer language or web framework that is best for all cases.

So, if best practices are super-rare, bordering on non-existant, what should we do when we want to know how people smarter than us have solved a recurring problem we have just faced? We should do like other industries, look for Good Practices. In many industries there are several agreed upon Good Practices that form the basis for making sure we do not all spend obscene amount of times reinventing an inferior wheel.

Focusing on Good Practices instead of the mythical Best Practices has many benefits: A practice that has proven to work in a good fashion for a team doesn not have the burden of proof as to wether it is also the absolute best practice concievable meaning there should be more good practices to choose from. As there are many, competing good practices you are encouraged to constantly inspect, adapt and revise any decisions to go with any particular practice, which is great since that causes you to pay ongoing attention to how you work and how your practices are working for your particular environment and people. Focusing on good practices also removes the stigma of trying on a Best Practice and finding it doesn’t deliver for your needs. If it truly was best, you must be doing something wrong. If the practice you tried was merely good, you can just keep looking or adapting the practice.

To round up: stop looking for Best Practices and embrace imperfection and ongoing improvement by going for Good Practices. I’ll follow this up by posting some good practices I’ve used in projects over the following months. Stay tuned!

Initial Derby-support in DbFit

by Pål Brattberg on May 30, 2008

DbFit does heavy lifting!This just in: you can now run FitNesse tests with DbFit using Derby/JavaDB as your database of choice.

Have a go and download it from Subversion here: https://dbfit.svn.sourceforge.net/svnroot/dbfit/dbfit/dist/dbfit-20080530.jar

It’s not terribly complete which you can tell from the limited set of acceptance tests available, but it works for the basic cases.

Notable omission: Stored Procedures. My current project doesn’t need it so it’s not yet implemented.

If you are missing something, let me know and I will add it. (To get it fixed fast, also supply a failing DbFit-test case.

JtestR 0.2 out now

by Pål Brattberg on April 17, 2008

Ola Bini+friends’ cool tool, JtestR has just been released in a 0.2 release that adds some nice features and fixes a few bugs as well.

JtestR, for those of you who haven’t heard about it yet, is a tool to enable testing of java code using alot of the cool ruby tools available for testing such as rspec, test/unit and more.

As if that wasn’t enough, it’s also integrated nicely with Ant and Maven and can also run your existing junit and testng tests.

Any day now I’ll try to find some time to put together a post on my experiences with using ruby to test java code. But for now, go take a look at jtestr!