diff --git a/java/pom.xml b/java/pom.xml
index 44806d51..70da6a6f 100755
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -29,6 +29,12 @@
       <version>4.8.1</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>javassist</groupId>
+      <artifactId>javassist</artifactId>
+      <version>3.12.1.GA</version>
+      <scope>compile</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -101,6 +107,13 @@
       <name>MessagePack Maven2 Repository</name>
       <url>http://msgpack.org/maven2</url>
     </repository>
+    <repository>
+      <id>repository.jboss.org</id>
+      <url>https://repository.jboss.org/nexus/content/groups/public/</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
   </repositories>
 
   <distributionManagement>
diff --git a/java/src/main/java/org/msgpack/util/annotation/MessagePackOptional.java b/java/src/main/java/org/msgpack/util/annotation/MessagePackOptional.java
new file mode 100644
index 00000000..a565292d
--- /dev/null
+++ b/java/src/main/java/org/msgpack/util/annotation/MessagePackOptional.java
@@ -0,0 +1,12 @@
+package org.msgpack.util.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.CLASS)
+public @interface MessagePackOptional {
+	int value() default -1;
+}
\ No newline at end of file
diff --git a/java/src/main/java/org/msgpack/util/annotation/MessagePackRequired.java b/java/src/main/java/org/msgpack/util/annotation/MessagePackRequired.java
new file mode 100644
index 00000000..03e50cf3
--- /dev/null
+++ b/java/src/main/java/org/msgpack/util/annotation/MessagePackRequired.java
@@ -0,0 +1,12 @@
+package org.msgpack.util.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.CLASS)
+public @interface MessagePackRequired {
+	int value() default -1;
+}
\ No newline at end of file
diff --git a/java/src/main/java/org/msgpack/util/annotation/MessagePackUnpackable.java b/java/src/main/java/org/msgpack/util/annotation/MessagePackUnpackable.java
new file mode 100644
index 00000000..77f6e6de
--- /dev/null
+++ b/java/src/main/java/org/msgpack/util/annotation/MessagePackUnpackable.java
@@ -0,0 +1,11 @@
+package org.msgpack.util.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MessagePackUnpackable {
+}
diff --git a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java
new file mode 100644
index 00000000..52384448
--- /dev/null
+++ b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java
@@ -0,0 +1,794 @@
+package org.msgpack.util.annotation;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtMethod;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.NotFoundException;
+
+import org.msgpack.MessageConvertable;
+import org.msgpack.MessagePackable;
+import org.msgpack.MessageTypeException;
+import org.msgpack.MessageUnpackable;
+import org.msgpack.Packer;
+import org.msgpack.Unpacker;
+
+public class PackUnpackUtil {
+	static class Constants {
+		static final String POSTFIX_TYPE_NAME_ENHANCER = "_$$_Enhanced";
+
+		static final String KEYWORD_MODIFIER_PUBLIC = "public";
+
+		static final String KEYWORD_FOR = "for";
+
+		static final String KEYWORD_THROWS = "throws";
+
+		static final String KEYWORD_NEW = "new";
+
+		static final String TYPE_NAME_VOID = void.class.getName();
+
+		static final String TYPE_NAME_OBJECT = Object.class.getName();
+
+		static final String TYPE_NAME_IOEXCEPTION = IOException.class.getName();
+
+		static final String TYPE_NAME_PACKER = Packer.class.getName();
+
+		static final String TYPE_NAME_UNPACKER = Unpacker.class.getName();
+
+		static final String TYPE_NAME_MSGPACKABLE = MessagePackable.class
+				.getName();
+
+		static final String TYPE_NAME_MSGUNPACKABLE = MessageUnpackable.class
+				.getName();
+
+		static final String TYPE_NAME_MSGCONVERTABLE = MessageConvertable.class
+				.getName();
+
+		static final String TYPE_NAME_MSGTYPEEXCEPTION = MessageTypeException.class
+				.getName();
+
+		static final String TYPE_NAME_MSGPACKUNPACKABLE = MessagePackUnpackable.class
+				.getName();
+
+		static final String TYPE_NAME_MSGPACKOPTIONAL = MessagePackOptional.class
+				.getName();
+
+		static final String TYPE_NAME_MSGPACKOREQUIRED = MessagePackRequired.class
+				.getName();
+
+		static final String CHAR_NAME_SPACE = " ";
+
+		static final String CHAR_NAME_COMMA = ",";
+
+		static final String CHAR_NAME_EQUAL = "=";
+
+		static final String CHAR_NAME_PLUS = "+";
+
+		static final String CHAR_NAME_LESSTHAN = "<";
+
+		static final String CHAR_NAME_RIGHT_PARENTHESIS = ")";
+
+		static final String CHAR_NAME_LEFT_PARENTHESIS = "(";
+
+		static final String CHAR_NAME_RIGHT_CURLY_BRACHET = "}";
+
+		static final String CHAR_NAME_LEFT_CURLY_BRACHET = "{";
+
+		static final String CHAR_NAME_DOT = ".";
+
+		static final String CHAR_NAME_SEMICOLON = ";";
+
+		static final String VARIABLE_NAME_PK = "pk";
+
+		static final String VARIABLE_NAME_OBJ = "obj";
+
+		static final String VARIABLE_NAME_SIZE = "len";
+
+		static final String VARIABLE_NAME_I = "i";
+
+		static final String METHOD_NAME_VALUEOF = "valueOf";
+
+		static final String METHOD_NAME_ADD = "add";
+
+		static final String METHOD_NAME_PUT = "put";
+
+		static final String METHOD_NAME_MSGPACK = "messagePack";
+
+		static final String METHOD_NAME_MSGUNPACK = "messageUnpack";
+
+		static final String METHOD_NAME_MSGCONVERT = "messageConvert";
+
+		static final String METHOD_NAME_PACK = "pack";
+
+		static final String METHOD_NAME_PACKARRAY = "packArray";
+
+		static final String METHOD_NAME_UNPACK = "unpack";
+
+		static final String METHOD_NAME_UNPACKBOOLEAN = "unpackBoolean";
+
+		static final String METHOD_NAME_UNPACKBYTE = "unpackByte";
+
+		static final String METHOD_NAME_UNPACKDOUBLE = "unpackDouble";
+
+		static final String METHOD_NAME_UNPACKFLOAT = "unpackFloat";
+
+		static final String METHOD_NAME_UNPACKINT = "unpackInt";
+
+		static final String METHOD_NAME_UNPACKLONG = "unpackLong";
+
+		static final String METHOD_NAME_UNPACKSHORT = "unpackShort";
+
+		static final String METHOD_NAME_UNPACKSTRING = "unpackString";
+
+		static final String METHOD_NAME_UNPACKBIGINTEGER = "unpackBigInteger";
+
+		static final String METHOD_NAME_UNPACKOBJECT = "unpackObject";
+
+		static final String METHOD_NAME_UNPACKBYTEARRAY = "unpackByteArray";
+
+		static final String METHOD_NAME_UNPACKARRAY = "unpackArray";
+
+		static final String METHOD_NAME_UNPACKMAP = "unpackMap";
+	}
+
+	public static class Enhancer {
+
+		private ConcurrentHashMap<String, Class<?>> classCache;
+
+		private ClassPool pool;
+
+		protected Enhancer() {
+			classCache = new ConcurrentHashMap<String, Class<?>>();
+			pool = ClassPool.getDefault();
+		}
+
+		protected Class<?> getCache(String origName) {
+			return classCache.get(origName);
+		}
+
+		protected void setCache(String origName, Class<?> enhClass) {
+			classCache.putIfAbsent(origName, enhClass);
+		}
+
+		protected Class<?> generate(Class<?> origClass)
+				throws NotFoundException, CannotCompileException {
+			String origName = origClass.getName();
+			String enhName = origName + Constants.POSTFIX_TYPE_NAME_ENHANCER;
+			CtClass origCtClass = pool.get(origName);
+			checkClassValidation(origClass);
+			checkDefaultConstructorValidation(origClass);
+			CtClass enhCtClass = pool.makeClass(enhName);
+			setSuperclass(enhCtClass, origCtClass);
+			setInterfaces(enhCtClass);
+			addConstructor(enhCtClass);
+			Field[] fields = getDeclaredFields(origClass);
+			addMessagePackMethod(enhCtClass, origCtClass, fields);
+			addMessageUnpackMethod(enhCtClass, origCtClass, fields);
+			addMessageConvertMethod(enhCtClass, origCtClass, fields);
+			return createClass(enhCtClass);
+		}
+
+		private void checkClassValidation(Class<?> origClass) {
+			// not public, abstract, final
+			int mod = origClass.getModifiers();
+			if ((!(Modifier.isPublic(mod) || Modifier.isProtected(mod)))
+					|| Modifier.isAbstract(mod) || Modifier.isFinal(mod)) {
+				throwClassValidationException(origClass);
+			}
+			// interface, enum
+			if (origClass.isInterface() || origClass.isEnum()) {
+				throwClassValidationException(origClass);
+			}
+			// annotation
+			checkPackUnpackAnnotation(origClass);
+		}
+
+		private static void throwClassValidationException(Class<?> origClass) {
+			throw new PackUnpackUtilException(
+					"it must be a public class and have @"
+							+ MessagePackUnpackable.class.getName() + ": "
+							+ origClass.getName());
+		}
+
+		private void checkPackUnpackAnnotation(Class<?> origClass) {
+			Annotation anno = origClass
+					.getAnnotation(MessagePackUnpackable.class);
+			if (anno == null) {
+				throwClassValidationException(origClass);
+			}
+		}
+
+		private void checkDefaultConstructorValidation(Class<?> origClass) {
+			Constructor<?> cons = null;
+			try {
+				cons = origClass.getDeclaredConstructor(new Class[0]);
+			} catch (Exception e1) {
+				throwConstructoValidationException(origClass);
+			}
+
+			int mod = cons.getModifiers();
+			if (!(Modifier.isPublic(mod) || Modifier.isProtected(mod))) {
+				throwConstructoValidationException(origClass);
+			}
+		}
+
+		private static void throwConstructoValidationException(
+				Class<?> origClass) {
+			throw new PackUnpackUtilException(
+					"it must have a public zero-argument constructor: "
+							+ origClass.getName());
+		}
+
+		private void setSuperclass(CtClass enhCtClass, CtClass origCtClass)
+				throws CannotCompileException {
+			enhCtClass.setSuperclass(origCtClass);
+		}
+
+		private void setInterfaces(CtClass enhCtClass) throws NotFoundException {
+			CtClass pacCtClass = pool.get(Constants.TYPE_NAME_MSGPACKABLE);
+			enhCtClass.addInterface(pacCtClass);
+			CtClass unpacCtClass = pool.get(Constants.TYPE_NAME_MSGUNPACKABLE);
+			enhCtClass.addInterface(unpacCtClass);
+			CtClass convCtClass = pool.get(Constants.TYPE_NAME_MSGCONVERTABLE);
+			enhCtClass.addInterface(convCtClass);
+		}
+
+		private void addConstructor(CtClass enhCtClass)
+				throws CannotCompileException {
+			CtConstructor newCtCons = CtNewConstructor
+					.defaultConstructor(enhCtClass);
+			enhCtClass.addConstructor(newCtCons);
+		}
+
+		private Field[] getDeclaredFields(Class<?> origClass) {
+			ArrayList<Field> allFields = new ArrayList<Field>();
+			Class<?> nextClass = origClass;
+			while (nextClass != Object.class) {
+				Field[] fields = nextClass.getDeclaredFields();
+				for (Field field : fields) {
+					try {
+						checkFieldValidation(field, allFields);
+						allFields.add(field);
+					} catch (PackUnpackUtilException e) { // ignore
+					}
+				}
+				nextClass = nextClass.getSuperclass();
+			}
+			return allFields.toArray(new Field[0]);
+		}
+
+		private void checkFieldValidation(Field field, List<Field> fields) {
+			// check modifiers (public or protected)
+			int mod = field.getModifiers();
+			if ((!(Modifier.isPublic(mod) || Modifier.isProtected(mod)))
+					|| Modifier.isStatic(mod) || Modifier.isFinal(mod)
+					|| Modifier.isTransient(mod) || field.isSynthetic()) {
+				throwFieldValidationException(field);
+			}
+			// check same name
+			for (Field f : fields) {
+				if (f.getName().equals(field.getName())) {
+					throwFieldValidationException(field);
+				}
+			}
+		}
+
+		private static void throwFieldValidationException(Field field) {
+			throw new PackUnpackUtilException("it must be a public field: "
+					+ field.getName());
+		}
+
+		private void addMessagePackMethod(CtClass enhCtClass,
+				CtClass origCtClass, Field[] fields)
+				throws CannotCompileException, NotFoundException {
+			StringBuilder sb = new StringBuilder();
+			sb.append(Constants.KEYWORD_MODIFIER_PUBLIC);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.TYPE_NAME_VOID);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.METHOD_NAME_MSGPACK);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.TYPE_NAME_PACKER);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.KEYWORD_THROWS);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.TYPE_NAME_IOEXCEPTION);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_DOT);
+			sb.append(Constants.METHOD_NAME_PACKARRAY);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(fields.length);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			for (Field field : fields) {
+				insertCodeOfMessagePack(sb, field);
+			}
+			sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET);
+			//System.out.println("messagePack method: " + sb.toString());
+			CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhCtClass);
+			enhCtClass.addMethod(newCtMethod);
+		}
+
+		private void insertCodeOfMessagePack(StringBuilder sb, Field field) {
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_DOT);
+			sb.append(Constants.METHOD_NAME_PACK);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(field.getName());
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+		}
+
+		private void addMessageUnpackMethod(CtClass enhCtClass,
+				CtClass origCtClass, Field[] fields)
+				throws CannotCompileException, NotFoundException {
+			StringBuilder sb = new StringBuilder();
+			sb.append(Constants.KEYWORD_MODIFIER_PUBLIC);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.TYPE_NAME_VOID);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.METHOD_NAME_MSGUNPACK);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.TYPE_NAME_UNPACKER);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.KEYWORD_THROWS);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.TYPE_NAME_MSGTYPEEXCEPTION);
+			sb.append(Constants.CHAR_NAME_COMMA);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.TYPE_NAME_IOEXCEPTION);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_DOT);
+			sb.append(Constants.METHOD_NAME_UNPACKARRAY);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			for (Field field : fields) {
+				insertCodeOfMessageUnpack(sb, field, field.getType());
+			}
+			sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET);
+			//System.out.println("messageUnpack method: " + sb.toString());
+			CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhCtClass);
+			enhCtClass.addMethod(newCtMethod);
+		}
+
+		private void insertCodeOfMessageUnpack(StringBuilder sb, Field field,
+				Class<?> type) throws NotFoundException {
+			if (type.isPrimitive()) {
+				// primitive type
+				insertCodeOfMessageUnpackForPrimitiveTypes(sb, field, type);
+			} else if (type.equals(Boolean.class) || // Boolean
+					type.equals(Byte.class) || // Byte
+					type.equals(Double.class) || // Double
+					type.equals(Float.class) || // Float
+					type.equals(Integer.class) || // Integer
+					type.equals(Long.class) || // Long
+					type.equals(Short.class)) { // Short
+				// reference type (wrapper type)
+				insertCodeOfMessageUnpackForWrapperTypes(sb, field, type);
+			} else if (type.equals(BigInteger.class) || // BigInteger
+					type.equals(String.class) || // String
+					type.equals(byte[].class)) { // byte[]
+				// reference type (other type)
+				insertCodeOfMessageUnpackForPrimitiveTypes(sb, field, type);
+			} else if (List.class.isAssignableFrom(type)) {
+				// List
+				insertCodeOfMessageUnpackForListType(sb, field, type);
+			} else if (Map.class.isAssignableFrom(type)) {
+				// Map
+				insertCodeOfMessageUnpackForMapType(sb, field, type);
+			} else if (MessageUnpackable.class.isAssignableFrom(type)
+					|| (getCache(type.getName()) != null)) {
+				// MessageUnpackable
+				insertCodeOfMessageUnpackForMsgUnpackableType(sb, field, type);
+			} else {
+				throw new NotFoundException("unknown type:  " + type.getName());
+			}
+		}
+
+		private void insertCodeOfMessageUnpackForPrimitiveTypes(
+				StringBuilder sb, Field field, Class<?> type)
+				throws NotFoundException {
+			// insert a right variable
+			if (field != null) {
+				sb.append(field.getName());
+				sb.append(Constants.CHAR_NAME_SPACE);
+				sb.append(Constants.CHAR_NAME_EQUAL);
+				sb.append(Constants.CHAR_NAME_SPACE);
+			}
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_DOT);
+			// insert unpack method
+			if (type.equals(boolean.class)) { // boolean
+				sb.append(Constants.METHOD_NAME_UNPACKBOOLEAN);
+			} else if (type.equals(byte.class)) { // byte
+				sb.append(Constants.METHOD_NAME_UNPACKBYTE);
+			} else if (type.equals(double.class)) { // double
+				sb.append(Constants.METHOD_NAME_UNPACKDOUBLE);
+			} else if (type.equals(float.class)) { // float
+				sb.append(Constants.METHOD_NAME_UNPACKFLOAT);
+			} else if (type.equals(int.class)) { // int
+				sb.append(Constants.METHOD_NAME_UNPACKINT);
+			} else if (type.equals(long.class)) { // long
+				sb.append(Constants.METHOD_NAME_UNPACKLONG);
+			} else if (type.equals(short.class)) { // short
+				sb.append(Constants.METHOD_NAME_UNPACKSHORT);
+			} else { // reference type
+				if (type.equals(BigInteger.class)) { // BigInteger
+					sb.append(Constants.METHOD_NAME_UNPACKBIGINTEGER);
+				} else if (type.equals(String.class)) { // String
+					sb.append(Constants.METHOD_NAME_UNPACKSTRING);
+				} else if (type.equals(byte[].class)) { // byte[]
+					sb.append(Constants.METHOD_NAME_UNPACKBYTEARRAY);
+				} else {
+					throw new NotFoundException("unknown type:  "
+							+ type.getName());
+				}
+			}
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			if (field != null) {
+				sb.append(Constants.CHAR_NAME_SEMICOLON);
+				sb.append(Constants.CHAR_NAME_SPACE);
+			}
+		}
+
+		private void insertCodeOfMessageUnpackForWrapperTypes(StringBuilder sb,
+				Field field, Class<?> type) throws NotFoundException {
+			// insert a right variable
+			if (field != null) {
+				sb.append(field.getName());
+				sb.append(Constants.CHAR_NAME_SPACE);
+				sb.append(Constants.CHAR_NAME_EQUAL);
+				sb.append(Constants.CHAR_NAME_SPACE);
+			}
+			sb.append(type.getName());
+			sb.append(Constants.CHAR_NAME_DOT);
+			sb.append(Constants.METHOD_NAME_VALUEOF);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_DOT);
+			// insert the name of a unpack method
+			if (type.equals(Boolean.class)) { // Boolean
+				sb.append(Constants.METHOD_NAME_UNPACKBOOLEAN);
+			} else if (type.equals(Byte.class)) { // Byte
+				sb.append(Constants.METHOD_NAME_UNPACKBYTE);
+			} else if (type.equals(Double.class)) { // Double
+				sb.append(Constants.METHOD_NAME_UNPACKDOUBLE);
+			} else if (type.equals(Float.class)) { // Float
+				sb.append(Constants.METHOD_NAME_UNPACKFLOAT);
+			} else if (type.equals(Integer.class)) { // Integer
+				sb.append(Constants.METHOD_NAME_UNPACKINT);
+			} else if (type.equals(Long.class)) { // Long
+				sb.append(Constants.METHOD_NAME_UNPACKLONG);
+			} else if (type.equals(Short.class)) { // Short
+				sb.append(Constants.METHOD_NAME_UNPACKSHORT);
+			} else {
+				throw new NotFoundException("unknown type:  " + type.getName());
+			}
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			if (field != null) {
+				sb.append(Constants.CHAR_NAME_SEMICOLON);
+				sb.append(Constants.CHAR_NAME_SPACE);
+			}
+		}
+
+		private void insertCodeOfMessageUnpackForListType(StringBuilder sb,
+				Field field, Class<?> type) throws NotFoundException {
+			ParameterizedType generic = (ParameterizedType) field
+					.getGenericType();
+			Class<?> genericType = (Class<?>) generic.getActualTypeArguments()[0];
+
+			// len
+			sb.append(int.class.getName());
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_SIZE);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_EQUAL);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_DOT);
+			sb.append(Constants.METHOD_NAME_UNPACKARRAY);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+
+			// field initializer
+			sb.append(field.getName());
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_EQUAL);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.KEYWORD_NEW);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(ArrayList.class.getName());
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+
+			// for loop
+			sb.append(Constants.KEYWORD_FOR);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(int.class.getName());
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_I);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_EQUAL);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(0);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_I);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_LESSTHAN);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_SIZE);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_PLUS);
+			sb.append(Constants.CHAR_NAME_PLUS);
+			sb.append(Constants.VARIABLE_NAME_I);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET);
+			sb.append(Constants.CHAR_NAME_SPACE);
+
+			// block
+			sb.append(field.getName());
+			sb.append(Constants.CHAR_NAME_DOT);
+			sb.append(Constants.METHOD_NAME_ADD);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			insertCodeOfMessageUnpack(sb, null, genericType);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+
+			sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET);
+			sb.append(Constants.CHAR_NAME_SPACE);
+		}
+
+		private void insertCodeOfMessageUnpackForMapType(StringBuilder sb,
+				Field field, Class<?> type) throws NotFoundException {
+			ParameterizedType generic = (ParameterizedType) field
+					.getGenericType();
+			Class<?> genericType0 = (Class<?>) generic.getActualTypeArguments()[0];
+			Class<?> genericType1 = (Class<?>) generic.getActualTypeArguments()[1];
+
+			// len
+			sb.append(int.class.getName());
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_SIZE);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_EQUAL);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_DOT);
+			sb.append(Constants.METHOD_NAME_UNPACKMAP);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+
+			// field initializer
+			sb.append(field.getName());
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_EQUAL);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.KEYWORD_NEW);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(HashMap.class.getName());
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+
+			// for loop
+			sb.append(Constants.KEYWORD_FOR);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(int.class.getName());
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_I);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_EQUAL);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(0);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_I);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_LESSTHAN);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.VARIABLE_NAME_SIZE);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_PLUS);
+			sb.append(Constants.CHAR_NAME_PLUS);
+			sb.append(Constants.VARIABLE_NAME_I);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET);
+			sb.append(Constants.CHAR_NAME_SPACE);
+
+			// block map.
+			sb.append(field.getName());
+			sb.append(Constants.CHAR_NAME_DOT);
+			sb.append(Constants.METHOD_NAME_PUT);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			insertCodeOfMessageUnpack(sb, null, genericType0);
+			sb.append(Constants.CHAR_NAME_COMMA);
+			sb.append(Constants.CHAR_NAME_SPACE);
+			insertCodeOfMessageUnpack(sb, null, genericType1);
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+
+			sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET);
+			sb.append(Constants.CHAR_NAME_SPACE);
+		}
+
+		private void insertCodeOfMessageUnpackForMsgUnpackableType(
+				StringBuilder sb, Field field, Class<?> type) {
+			// insert a right variable // ignore
+			sb.append(Constants.VARIABLE_NAME_PK);
+			sb.append(Constants.CHAR_NAME_DOT);
+			sb.append(Constants.METHOD_NAME_UNPACK);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS);
+			sb.append(MessageUnpackable.class.getName());
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(field.getName());
+			sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS);
+			sb.append(Constants.CHAR_NAME_SEMICOLON);
+			sb.append(Constants.CHAR_NAME_SPACE);
+		}
+
+		private void addMessageConvertMethod(CtClass enhCtClass,
+				CtClass origCtClass, Field[] fields)
+				throws CannotCompileException {
+			StringBuilder sb = new StringBuilder();
+			sb.append(Constants.KEYWORD_MODIFIER_PUBLIC).append(
+					Constants.CHAR_NAME_SPACE).append(Constants.TYPE_NAME_VOID)
+					.append(Constants.CHAR_NAME_SPACE).append(
+							Constants.METHOD_NAME_MSGCONVERT).append(
+							Constants.CHAR_NAME_LEFT_PARENTHESIS).append(
+							Constants.TYPE_NAME_OBJECT).append(
+							Constants.CHAR_NAME_SPACE).append(
+							Constants.VARIABLE_NAME_OBJ).append(
+							Constants.CHAR_NAME_RIGHT_PARENTHESIS).append(
+							Constants.CHAR_NAME_SPACE).append(
+							Constants.KEYWORD_THROWS).append(
+							Constants.CHAR_NAME_SPACE).append(
+							Constants.TYPE_NAME_MSGTYPEEXCEPTION).append(
+							Constants.CHAR_NAME_SPACE).append(
+							Constants.CHAR_NAME_LEFT_CURLY_BRACHET).append(
+							Constants.CHAR_NAME_SPACE);
+			// TODO
+			sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET);
+			//System.out.println("messageConvert method: " + sb.toString());
+			CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhCtClass);
+			enhCtClass.addMethod(newCtMethod);
+		}
+
+		private Class<?> createClass(CtClass enhCtClass)
+				throws CannotCompileException {
+			return enhCtClass.toClass(null, null);
+		}
+	}
+
+	private static Enhancer enhancer;
+
+	public static Class<?> getEnhancedClass(Class<?> origClass) {
+		if (enhancer == null) {
+			enhancer = new Enhancer();
+		}
+
+		String origName = origClass.getName();
+		Class<?> enhClass = enhancer.getCache(origName);
+		if (enhClass == null) {
+			// generate a class object related to the original class
+			try {
+				enhClass = enhancer.generate(origClass);
+			} catch (NotFoundException e) {
+				throw new PackUnpackUtilException(e.getMessage(), e);
+			} catch (CannotCompileException e) {
+				throw new PackUnpackUtilException(e.getMessage(), e);
+			}
+			// set the generated class to the cache
+			enhancer.setCache(origName, enhClass);
+		}
+		return enhClass;
+	}
+
+	public static Object newEnhancedInstance(Class<?> origClass) {
+		try {
+			Class<?> enhClass = getEnhancedClass(origClass);
+			// create a new object of the generated class
+			return enhClass.newInstance();
+		} catch (InstantiationException e) {
+			throw new PackUnpackUtilException(e.getMessage(), e);
+		} catch (IllegalAccessException e) {
+			throw new PackUnpackUtilException(e.getMessage(), e);
+		}
+	}
+
+	@MessagePackUnpackable
+	public static class Image {
+		public String uri = "";
+
+		public String title = "";
+
+		public int width = 0;
+
+		public int height = 0;
+
+		public int size = 0;
+
+		public boolean equals(Image obj) {
+			return uri.equals(obj.uri) && title.equals(obj.title)
+					&& width == obj.width && height == obj.height
+					&& size == obj.size;
+		}
+
+		public boolean equals(Object obj) {
+			if (obj.getClass() != Image.class) {
+				return false;
+			}
+			return equals((Image) obj);
+		}
+	}
+
+	public static void main(final String[] args) throws Exception {
+		PackUnpackUtil.getEnhancedClass(Image.class);
+		Image src = (Image) PackUnpackUtil.newEnhancedInstance(Image.class);
+		src.title = "msgpack";
+		src.uri = "http://msgpack.org/";
+		src.width = 2560;
+		src.height = 1600;
+		src.size = 4096000;
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		new Packer(out).pack(src);
+		Image dst = (Image) PackUnpackUtil.newEnhancedInstance(Image.class);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		Unpacker pac = new Unpacker(in);
+		pac.unpack((MessageUnpackable) dst);
+		//System.out.println(src.equals(dst));
+	}
+}
diff --git a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtilException.java b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtilException.java
new file mode 100644
index 00000000..df3af058
--- /dev/null
+++ b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtilException.java
@@ -0,0 +1,12 @@
+package org.msgpack.util.annotation;
+
+public class PackUnpackUtilException extends RuntimeException {
+
+	public PackUnpackUtilException(String reason) {
+		super(reason);
+	}
+	
+	public PackUnpackUtilException(String reason, Throwable t) {
+		super(reason, t);
+	}
+}
diff --git a/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java b/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java
new file mode 100644
index 00000000..dbea7cb2
--- /dev/null
+++ b/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java
@@ -0,0 +1,478 @@
+package org.msgpack.util.annotation;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.msgpack.MessageUnpackable;
+import org.msgpack.Packer;
+import org.msgpack.Unpacker;
+
+public class TestMessagePackUnpackable extends TestCase {
+
+	@Test
+	public void testPrimitiveTypeFields() throws Exception {
+		PrimitiveTypeFieldsClass src = (PrimitiveTypeFieldsClass) PackUnpackUtil
+				.newEnhancedInstance(PrimitiveTypeFieldsClass.class);
+		src.f0 = (byte) 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f6 = false;
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		new Packer(out).pack(src);
+		PrimitiveTypeFieldsClass dst = (PrimitiveTypeFieldsClass) PackUnpackUtil
+				.newEnhancedInstance(PrimitiveTypeFieldsClass.class);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		Unpacker pac = new Unpacker(in);
+		pac.unpack((MessageUnpackable) dst);
+		assertEquals(src.f0, dst.f0);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2, dst.f2);
+		assertEquals(src.f3, dst.f3);
+		assertEquals(src.f4, dst.f4);
+		assertEquals(src.f5, dst.f5);
+		assertEquals(src.f6, dst.f6);
+	}
+
+	@MessagePackUnpackable
+	public static class PrimitiveTypeFieldsClass {
+		public byte f0;
+		public short f1;
+		public int f2;
+		public long f3;
+		public float f4;
+		public double f5;
+		public boolean f6;
+
+		public PrimitiveTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testGeneralReferenceTypeFieldsClass() throws Exception {
+		GeneralReferenceTypeFieldsClass src = (GeneralReferenceTypeFieldsClass) PackUnpackUtil
+				.newEnhancedInstance(GeneralReferenceTypeFieldsClass.class);
+		src.f0 = 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = (long) 3;
+		src.f4 = (float) 4;
+		src.f5 = (double) 5;
+		src.f6 = false;
+		src.f7 = new BigInteger("7");
+		src.f8 = "8";
+		src.f9 = new byte[] { 0x01, 0x02 };
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		new Packer(out).pack(src);
+		GeneralReferenceTypeFieldsClass dst = (GeneralReferenceTypeFieldsClass) PackUnpackUtil
+				.newEnhancedInstance(GeneralReferenceTypeFieldsClass.class);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		Unpacker pac = new Unpacker(in);
+		pac.unpack((MessageUnpackable) dst);
+		assertEquals(src.f0, dst.f0);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2, dst.f2);
+		assertEquals(src.f3, dst.f3);
+		assertEquals(src.f4, dst.f4);
+		assertEquals(src.f5, dst.f5);
+		assertEquals(src.f6, dst.f6);
+		assertEquals(src.f7, dst.f7);
+		assertEquals(src.f8, dst.f8);
+		assertEquals(src.f9[0], dst.f9[0]);
+		assertEquals(src.f9[1], dst.f9[1]);
+	}
+
+	@MessagePackUnpackable
+	public static class GeneralReferenceTypeFieldsClass {
+		public Byte f0;
+		public Short f1;
+		public Integer f2;
+		public Long f3;
+		public Float f4;
+		public Double f5;
+		public Boolean f6;
+		public BigInteger f7;
+		public String f8;
+		public byte[] f9;
+
+		public GeneralReferenceTypeFieldsClass() {
+		}
+	}
+
+	public void testListTypes() throws Exception {
+		SampleListTypes src = (SampleListTypes) PackUnpackUtil
+				.newEnhancedInstance(SampleListTypes.class);
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = new ArrayList<Integer>();
+		src.f1.add(1);
+		src.f1.add(2);
+		src.f1.add(3);
+		src.f2 = new ArrayList<String>();
+		src.f2.add("e1");
+		src.f2.add("e2");
+		src.f2.add("e3");
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		new Packer(out).pack(src);
+		SampleListTypes dst = (SampleListTypes) PackUnpackUtil
+				.newEnhancedInstance(SampleListTypes.class);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		Unpacker pac = new Unpacker(in);
+		pac.unpack((MessageUnpackable) dst);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		for (int i = 0; i < src.f1.size(); ++i) {
+			assertEquals(src.f1.get(i), dst.f1.get(i));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		for (int i = 0; i < src.f2.size(); ++i) {
+			assertEquals(src.f2.get(i), dst.f2.get(i));
+		}
+	}
+
+	@MessagePackUnpackable
+	public static class SampleListTypes {
+		public List<Integer> f0;
+		public List<Integer> f1;
+		public List<String> f2;
+
+		public SampleListTypes() {
+		}
+	}
+
+	public void testMapTypes() throws Exception {
+		SampleMapTypes src = (SampleMapTypes) PackUnpackUtil
+				.newEnhancedInstance(SampleMapTypes.class);
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = new HashMap<Integer, Integer>();
+		src.f1.put(1, 1);
+		src.f1.put(2, 2);
+		src.f1.put(3, 3);
+		src.f2 = new HashMap<String, Integer>();
+		src.f2.put("k1", 1);
+		src.f2.put("k2", 2);
+		src.f2.put("k3", 3);
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		new Packer(out).pack(src);
+		SampleMapTypes dst = (SampleMapTypes) PackUnpackUtil
+				.newEnhancedInstance(SampleMapTypes.class);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		Unpacker pac = new Unpacker(in);
+		pac.unpack((MessageUnpackable) dst);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		Iterator<Integer> srcf1 = src.f1.keySet().iterator();
+		Iterator<Integer> dstf1 = dst.f1.keySet().iterator();
+		while (srcf1.hasNext()) {
+			Integer s1 = srcf1.next();
+			Integer d1 = dstf1.next();
+			assertEquals(s1, d1);
+			assertEquals(src.f1.get(s1), dst.f1.get(d1));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		Iterator<String> srcf2 = src.f2.keySet().iterator();
+		Iterator<String> dstf2 = dst.f2.keySet().iterator();
+		while (srcf2.hasNext()) {
+			String s2 = srcf2.next();
+			String d2 = dstf2.next();
+			assertEquals(s2, d2);
+			assertEquals(src.f2.get(s2), dst.f2.get(d2));
+		}
+	}
+
+	@MessagePackUnpackable
+	public static class SampleMapTypes {
+		public Map<Integer, Integer> f0;
+		public Map<Integer, Integer> f1;
+		public Map<String, Integer> f2;
+
+		public SampleMapTypes() {
+		}
+	}
+
+	@Test
+	public void testDefaultConstructorModifiers() throws Exception {
+		try {
+			PackUnpackUtil.newEnhancedInstance(NoDefaultConstructorClass.class);
+			fail();
+		} catch (PackUnpackUtilException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+		try {
+			PackUnpackUtil
+					.newEnhancedInstance(PrivateDefaultConstructorClass.class);
+			fail();
+		} catch (PackUnpackUtilException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+		try {
+			PackUnpackUtil
+					.newEnhancedInstance(ProtectedDefaultConstructorClass.class);
+			assertTrue(true);
+		} catch (PackUnpackUtilException e) {
+			fail();
+		}
+		assertTrue(true);
+		try {
+			PackUnpackUtil
+					.newEnhancedInstance(PackageDefaultConstructorClass.class);
+			fail();
+		} catch (PackUnpackUtilException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	@MessagePackUnpackable
+	public static class NoDefaultConstructorClass {
+		public NoDefaultConstructorClass(int i) {
+		}
+	}
+
+	@MessagePackUnpackable
+	public static class PrivateDefaultConstructorClass {
+		private PrivateDefaultConstructorClass() {
+		}
+	}
+
+	@MessagePackUnpackable
+	public static class ProtectedDefaultConstructorClass {
+		protected ProtectedDefaultConstructorClass() {
+		}
+	}
+
+	@MessagePackUnpackable
+	public static class PackageDefaultConstructorClass {
+		PackageDefaultConstructorClass() {
+		}
+	}
+
+	@Test
+	public void testClassModifiers() throws Exception {
+		try {
+			PackUnpackUtil.newEnhancedInstance(PrivateModifierClass.class);
+			fail();
+		} catch (PackUnpackUtilException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+		try {
+			PackUnpackUtil.newEnhancedInstance(ProtectedModifierClass.class);
+			assertTrue(true);
+		} catch (PackUnpackUtilException e) {
+			fail();
+		}
+		assertTrue(true);
+		try {
+			PackUnpackUtil.newEnhancedInstance(PackageModifierClass.class);
+			fail();
+		} catch (PackUnpackUtilException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	@MessagePackUnpackable
+	private static class PrivateModifierClass {
+	}
+
+	@MessagePackUnpackable
+	protected static class ProtectedModifierClass {
+		protected ProtectedModifierClass() {
+		}
+	}
+
+	@MessagePackUnpackable
+	static class PackageModifierClass {
+	}
+
+	@Test
+	public void testFinalClassAndAbstractClass() throws Exception {
+		try {
+			PackUnpackUtil.newEnhancedInstance(FinalModifierClass.class);
+			fail();
+		} catch (PackUnpackUtilException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+		try {
+			PackUnpackUtil.newEnhancedInstance(AbstractModifierClass.class);
+			fail();
+		} catch (PackUnpackUtilException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	@MessagePackUnpackable
+	public final static class FinalModifierClass {
+	}
+
+	@MessagePackUnpackable
+	public abstract static class AbstractModifierClass {
+	}
+
+	@Test
+	public void testInterfaceAndEnumType() throws Exception {
+		try {
+			PackUnpackUtil.newEnhancedInstance(SampleInterface.class);
+			fail();
+		} catch (PackUnpackUtilException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+		try {
+			PackUnpackUtil.newEnhancedInstance(SampleEnum.class);
+			fail();
+		} catch (PackUnpackUtilException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	@MessagePackUnpackable
+	public interface SampleInterface {
+	}
+
+	@MessagePackUnpackable
+	public enum SampleEnum {
+	}
+
+	@Test
+	public void testFieldModifiers() throws Exception {
+		FieldModifiersClass src = (FieldModifiersClass) PackUnpackUtil
+				.newEnhancedInstance(FieldModifiersClass.class);
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		new Packer(out).pack(src);
+		FieldModifiersClass dst = (FieldModifiersClass) PackUnpackUtil
+				.newEnhancedInstance(FieldModifiersClass.class);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		Unpacker pac = new Unpacker(in);
+		pac.unpack((MessageUnpackable) dst);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+		assertTrue(src.f2 != dst.f2);
+		assertTrue(src.f3 == dst.f3);
+		assertTrue(src.f4 != dst.f4);
+	}
+
+	@MessagePackUnpackable
+	public static class FieldModifiersClass {
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public FieldModifiersClass() {
+		}
+	}
+
+	@Test
+	public void testNestedAnnotatedFieldClass() throws Exception {
+		NestedClass src2 = (NestedClass) PackUnpackUtil
+				.newEnhancedInstance(NestedClass.class);
+		BaseClass src = (BaseClass) PackUnpackUtil
+				.newEnhancedInstance(BaseClass.class);
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		new Packer(out).pack(src);
+		NestedClass dst2 = (NestedClass) PackUnpackUtil
+				.newEnhancedInstance(NestedClass.class);
+		BaseClass dst = (BaseClass) PackUnpackUtil
+				.newEnhancedInstance(BaseClass.class);
+		dst.f1 = dst2;
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		Unpacker pac = new Unpacker(in);
+		pac.unpack((MessageUnpackable) dst);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src2.f2 == dst.f1.f2);
+	}
+
+	@MessagePackUnpackable
+	public static class BaseClass {
+		public int f0;
+		public NestedClass f1;
+
+		public BaseClass() {
+		}
+	}
+
+	@MessagePackUnpackable
+	public static class NestedClass {
+		public int f2;
+
+		public NestedClass() {
+		}
+	}
+
+	@Test
+	public void testExtendedClass() throws Exception {
+		SampleSubClass src = (SampleSubClass) PackUnpackUtil
+				.newEnhancedInstance(SampleSubClass.class);
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f8 = 8;
+		src.f9 = 9;
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		new Packer(out).pack(src);
+		SampleSubClass dst = (SampleSubClass) PackUnpackUtil
+				.newEnhancedInstance(SampleSubClass.class);
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		Unpacker pac = new Unpacker(in);
+		pac.unpack((MessageUnpackable) dst);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+		assertTrue(src.f2 != dst.f2);
+		assertTrue(src.f3 == dst.f3);
+		assertTrue(src.f4 != dst.f4);
+		assertTrue(src.f5 == dst.f5);
+		assertTrue(src.f6 == dst.f6);
+		assertTrue(src.f8 == dst.f8);
+		assertTrue(src.f9 != dst.f9);
+	}
+
+	@MessagePackUnpackable
+	public static class SampleSubClass extends SampleSuperClass {
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public SampleSubClass() {
+		}
+	}
+
+	public static class SampleSuperClass {
+		public int f5;
+		public final int f6 = 2;
+		private int f7;
+		protected int f8;
+		int f9;
+
+		public SampleSuperClass() {
+		}
+	}
+}