MemCached CustomSerializingTranscoder Serialized Object

Was getting this error when I was trying to set a custom object:
memCachedClient.set(“customerObj”, 3600, new Customer(“John”, “Doe”));

2014-03-01 15:47:21.339 WARN net.spy.memcached.transcoders.SerializingTranscoder:  Caught CNFE decoding 150 bytes of data
2014-03-01T15:47:21.340137+00:00 app[web.1]: java.lang.ClassNotFoundException: com.example.model.Customer
2014-03-01T15:47:21.340436+00:00 app[web.1]: 	at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
2014-03-01T15:47:21.340436+00:00 app[web.1]: 	at java.security.AccessController.doPrivileged(Native Method)
2014-03-01T15:47:21.340436+00:00 app[web.1]: 	at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
2014-03-01T15:47:21.340436+00:00 app[web.1]: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
2014-03-01T15:47:21.340436+00:00 app[web.1]: 	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
2014-03-01T15:47:21.340436+00:00 app[web.1]: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
2014-03-01T15:47:21.340436+00:00 app[web.1]: 	at java.lang.Class.forName0(Native Method)
2014-03-01T15:47:21.340436+00:00 app[web.1]: 	at java.lang.Class.forName(Class.java:266)

This occurs because the memcached client is loaded using a classloader and the serialized object class is loaded using another classloader. To fix this you must pass to the memcahed client connector factory a custom transcoder.


 MemcachedClient mc = new MemcachedClient(
   new ConnectionFactoryBuilder()
   .setTranscoder(new CustomSerializingTranscoder()) //Add this line
   .setProtocol(ConnectionFactoryBuilder.Protocol.BINARY)
   .setAuthDescriptor(ad).build(),
package com.example.cache;

import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;

import net.spy.memcached.transcoders.SerializingTranscoder;

public class CustomSerializingTranscoder extends SerializingTranscoder{

    @Override
    protected Object deserialize(byte[] bytes) {
        final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
        ObjectInputStream in = null;
        try {
            ByteArrayInputStream bs = new ByteArrayInputStream(bytes);
            in = new ObjectInputStream(bs) {
                @Override
                protected  Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
                    try {
                        return currentClassLoader.loadClass(objectStreamClass.getName());
                    } catch (Exception e) {
                        return super.resolveClass(objectStreamClass);
                    }
                }
            };
            return in.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            closeStream(in);
        }
    }

    private static void closeStream(Closeable c) {
        if (c != null) {
            try {
                c.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Heroku MemCached Junit Test

Heroku Memcached Cloud Junit Test

package com.example.cache;

import static org.junit.Assert.*;

import java.io.IOException;
import java.math.BigInteger;

import net.spy.memcached.AddrUtil;
import net.spy.memcached.ConnectionFactoryBuilder;
import net.spy.memcached.MemcachedClient;
import net.spy.memcached.auth.AuthDescriptor;
import net.spy.memcached.auth.PlainCallbackHandler;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.example.model.Customer;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CacheCustomerServiceTest {
	
	private static final String USERNAME="######";
	private static final String PASSWORD="######";
	private static final String HOST="######";
	
	@Configuration
	static class ContextConfiguration{
		
		@Bean
		public MemcachedClient memcachedConfig() {
		try{
			 AuthDescriptor ad = new AuthDescriptor(new String[] { "PLAIN" },
				        new PlainCallbackHandler(USERNAME, PASSWORD));

			MemcachedClient mc = new MemcachedClient(
					new ConnectionFactoryBuilder()
				                  .setProtocol(ConnectionFactoryBuilder.Protocol.BINARY)
				                  .setAuthDescriptor(ad).build(),
		    AddrUtil.getAddresses(HOST));
			return mc;
		} catch (IOException ex) {}
			return null;
		}
	}
	
	@Autowired MemcachedClient memcachedConfig;
	
	@Test
	public void testMemCachedSet() {
		memcachedConfig.set("customerKey", 3600, new Customer(new BigInteger("1"), "John", "Doe"));
		Object value = memcachedConfig.get("customerKey");
		System.out.println(value);
	}
}

Setup Memcached on Heroku

Add memcachedcloud to your project:
heroku addons:add memcachedcloud

1. Setup Memcached Configuration
2. Setup Memcached Service

1. Setup Memcached Configuration

package com.example.cache;

import java.io.IOException;

import net.spy.memcached.AddrUtil;
import net.spy.memcached.ConnectionFactoryBuilder;
import net.spy.memcached.MemcachedClient;
import net.spy.memcached.auth.AuthDescriptor;
import net.spy.memcached.auth.PlainCallbackHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CacheConfiguration {
	
	@Bean
	public MemcachedClient memcachedCloudConfiguration() {
		try {
		    AuthDescriptor ad = new AuthDescriptor(new String[] { "PLAIN" },
		        new PlainCallbackHandler(System.getenv("MEMCACHEDCLOUD_USERNAME"), System.getenv("MEMCACHEDCLOUD_PASSWORD")));

		    MemcachedClient mc = new MemcachedClient(
		              new ConnectionFactoryBuilder()
		                  .setProtocol(ConnectionFactoryBuilder.Protocol.BINARY)
		                  .setAuthDescriptor(ad).build(),
		          AddrUtil.getAddresses(System.getenv("MEMCACHEDCLOUD_SERVERS")));
		    return mc;
		} catch (IOException ex) {
		    // the Memcached client could not be initialized.
		}
		return null;
	}
}

2. Setup Memcached Service

package com.example.cache;

import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import net.spy.memcached.MemcachedClient;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;

import com.example.model.Customer;
import com.example.service.CustomerService;

@Service
@Import({CacheConfiguration.class})
public class CacheCustomerService implements CustomerService{

	@Autowired MemcachedClient memCachedClient;
	
	@Override
	public Customer getCustomerById(BigInteger id) {
		return (Customer) memCachedClient.get(String.valueOf(id));
	}

	@Override
	public void createCustomer(String fn, String ln) {
		memCachedClient.set("customerKey", 3600, new Customer(fn, ln));
	}

	@Override
	public void deleteCustomer(BigInteger id) {
		memCachedClient.delete(String.valueOf(id));
	}

	@Override
	public void createCustomer(Customer newCustomer) {
		memCachedClient.set("customerKey", 3600, newCustomer);
	}	
}