typeConverters) {
+ this.typeConverters = typeConverters;
+ }
+
+ @Override
+ public TypeConverter getTypeConverter(Method getter) {
+ for (TypeConverter each: typeConverters) {
+ if (each.accept(getter))
+ return each;
+ }
+ return null;
+ }
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/EditableModule.java b/commons.editable/src/main/java/com/pmease/commons/editable/EditableModule.java
new file mode 100644
index 0000000000..021a4c3f87
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/EditableModule.java
@@ -0,0 +1,26 @@
+package com.pmease.commons.editable;
+
+import com.pmease.commons.editable.typeconverter.TypeConverter;
+import com.pmease.commons.loader.AbstractPlugin;
+import com.pmease.commons.loader.AbstractPluginModule;
+
+/**
+ * NOTE: Do not forget to rename moduleClass property defined in the pom if you've renamed this class.
+ *
+ */
+public class EditableModule extends AbstractPluginModule {
+
+ @Override
+ protected void configure() {
+ super.configure();
+
+ // put your guice bindings here
+ addExtensionsFromPackage(TypeConverter.class, TypeConverter.class);
+ }
+
+ @Override
+ protected Class extends AbstractPlugin> getPluginClass() {
+ return EditablePlugin.class;
+ }
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/EditablePlugin.java b/commons.editable/src/main/java/com/pmease/commons/editable/EditablePlugin.java
new file mode 100644
index 0000000000..bfa4dd99e5
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/EditablePlugin.java
@@ -0,0 +1,13 @@
+package com.pmease.commons.editable;
+
+import java.util.Collection;
+import com.pmease.commons.loader.AbstractPlugin;
+
+public class EditablePlugin extends AbstractPlugin {
+
+ @Override
+ public Collection> getExtensions() {
+ return null;
+ }
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/TypeConverterRegistry.java b/commons.editable/src/main/java/com/pmease/commons/editable/TypeConverterRegistry.java
new file mode 100644
index 0000000000..a6f19b2b4e
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/TypeConverterRegistry.java
@@ -0,0 +1,22 @@
+package com.pmease.commons.editable;
+
+import java.lang.reflect.Method;
+
+import com.google.inject.ImplementedBy;
+import com.pmease.commons.editable.typeconverter.TypeConverter;
+
+@ImplementedBy(DefaultTypeConverterRegistry.class)
+public interface TypeConverterRegistry {
+
+ /**
+ * Get type converter of the property given the property getter.
+ *
+ * @param getter
+ * getter of the property
+ * @return
+ * type converter instance for the property, or null if no
+ * type converter matches the property getter
+ */
+ TypeConverter getTypeConverter(Method getter);
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/annotation/ChoiceProvider.java b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/ChoiceProvider.java
new file mode 100644
index 0000000000..f82a92e227
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/ChoiceProvider.java
@@ -0,0 +1,12 @@
+package com.pmease.commons.editable.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ChoiceProvider {
+ String value();
+}
\ No newline at end of file
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Choices.java b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Choices.java
new file mode 100644
index 0000000000..80a8389efb
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Choices.java
@@ -0,0 +1,12 @@
+package com.pmease.commons.editable.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Choices {
+ String[] value();
+}
\ No newline at end of file
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Editable.java b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Editable.java
new file mode 100644
index 0000000000..3b81bf6561
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Editable.java
@@ -0,0 +1,17 @@
+package com.pmease.commons.editable.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Editable {
+ String name() default "";
+
+ int order() default 0;
+
+ String description() default "";
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/annotation/ImplementationProvider.java b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/ImplementationProvider.java
new file mode 100644
index 0000000000..a8f89772dd
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/ImplementationProvider.java
@@ -0,0 +1,14 @@
+package com.pmease.commons.editable.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 ImplementationProvider {
+ Class> clazz() default Void.class;
+
+ String method();
+}
\ No newline at end of file
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Multiline.java b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Multiline.java
new file mode 100644
index 0000000000..dc59a2cbbe
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Multiline.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright PMEase (c) 2005-2008,
+ * Date: Feb 24, 2008
+ * Time: 4:29:05 PM
+ * All rights reserved.
+ *
+ * Revision: $Id: Multiline.java 1209 2008-07-28 00:16:18Z robin $
+ */
+package com.pmease.commons.editable.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Multiline {
+ int value() default 4;
+}
\ No newline at end of file
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Numeric.java b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Numeric.java
new file mode 100644
index 0000000000..f8fa867a94
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Numeric.java
@@ -0,0 +1,11 @@
+package com.pmease.commons.editable.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Numeric {
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Password.java b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Password.java
new file mode 100644
index 0000000000..151a6c81d2
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/annotation/Password.java
@@ -0,0 +1,12 @@
+package com.pmease.commons.editable.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Password {
+ boolean confirmative() default false;
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/BooleanConverter.java b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/BooleanConverter.java
new file mode 100644
index 0000000000..2664266661
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/BooleanConverter.java
@@ -0,0 +1,37 @@
+package com.pmease.commons.editable.typeconverter;
+
+import java.lang.reflect.Method;
+
+public class BooleanConverter implements TypeConverter {
+
+ public boolean accept(Method getter) {
+ return getter.getReturnType() == boolean.class ||
+ getter.getReturnType() == Boolean.class;
+ }
+
+ public Object toObject(Class> type, String string) {
+ if (string == null)
+ return null;
+
+ if ("yes".equalsIgnoreCase(string) || "y".equalsIgnoreCase(string)
+ || "true".equalsIgnoreCase(string) || "t".equalsIgnoreCase(string))
+ return Boolean.TRUE;
+
+ if ("no".equalsIgnoreCase(string) || "n".equalsIgnoreCase(string)
+ || "false".equalsIgnoreCase(string) || "f".equalsIgnoreCase(string))
+ return Boolean.FALSE;
+
+ throw new ConversionException("Invalid boolean value: " + string);
+ }
+
+ public String toString(Object obj) {
+ if (obj == null)
+ return null;
+
+ if ((Boolean)obj)
+ return "yes";
+ else
+ return "no";
+ }
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/ConfirmativePasswordConverter.java b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/ConfirmativePasswordConverter.java
new file mode 100644
index 0000000000..a7e2006b05
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/ConfirmativePasswordConverter.java
@@ -0,0 +1,42 @@
+package com.pmease.commons.editable.typeconverter;
+
+import java.lang.reflect.Method;
+
+import com.pmease.commons.editable.annotation.Password;
+import com.pmease.commons.util.StringUtils;
+
+public class ConfirmativePasswordConverter implements TypeConverter {
+
+ public boolean accept(Method getter) {
+ return getter.getAnnotation(Password.class) != null
+ && getter.getAnnotation(Password.class).confirmative();
+ }
+
+ public Object toObject(Class> type, String string) {
+ if (string != null) {
+ if (string.indexOf('\n') != -1) {
+ String[] parts = StringUtils.split(string, "\n");
+ if (parts.length == 0) {
+ return null;
+ } else if (parts.length != 2 || !parts[0].equals(parts[1])) {
+ throw new ConversionException("Password and it's " +
+ "confirmation should be identical.");
+ } else {
+ if (parts[0].length() == 0)
+ return null;
+ else
+ return parts[0];
+ }
+ } else {
+ return string;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public String toString(Object obj) {
+ return (String) obj;
+ }
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/ConversionException.java b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/ConversionException.java
new file mode 100644
index 0000000000..d0cb6632da
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/ConversionException.java
@@ -0,0 +1,14 @@
+package com.pmease.commons.editable.typeconverter;
+
+import com.pmease.commons.util.ExceptionUtils;
+
+@SuppressWarnings("serial")
+public class ConversionException extends RuntimeException {
+ public ConversionException(String message) {
+ super(message);
+ }
+
+ public ConversionException(String message, Object...factors) {
+ super(ExceptionUtils.buildMessage(message, factors));
+ }
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/EnumConverter.java b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/EnumConverter.java
new file mode 100644
index 0000000000..376815b438
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/EnumConverter.java
@@ -0,0 +1,31 @@
+package com.pmease.commons.editable.typeconverter;
+
+import java.lang.reflect.Method;
+
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class EnumConverter implements TypeConverter {
+
+ public boolean accept(Method getter) {
+ return Enum.class.isAssignableFrom(getter.getReturnType());
+ }
+
+ public Object toObject(Class type, String string) {
+ if (string == null)
+ return null;
+
+ try {
+ return Enum.valueOf(type, string);
+ } catch (IllegalArgumentException e) {
+ throw new ConversionException("Invalid value of enum '" +
+ type.getName() + "'.");
+ }
+ }
+
+ public String toString(Object obj) {
+ if (obj == null)
+ return null;
+ else
+ return obj.toString();
+ }
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/NumericConverter.java b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/NumericConverter.java
new file mode 100644
index 0000000000..dd1759cf80
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/NumericConverter.java
@@ -0,0 +1,51 @@
+package com.pmease.commons.editable.typeconverter;
+
+import java.lang.reflect.Method;
+
+import com.pmease.commons.editable.annotation.Numeric;
+
+public class NumericConverter implements TypeConverter {
+
+ public boolean accept(Method getter) {
+ Class> type = getter.getReturnType();
+ return type == int.class || type == long.class || type == Integer.class ||
+ type == Long.class || type == float.class || type == Float.class ||
+ type == double.class || type == Double.class || getter.getAnnotation(Numeric.class) != null;
+ }
+
+ public Object toObject(Class> type, String string) {
+ if (string == null) {
+ if (type == int.class || type == long.class
+ || type == float.class || type == double.class) {
+ throw new ConversionException("Should not be empty.");
+ } else {
+ return null;
+ }
+ }
+
+ try {
+ if (type == int.class || type == Integer.class) {
+ return Integer.parseInt(string);
+ } else if (type == long.class || type == Long.class) {
+ return Long.parseLong(string);
+ } else if (type == float.class || type == Float.class) {
+ return Float.parseFloat(string);
+ } else if (type == double.class || type == Double.class) {
+ return Double.parseDouble(string);
+ } else {
+ Long.parseLong(string);
+ return string;
+ }
+ } catch (NumberFormatException e) {
+ throw new ConversionException("Invalid numeric value.");
+ }
+ }
+
+ public String toString(Object obj) {
+ if (obj == null)
+ return null;
+ else
+ return obj.toString();
+ }
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/PasswordConverter.java b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/PasswordConverter.java
new file mode 100644
index 0000000000..4e8e10a8d9
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/PasswordConverter.java
@@ -0,0 +1,23 @@
+package com.pmease.commons.editable.typeconverter;
+
+import java.lang.reflect.Method;
+
+import com.pmease.commons.editable.annotation.Password;
+
+public class PasswordConverter implements TypeConverter {
+
+ public boolean accept(Method getter) {
+ return getter.getReturnType() == String.class &&
+ getter.getAnnotation(Password.class) != null &&
+ !getter.getAnnotation(Password.class).confirmative();
+ }
+
+ public Object toObject(Class> type, String string) {
+ return string;
+ }
+
+ public String toString(Object obj) {
+ return (String) obj;
+ }
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/StringConverter.java b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/StringConverter.java
new file mode 100644
index 0000000000..7246a44153
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/StringConverter.java
@@ -0,0 +1,27 @@
+package com.pmease.commons.editable.typeconverter;
+
+import java.lang.reflect.Method;
+
+import com.pmease.commons.editable.annotation.Password;
+
+public class StringConverter implements TypeConverter {
+
+ public boolean accept(Method getter) {
+ if (getter.getAnnotation(Password.class) != null)
+ return false;
+ else
+ return getter.getReturnType() == String.class;
+ }
+
+ public Object toObject(Class> type, String string) {
+ return string;
+ }
+
+ public String toString(Object obj) {
+ if (obj == null)
+ return null;
+ else
+ return obj.toString();
+ }
+
+}
diff --git a/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/TypeConverter.java b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/TypeConverter.java
new file mode 100644
index 0000000000..3fa1d2f49c
--- /dev/null
+++ b/commons.editable/src/main/java/com/pmease/commons/editable/typeconverter/TypeConverter.java
@@ -0,0 +1,12 @@
+package com.pmease.commons.editable.typeconverter;
+
+import java.lang.reflect.Method;
+
+public interface TypeConverter {
+
+ boolean accept(Method getter);
+
+ String toString(Object obj);
+
+ Object toObject(Class> type, String string);
+}
diff --git a/commons.editable/src/main/resources/META-INF/maven/archetype-metadata.xml b/commons.editable/src/main/resources/META-INF/maven/archetype-metadata.xml
new file mode 100644
index 0000000000..904fb7a4bb
--- /dev/null
+++ b/commons.editable/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ src/main/java
+
+
+ src/main/resources
+
+
+ src/test/java
+
+
+ src/test/resources
+
+
+
\ No newline at end of file
diff --git a/commons.editable/src/main/resources/archetype-resource/pom.xml b/commons.editable/src/main/resources/archetype-resource/pom.xml
new file mode 100644
index 0000000000..58eb98e1af
--- /dev/null
+++ b/commons.editable/src/main/resources/archetype-resource/pom.xml
@@ -0,0 +1,60 @@
+
+ 4.0.0
+#if (\${groupId} != "com.pmease")
+ \${groupId}
+#end
+ \${artifactId}
+ \${version}
+
+ com.pmease
+ parent.general
+ 1.0.28
+
+
+
+
+
+ com.pmease
+ plugin.maven
+
+
+ maven-source-plugin
+
+
+ maven-javadoc-plugin
+
+
+
+
+
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${project.version}
+
+
+
+
+
+ pmeaseRepo
+ PMEase Repository
+
+ true
+ never
+ fail
+
+
+ true
+ always
+ fail
+
+ http://artifact.pmease.com/
+
+
+
+
+ \${package}.PluginModule
+
+
+
diff --git a/commons.editable/src/main/resources/archetype-resource/src/main/java/Plugin.java b/commons.editable/src/main/resources/archetype-resource/src/main/java/Plugin.java
new file mode 100644
index 0000000000..da8842a8e2
--- /dev/null
+++ b/commons.editable/src/main/resources/archetype-resource/src/main/java/Plugin.java
@@ -0,0 +1,13 @@
+package ${package};
+
+import java.util.Collection;
+import com.pmease.commons.loader.AbstractPlugin;
+
+public class Plugin extends AbstractPlugin {
+
+ @Override
+ public Collection> getExtensions() {
+ return null;
+ }
+
+}
diff --git a/commons.editable/src/main/resources/archetype-resource/src/main/java/PluginModule.java b/commons.editable/src/main/resources/archetype-resource/src/main/java/PluginModule.java
new file mode 100644
index 0000000000..d566c299ba
--- /dev/null
+++ b/commons.editable/src/main/resources/archetype-resource/src/main/java/PluginModule.java
@@ -0,0 +1,24 @@
+package ${package};
+
+import com.pmease.commons.loader.AbstractPlugin;
+import com.pmease.commons.loader.AbstractPluginModule;
+
+/**
+ * NOTE: Do not forget to rename moduleClass property defined in the pom if you've renamed this class.
+ *
+ */
+public class PluginModule extends AbstractPluginModule {
+
+ @Override
+ protected void configure() {
+ super.configure();
+
+ // put your guice bindings here
+ }
+
+ @Override
+ protected Class extends AbstractPlugin> getPluginClass() {
+ return Plugin.class;
+ }
+
+}
diff --git a/commons.hibernate/pom.xml b/commons.hibernate/pom.xml
index bd2961652b..8e9e36c007 100644
--- a/commons.hibernate/pom.xml
+++ b/commons.hibernate/pom.xml
@@ -73,11 +73,6 @@
jtds
1.3.0
-
- org.javassist
- javassist
- 3.17.1-GA
-
com.pmease
commons.jetty
diff --git a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/ConfigurationProvider.java b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/ConfigurationProvider.java
index cfa5b0a108..ff230da32c 100644
--- a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/ConfigurationProvider.java
+++ b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/ConfigurationProvider.java
@@ -3,6 +3,7 @@ package com.pmease.commons.hibernate;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Properties;
+import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -15,8 +16,6 @@ import org.hibernate.cfg.NamingStrategy;
import com.google.inject.Provider;
import com.pmease.commons.bootstrap.Bootstrap;
-import com.pmease.commons.hibernate.extensionpoints.ModelContribution;
-import com.pmease.commons.loader.PluginManager;
import com.pmease.commons.util.ClassUtils;
import com.pmease.commons.util.StringUtils;
@@ -25,16 +24,16 @@ public class ConfigurationProvider implements Provider {
private Configuration configuration;
- private final PluginManager pluginManager;
+ private final Set modelProviders;
private final NamingStrategy namingStrategy;
private final Properties hibernateProperties;
@Inject
- public ConfigurationProvider(PluginManager pluginManager, NamingStrategy namingStrategy,
+ public ConfigurationProvider(Set modelProviders, NamingStrategy namingStrategy,
@Nullable @Named("hibernate") Properties hibernateProperties) {
- this.pluginManager = pluginManager;
+ this.modelProviders = modelProviders;
this.namingStrategy = namingStrategy;
this.hibernateProperties = hibernateProperties;
}
@@ -51,17 +50,15 @@ public class ConfigurationProvider implements Provider {
configuration = new Configuration();
configuration.setNamingStrategy(namingStrategy);
- Collection> modelClasses =
+ Collection> modelClasses =
ClassUtils.findSubClasses(AbstractEntity.class, AbstractEntity.class);
- for (Class model: modelClasses) {
+ for (Class extends AbstractEntity> model: modelClasses) {
if (!Modifier.isAbstract(model.getModifiers()))
configuration.addAnnotatedClass(model);
}
- Collection contributions =
- pluginManager.getExtensions(ModelContribution.class);
- for (ModelContribution contribution: contributions) {
- for (Class extends AbstractEntity> modelClass: contribution.getModelClasses())
+ for (ModelProvider provider: modelProviders) {
+ for (Class extends AbstractEntity> modelClass: provider.getModelClasses())
configuration.addAnnotatedClass(modelClass);
}
diff --git a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/HibernateModule.java b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/HibernateModule.java
index 405f08f62d..ba640decbd 100644
--- a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/HibernateModule.java
+++ b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/HibernateModule.java
@@ -32,7 +32,6 @@ public class HibernateModule extends AbstractPluginModule {
bind(Configuration.class).toProvider(ConfigurationProvider.class);
bind(UnitOfWork.class).to(UnitOfWorkImpl.class);
bind(Session.class).toProvider(UnitOfWorkImpl.class);
- bind(SessionProvider.class).to(UnitOfWorkImpl.class);
bind(GeneralDao.class).to(DefaultGeneralDao.class);
diff --git a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/HibernatePlugin.java b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/HibernatePlugin.java
index 6d8b98dda9..ce177323af 100644
--- a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/HibernatePlugin.java
+++ b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/HibernatePlugin.java
@@ -1,5 +1,7 @@
package com.pmease.commons.hibernate;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.EnumSet;
@@ -10,8 +12,10 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.commons.loader.AbstractPlugin;
+import com.pmease.commons.util.ExceptionUtils;
public class HibernatePlugin extends AbstractPlugin {
@@ -19,6 +23,21 @@ public class HibernatePlugin extends AbstractPlugin {
private final HibernateFilter hibernateFilter;
+ private static Field typeLiteralTypeField;
+
+ static {
+ try {
+ typeLiteralTypeField = TypeLiteral.class.getDeclaredField("type");
+ typeLiteralTypeField.setAccessible(true);
+ Field modifiersField = Field.class.getDeclaredField("modifiers");
+ modifiersField.setAccessible(true);
+ modifiersField.setInt(typeLiteralTypeField, Modifier.PROTECTED);
+ } catch (Exception e) {
+ throw ExceptionUtils.unchecked(e);
+ }
+
+ }
+
@Inject
public HibernatePlugin(PersistService persistService, HibernateFilter hibernateFilter) {
this.persistService = persistService;
diff --git a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/ModelProvider.java b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/ModelProvider.java
new file mode 100644
index 0000000000..2ad2d23d1f
--- /dev/null
+++ b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/ModelProvider.java
@@ -0,0 +1,7 @@
+package com.pmease.commons.hibernate;
+
+import java.util.Collection;
+
+public interface ModelProvider {
+ Collection> getModelClasses();
+}
diff --git a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/SessionProvider.java b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/SessionProvider.java
deleted file mode 100644
index 2024976922..0000000000
--- a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/SessionProvider.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.pmease.commons.hibernate;
-
-import javax.inject.Provider;
-
-import org.hibernate.Session;
-
-public interface SessionProvider extends Provider {
-
-}
diff --git a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/UnitOfWorkImpl.java b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/UnitOfWorkImpl.java
index 9a5f4f2bd7..a27dd48483 100644
--- a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/UnitOfWorkImpl.java
+++ b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/UnitOfWorkImpl.java
@@ -11,7 +11,7 @@ import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
-public class UnitOfWorkImpl implements UnitOfWork, Provider, SessionProvider {
+public class UnitOfWorkImpl implements UnitOfWork, Provider {
private final Provider sessionFactoryProvider;
diff --git a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/extensionpoints/ModelContribution.java b/commons.hibernate/src/main/java/com/pmease/commons/hibernate/extensionpoints/ModelContribution.java
deleted file mode 100644
index c287e8f1fa..0000000000
--- a/commons.hibernate/src/main/java/com/pmease/commons/hibernate/extensionpoints/ModelContribution.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.pmease.commons.hibernate.extensionpoints;
-
-import java.util.Collection;
-
-import com.pmease.commons.hibernate.AbstractEntity;
-
-public interface ModelContribution {
- Collection> getModelClasses();
-}
diff --git a/commons.jetty/src/main/java/com/pmease/commons/jetty/JettyPlugin.java b/commons.jetty/src/main/java/com/pmease/commons/jetty/JettyPlugin.java
index f0b377bf52..4798faffff 100644
--- a/commons.jetty/src/main/java/com/pmease/commons/jetty/JettyPlugin.java
+++ b/commons.jetty/src/main/java/com/pmease/commons/jetty/JettyPlugin.java
@@ -3,6 +3,7 @@ package com.pmease.commons.jetty;
import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
+import java.util.Set;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
@@ -24,7 +25,6 @@ import com.pmease.commons.bootstrap.BootstrapUtils;
import com.pmease.commons.jetty.extensionpoints.ServerConfigurator;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.commons.loader.AbstractPlugin;
-import com.pmease.commons.loader.PluginManager;
public class JettyPlugin extends AbstractPlugin {
@@ -32,11 +32,16 @@ public class JettyPlugin extends AbstractPlugin {
private ServletContextHandler context;
- private final PluginManager pluginManager;
+ private final Set serverConfigurators;
+
+ private final Set servletContextConfigurators;
@Inject
- public JettyPlugin(PluginManager pluginManager) {
- this.pluginManager = pluginManager;
+ public JettyPlugin(
+ Set serverConfigurators,
+ Set servletContextConfigurators) {
+ this.serverConfigurators = serverConfigurators;
+ this.servletContextConfigurators = servletContextConfigurators;
}
@Override
@@ -75,8 +80,6 @@ public class JettyPlugin extends AbstractPlugin {
context.addFilter(DisableTraceFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
- Collection servletContextConfigurators =
- pluginManager.getExtensions(ServletContextConfigurator.class);
for (ServletContextConfigurator configurator: servletContextConfigurators)
configurator.configure(context);
@@ -89,9 +92,7 @@ public class JettyPlugin extends AbstractPlugin {
server.setHandler(context);
- Collection servletContainerConfigurators =
- pluginManager.getExtensions(ServerConfigurator.class);
- for (ServerConfigurator configurator: servletContainerConfigurators)
+ for (ServerConfigurator configurator: serverConfigurators)
configurator.configure(server);
return server;
diff --git a/commons.loader/pom.xml b/commons.loader/pom.xml
index f465772e33..e730c6503f 100644
--- a/commons.loader/pom.xml
+++ b/commons.loader/pom.xml
@@ -52,7 +52,11 @@
com.google.inject.extensions
guice-multibindings
${guiceVersion}
-
+
+
+ org.javassist
+ javassist
+
@@ -72,7 +76,7 @@
- 3.0
+ 4.0-beta
1.0.29
diff --git a/commons.loader/src/main/java/com/pmease/commons/loader/AbstractPluginModule.java b/commons.loader/src/main/java/com/pmease/commons/loader/AbstractPluginModule.java
index bab2c03f52..639de22afa 100644
--- a/commons.loader/src/main/java/com/pmease/commons/loader/AbstractPluginModule.java
+++ b/commons.loader/src/main/java/com/pmease/commons/loader/AbstractPluginModule.java
@@ -11,6 +11,7 @@ import com.google.inject.multibindings.Multibinder;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
+import com.pmease.commons.util.ClassUtils;
import com.pmease.commons.util.dependency.Dependency;
public abstract class AbstractPluginModule extends AbstractModule implements Dependency {
@@ -31,8 +32,7 @@ public abstract class AbstractPluginModule extends AbstractModule implements Dep
protected void configure() {
final Class extends AbstractPlugin> pluginClass = getPluginClass();
if (pluginClass != null) {
- Multibinder pluginBinder = Multibinder.newSetBinder(binder(), AbstractPlugin.class);
- pluginBinder.addBinding().to(pluginClass).in(Singleton.class);
+ addExtension(AbstractPlugin.class, pluginClass);
bindListener(new AbstractMatcher>() {
@@ -101,4 +101,16 @@ public abstract class AbstractPluginModule extends AbstractModule implements Dep
return pluginDependencies;
}
+ protected void addExtension(Class extensionPoint, Class extends T> extensionClass) {
+ Multibinder pluginBinder = Multibinder.newSetBinder(binder(), extensionPoint);
+ pluginBinder.addBinding().to(extensionClass).in(Singleton.class);
+ }
+
+ protected void addExtensionsFromPackage(Class extensionPoint, Class> packageLocator) {
+ for (Class extends T> subClass: ClassUtils.findSubClasses(extensionPoint, packageLocator)) {
+ if (ClassUtils.isConcrete(subClass))
+ addExtension(extensionPoint, subClass);
+ }
+ }
+
}
diff --git a/commons.loader/src/main/java/com/pmease/commons/loader/AppLoader.java b/commons.loader/src/main/java/com/pmease/commons/loader/AppLoader.java
index e266bb275b..b86284520b 100644
--- a/commons.loader/src/main/java/com/pmease/commons/loader/AppLoader.java
+++ b/commons.loader/src/main/java/com/pmease/commons/loader/AppLoader.java
@@ -8,6 +8,16 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
+
+import javassist.ClassClassPath;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.bytecode.SignatureAttribute.ClassSignature;
+import javassist.bytecode.SignatureAttribute.ClassType;
+import javassist.bytecode.SignatureAttribute.TypeArgument;
+import javassist.bytecode.SignatureAttribute.TypeParameter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,11 +25,14 @@ import org.slf4j.LoggerFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
+import com.google.inject.Key;
import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
import com.google.inject.util.Modules;
import com.google.inject.util.Modules.OverriddenModuleBuilder;
import com.pmease.commons.bootstrap.Bootstrap;
import com.pmease.commons.bootstrap.Lifecycle;
+import com.pmease.commons.util.ExceptionUtils;
import com.pmease.commons.util.FileUtils;
import com.pmease.commons.util.StringUtils;
import com.pmease.commons.util.dependency.DependencyHelper;
@@ -30,6 +43,9 @@ public class AppLoader implements Lifecycle {
public static Injector injector;
+ @SuppressWarnings("rawtypes")
+ private static Map typeLiterals = new HashMap();
+
@Override
public void start() {
File tempDir = Bootstrap.getTempDir();
@@ -117,4 +133,46 @@ public class AppLoader implements Lifecycle {
return injector.getInstance(type);
}
+ @SuppressWarnings({ "unchecked"})
+ public static Set getExtensions(Class extensionPoint) {
+ synchronized (typeLiterals) {
+ TypeLiteral> typeLiteral = typeLiterals.get(extensionPoint.getName());
+ if (typeLiteral == null) {
+ try {
+ String packageName = extensionPoint.getPackage().getName();
+ String generatedTypeLiteralClassName =
+ "generated." + packageName + "." + extensionPoint.getSimpleName() + "TypeLiteral";
+ ClassPool classPool = ClassPool.getDefault();
+ classPool.insertClassPath(new ClassClassPath(TypeLiteral.class));
+ CtClass ctGeneratedTypeLiteral = classPool.makeClass(generatedTypeLiteralClassName);
+ CtClass ctTypeLiteral = classPool.get(TypeLiteral.class.getName());
+ ctGeneratedTypeLiteral.setSuperclass(ctTypeLiteral);
+ CtConstructor constructor = new CtConstructor(new CtClass[0], ctGeneratedTypeLiteral);
+ constructor.setBody(null);
+ ctGeneratedTypeLiteral.addConstructor(constructor);
+
+ TypeArgument setTypeArgument = new TypeArgument(new ClassType(extensionPoint.getName()));
+ TypeArgument superClassTypeArgument = new TypeArgument(
+ new ClassType(Set.class.getName(), new TypeArgument[]{setTypeArgument}));
+
+ ClassType superClass = new ClassType(
+ TypeLiteral.class.getName(),
+ new TypeArgument[]{superClassTypeArgument});
+
+ ClassSignature signature = new ClassSignature(new TypeParameter[0], superClass, new ClassType[0]);
+ ctGeneratedTypeLiteral.setGenericSignature(signature.encode());
+
+ Class> typeLiteralClassOfExtensionPoint = ctGeneratedTypeLiteral.toClass();
+ typeLiteral = (TypeLiteral>) typeLiteralClassOfExtensionPoint.newInstance();
+
+ typeLiterals.put(extensionPoint.getName(), typeLiteral);
+ } catch (Exception e) {
+ throw ExceptionUtils.unchecked(e);
+ }
+ }
+
+ return injector.getInstance(Key.get(typeLiteral));
+ }
+ }
+
}
diff --git a/commons.loader/src/main/java/com/pmease/commons/loader/DefaultPluginManager.java b/commons.loader/src/main/java/com/pmease/commons/loader/DefaultPluginManager.java
index f45ba6c802..8c8ad6d29c 100644
--- a/commons.loader/src/main/java/com/pmease/commons/loader/DefaultPluginManager.java
+++ b/commons.loader/src/main/java/com/pmease/commons/loader/DefaultPluginManager.java
@@ -54,24 +54,6 @@ public class DefaultPluginManager implements PluginManager {
plugin.stop();
}
- public Collection getExtensions(Class extensionPoint) {
- List extensions = new ArrayList();
- for (AbstractPlugin plugin: pluginMap.values()) {
- if (plugin.isEnabled()) {
- Collection> pluginExtensions = plugin.getExtensions();
- if (pluginExtensions != null) {
- for (Object extension: pluginExtensions) {
- if (extension != null && extensionPoint.isAssignableFrom(
- extension.getClass())) {
- extensions.add(extensionPoint.cast(extension));
- }
- }
- }
- }
- }
- return extensions;
- }
-
@Override
public Collection getPlugins() {
return Collections.unmodifiableCollection(pluginMap.values());
diff --git a/commons.loader/src/main/java/com/pmease/commons/loader/PluginManager.java b/commons.loader/src/main/java/com/pmease/commons/loader/PluginManager.java
index b31d512595..6a5fd12bd1 100644
--- a/commons.loader/src/main/java/com/pmease/commons/loader/PluginManager.java
+++ b/commons.loader/src/main/java/com/pmease/commons/loader/PluginManager.java
@@ -6,8 +6,6 @@ import com.pmease.commons.bootstrap.Lifecycle;
public interface PluginManager extends Lifecycle {
- Collection getExtensions(Class extensionPoint);
-
Collection getPlugins();
T getPlugin(Class pluginClass);
diff --git a/commons.shiro/src/main/java/com/pmease/commons/shiro/DefaultFilterChainResolver.java b/commons.shiro/src/main/java/com/pmease/commons/shiro/DefaultFilterChainResolver.java
index 1a020e1adc..96c752181a 100644
--- a/commons.shiro/src/main/java/com/pmease/commons/shiro/DefaultFilterChainResolver.java
+++ b/commons.shiro/src/main/java/com/pmease/commons/shiro/DefaultFilterChainResolver.java
@@ -1,12 +1,13 @@
package com.pmease.commons.shiro;
+import java.util.Set;
+
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
-import com.pmease.commons.loader.PluginManager;
import com.pmease.commons.shiro.extensionpoint.FilterChainConfigurator;
@Singleton
@@ -14,7 +15,7 @@ public class DefaultFilterChainResolver extends PathMatchingFilterChainResolver
@Inject
public DefaultFilterChainResolver(
- PluginManager pluginManager,
+ Set filterChainConfigurators,
BasicAuthenticationFilter basicAuthenticationFilter) {
super();
@@ -23,7 +24,7 @@ public class DefaultFilterChainResolver extends PathMatchingFilterChainResolver
filterChainManager.addFilter("authcBasic", basicAuthenticationFilter);
- for (FilterChainConfigurator configurator: pluginManager.getExtensions(FilterChainConfigurator.class)) {
+ for (FilterChainConfigurator configurator: filterChainConfigurators) {
configurator.configure(filterChainManager);
}
diff --git a/commons.util/src/main/java/com/pmease/commons/util/ClassUtils.java b/commons.util/src/main/java/com/pmease/commons/util/ClassUtils.java
index 859d71e6ff..6910f28edf 100644
--- a/commons.util/src/main/java/com/pmease/commons/util/ClassUtils.java
+++ b/commons.util/src/main/java/com/pmease/commons/util/ClassUtils.java
@@ -4,6 +4,7 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
@@ -27,21 +28,21 @@ public class ClassUtils extends org.apache.commons.lang3.ClassUtils {
*
* @param superClass
* super class (or interface) to match.
- * @param packageClass
+ * @param packageLocatorClass
* find sub classes in the same package as this class. Package will not be searched recursively.
* @return collection of sub classes (not include the super class)
*/
@SuppressWarnings("unchecked")
- public static Collection> findSubClasses(Class superClass, Class> packageClass) {
+ public static Collection> findSubClasses(Class superClass, Class> packageLocatorClass) {
Preconditions.checkNotNull(superClass);
- Preconditions.checkNotNull(packageClass);
+ Preconditions.checkNotNull(packageLocatorClass);
- Collection> classes = new HashSet>();
+ Collection> classes = new HashSet>();
- File location = new File(packageClass.getProtectionDomain()
+ File location = new File(packageLocatorClass.getProtectionDomain()
.getCodeSource().getLocation().getFile());
if (location.isFile()) {
- String packagePath = packageClass.getPackage().getName().replace('.', '/') + "/";
+ String packagePath = packageLocatorClass.getPackage().getName().replace('.', '/') + "/";
JarFile jarFile;
try {
jarFile = new JarFile(location);
@@ -68,14 +69,14 @@ public class ClassUtils extends org.apache.commons.lang3.ClassUtils {
}
}
} else {
- String packagePath = packageClass.getPackage().getName().replace('.', File.separatorChar);
+ String packagePath = packageLocatorClass.getPackage().getName().replace('.', File.separatorChar);
File packageDir = new File(location, packagePath);
if (packageDir.exists()) {
for (File file: packageDir.listFiles()) {
if (file.getName().endsWith(".class")) {
Class clazz;
try {
- String className = packageClass.getPackage().getName() + "." +
+ String className = packageLocatorClass.getPackage().getName() + "." +
StringUtils.substringBeforeLast(file.getName(), ".");
clazz = (Class) superClass.getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
@@ -181,5 +182,9 @@ public class ClassUtils extends org.apache.commons.lang3.ClassUtils {
return null;
}
}
+
+ public static boolean isConcrete(Class> clazz) {
+ return !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers());
+ }
}
diff --git a/commons.util/src/main/java/com/pmease/commons/util/EasyMap.java b/commons.util/src/main/java/com/pmease/commons/util/EasyMap.java
index e403ef0f09..bef1d4cec7 100644
--- a/commons.util/src/main/java/com/pmease/commons/util/EasyMap.java
+++ b/commons.util/src/main/java/com/pmease/commons/util/EasyMap.java
@@ -1,6 +1,7 @@
package com.pmease.commons.util;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
import com.google.common.base.Preconditions;
@@ -28,4 +29,16 @@ public class EasyMap {
return map;
}
+ public static Map