tapestry hibernate integration

This commit is contained in:
robin 2012-05-28 15:30:21 +08:00
parent 78503ebd87
commit b42f22846e
33 changed files with 711 additions and 447 deletions

View File

@ -0,0 +1,71 @@
package com.pmease.commons.hibernate;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Properties;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
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;
@Singleton
public class ConfigurationProvider implements Provider<Configuration> {
private Configuration configuration;
private final PluginManager pluginManager;
private final NamingStrategy namingStrategy;
private final Properties hibernateProperties;
@Inject
public ConfigurationProvider(PluginManager pluginManager, NamingStrategy namingStrategy,
@Hibernate Properties hibernateProperties) {
this.pluginManager = pluginManager;
this.namingStrategy = namingStrategy;
this.hibernateProperties = hibernateProperties;
}
@Override
public synchronized Configuration get() {
if (configuration == null) {
String url = hibernateProperties.getProperty(Environment.URL);
hibernateProperties.setProperty(Environment.URL,
StringUtils.replace(url, "${installDir}", Bootstrap.installDir.getAbsolutePath()));
String encryptedPassword = hibernateProperties.getProperty("hibernate.connection.encrypted_password");
if (StringUtils.isNotBlank(encryptedPassword))
hibernateProperties.setProperty(Environment.PASS, StringUtils.decrypt(encryptedPassword.trim()));
configuration = new Configuration();
configuration.setNamingStrategy(namingStrategy);
Collection<Class<AbstractEntity>> modelClasses =
ClassUtils.findSubClasses(AbstractEntity.class, AbstractEntity.class);
for (Class<AbstractEntity> model: modelClasses) {
if (!Modifier.isAbstract(model.getModifiers()))
configuration.addAnnotatedClass(model);
}
Collection<ModelContribution> contributions =
pluginManager.getExtensions(ModelContribution.class);
for (ModelContribution contribution: contributions) {
for (Class<? extends AbstractEntity> modelClass: contribution.getModelClasses())
configuration.addAnnotatedClass(modelClass);
}
configuration.setProperties(hibernateProperties);
}
return configuration;
}
}

View File

@ -2,6 +2,7 @@ package com.pmease.commons.hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.ImprovedNamingStrategy;
import org.hibernate.cfg.NamingStrategy;
@ -21,8 +22,10 @@ public class HibernateModule extends AbstractPluginModule {
bind(PersistService.class).to(PersistServiceImpl.class);
bind(SessionFactory.class).toProvider(PersistServiceImpl.class);
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(GeneralDaoImpl.class);

View File

@ -1,17 +1,28 @@
package com.pmease.commons.hibernate;
import java.util.Collection;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.commons.loader.AbstractPlugin;
public class HibernatePlugin extends AbstractPlugin {
private final PersistService persistService;
private final HibernateFilter hibernateFilter;
@Inject
public HibernatePlugin(PersistService persistService) {
public HibernatePlugin(PersistService persistService, HibernateFilter hibernateFilter) {
this.persistService = persistService;
this.hibernateFilter = hibernateFilter;
}
@Override
@ -26,7 +37,17 @@ public class HibernatePlugin extends AbstractPlugin {
@Override
public Collection<?> getExtensions() {
return null;
return ImmutableList.of(
new ServletContextConfigurator() {
@Override
public void configure(ServletContextHandler context) {
FilterHolder filterHolder = new FilterHolder(hibernateFilter);
context.addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
}
}
);
}
}

View File

@ -1,71 +1,27 @@
package com.pmease.commons.hibernate;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Properties;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.NamingStrategy;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
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;
@Singleton
public class PersistServiceImpl implements PersistService, Provider<SessionFactory> {
private final PluginManager pluginManager;
private final NamingStrategy namingStrategy;
private final Properties hibernateProperties;
private final ConfigurationProvider configurationProvider;
private volatile SessionFactory sessionFactory;
@Inject
public PersistServiceImpl(PluginManager pluginManager, NamingStrategy namingStrategy,
@Hibernate Properties hibernateProperties) {
this.pluginManager = pluginManager;
this.namingStrategy = namingStrategy;
this.hibernateProperties = hibernateProperties;
String url = hibernateProperties.getProperty(Environment.URL);
hibernateProperties.setProperty(Environment.URL,
StringUtils.replace(url, "${installDir}", Bootstrap.installDir.getAbsolutePath()));
String encryptedPassword = hibernateProperties.getProperty("hibernate.connection.encrypted_password");
if (StringUtils.isNotBlank(encryptedPassword))
hibernateProperties.setProperty(Environment.PASS, StringUtils.decrypt(encryptedPassword.trim()));
public PersistServiceImpl(ConfigurationProvider configurationProvider) {
this.configurationProvider = configurationProvider;
}
public void start() {
Preconditions.checkState(sessionFactory == null);
Configuration cfg = new Configuration();
cfg.setNamingStrategy(namingStrategy);
Collection<Class<AbstractEntity>> modelClasses =
ClassUtils.findSubClasses(AbstractEntity.class, AbstractEntity.class);
for (Class<AbstractEntity> model: modelClasses) {
if (!Modifier.isAbstract(model.getModifiers()))
cfg.addAnnotatedClass(model);
}
Collection<ModelContribution> contributions =
pluginManager.getExtensions(ModelContribution.class);
for (ModelContribution contribution: contributions) {
for (Class<? extends AbstractEntity> modelClass: contribution.getModelClasses())
cfg.addAnnotatedClass(modelClass);
}
cfg.setProperties(hibernateProperties);
sessionFactory = cfg.buildSessionFactory();
sessionFactory = configurationProvider.get().buildSessionFactory();
}
public void stop() {
@ -76,12 +32,10 @@ public class PersistServiceImpl implements PersistService, Provider<SessionFacto
}
}
@Override
public SessionFactory get() {
Preconditions.checkNotNull(sessionFactory);
return sessionFactory;
}
public Properties getHibernateProperties() {
return hibernateProperties;
}
}

View File

@ -0,0 +1,9 @@
package com.pmease.commons.hibernate;
import javax.inject.Provider;
import org.hibernate.Session;
public interface SessionProvider extends Provider<Session> {
}

View File

@ -11,7 +11,7 @@ import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class UnitOfWorkImpl implements UnitOfWork, Provider<Session> {
public class UnitOfWorkImpl implements UnitOfWork, Provider<Session>, SessionProvider {
private final Provider<SessionFactory> sessionFactoryProvider;

View File

@ -1,20 +0,0 @@
package com.pmease.commons.product;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GitServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getOutputStream().println(req.getServletPath());
}
}

View File

@ -2,19 +2,14 @@ package com.pmease.commons.product;
import java.io.File;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Properties;
import javax.inject.Inject;
import javax.servlet.DispatcherType;
import org.apache.tapestry5.TapestryFilter;
import org.apache.tapestry5.internal.InternalConstants;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -37,14 +32,8 @@ public class Plugin extends AbstractPlugin {
private Properties serverProps;
private final HibernateFilter hibernateFilter;
private final TapestryFilter tapestryFilter;
@Inject
public Plugin(HibernateFilter hibernateFilter, TapestryFilter tapestryFilter) {
this.hibernateFilter = hibernateFilter;
this.tapestryFilter = tapestryFilter;
serverProps = FileUtils.loadProperties(new File(Bootstrap.getConfDir(), "server.properties"));
}
@ -70,21 +59,7 @@ public class Plugin extends AbstractPlugin {
File resourceDir = new File(Bootstrap.installDir, "resource");
context.setResourceBase(resourceDir.getAbsolutePath());
ServletHolder servletHolder = JettyUtils.createResourceServletHolder();
for (String path: resourceDir.list())
context.addServlet(servletHolder, "/" + path);
context.addServlet(JettyUtils.createResourceServletHolder(), "/");
FilterHolder filterHolder = new FilterHolder(hibernateFilter);
context.addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
filterHolder = new FilterHolder(tapestryFilter);
filterHolder.setName("app");
context.setInitParameter(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM, Plugin.class.getPackage().getName());
context.addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
context.addServlet(GitServlet.class, "*.git");
}
},

View File

@ -7,6 +7,7 @@ import com.pmease.commons.bootstrap.Bootstrap;
import com.pmease.commons.hibernate.Hibernate;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
import com.pmease.commons.tapestry.Tapestry;
import com.pmease.commons.util.FileUtils;
public class PluginModule extends AbstractPluginModule {
@ -18,6 +19,7 @@ public class PluginModule extends AbstractPluginModule {
Properties hibernateProps = FileUtils.loadProperties(
new File(Bootstrap.installDir, "conf/hibernate.properties"));
bind(Properties.class).annotatedWith(Hibernate.class).toInstance(hibernateProps);
bind(Package.class).annotatedWith(Tapestry.class).toInstance(Plugin.class.getPackage());
}
@Override

View File

@ -1,18 +0,0 @@
package com.pmease.commons.product.components;
public class LoginForm {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
void onActionFromLogin() {
}
}

View File

@ -1,3 +0,0 @@
<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd" xmlns:p="tapestry:parameter">
<t:actionlink t:id="login">login as ${userName}</t:actionlink>
</t:container>

View File

@ -1,28 +0,0 @@
package com.pmease.commons.product.model;
import javax.persistence.Entity;
@Entity
public class Repository {
private String name;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -1,19 +1,18 @@
package com.pmease.commons.product.model;
public class User {
import javax.persistence.Entity;
import com.pmease.commons.hibernate.AbstractEntity;
@Entity
public class User extends AbstractEntity {
private static final long serialVersionUID = 1L;
private String name;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
@ -21,5 +20,13 @@ public class User {
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -1,87 +1,31 @@
package com.pmease.commons.product.pages;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import javax.inject.Inject;
import org.apache.tapestry5.Block;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.services.ComponentSource;
import org.apache.tapestry5.annotations.SessionState;
import com.pmease.commons.product.model.Repository;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.commons.product.model.User;
public class Index {
@Inject
private GeneralDao generalDao;
@SessionState
@Property
private User user;
@Property
private Repository repository;
@Inject
private ComponentSource componentSource;
@Inject
private Block emailBlock;
@Inject
private Block loginBlock;
public List<User> getUsers() {
List<User> users = new ArrayList<User>();
User user = new User();
user.setName("robin");
public void onActionFromBtn() {
user.setName(new Date().toString());
user.setEmail("robin@pmease.com");
users.add(user);
user = new User();
user.setName("steve");
user.setEmail("steve@pmease.com");
users.add(user);
return users;
generalDao.save(user);
}
void onActivate(String userName) {
user = new User();
user.setName(userName);
}
void onActivate(String userName, String repositoryName) {
user = new User();
user.setName(userName);
repository = new Repository();
repository.setName(repositoryName);
}
public UserViewer getUserViewer() {
UserViewer viewer = (UserViewer) componentSource.getPage(UserViewer.class);
viewer.setUser(user);
return viewer;
}
public RepositoryViewer getRepositoryViewer() {
RepositoryViewer viewer = (RepositoryViewer) componentSource.getPage(RepositoryViewer.class);
viewer.setUser(user);
viewer.setRepository(repository);
return viewer;
}
Object onActionFromEmailLink(String userName) {
user = new User();
user.setName(userName);
user.setEmail(userName + "@pmease.com");
return emailBlock;
}
Object onActionFromLoginLink(String userName) {
user = new User();
user.setName(userName);
user.setEmail(userName + "@pmease.com");
return loginBlock;
public void onActivate(User user) {
this.user = user;
}
}

View File

@ -1,40 +1,10 @@
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd" xmlns:p="tapestry:parameter">
<head>
<title>Workshop</title>
</head>
<body>
<t:if test="user">
<t:if test="repository">
<t:delegate to="repositoryViewer"/>
<p:else>
<t:delegate to="userViewer"/>
</p:else>
</t:if>
<p:else>
<t:grid source="users" row="user" add="action">
<p:nameCell>
<t:pagelink page="index" context="user.name">${user.name}</t:pagelink>
</p:nameCell>
<p:emailCell>
<t:jquery.dialogajaxlink t:id="emailLink" t:dialog="myDialog" t:zone="myZone" context="user.name">open</t:jquery.dialogajaxlink>
</p:emailCell>
<p:actionCell>
<t:jquery.dialogajaxlink t:id="loginLink" t:dialog="myDialog" t:zone="myZone" context="user.name">login</t:jquery.dialogajaxlink>
</p:actionCell>
</t:grid>
</p:else>
</t:if>
<t:jquery.dialog t:clientId="myDialog">
<t:zone id="myZone"></t:zone>
</t:jquery.dialog>
<t:block t:id="emailBlock">
${user.email}
</t:block>
<t:block t:id="loginBlock">
<t:loginForm></t:loginForm>
</t:block>
</body>
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"
xmlns:p="tapestry:parameter">
<head>
<title>Hello world</title>
</head>
<body>
<t:actionlink t:id="btn">add</t:actionlink>
${user.name}
</body>
</html>

View File

@ -1,6 +0,0 @@
package com.pmease.commons.product.pages;
public class Preference {
}

View File

@ -1,7 +0,0 @@
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd" xmlns:p="tapestry:parameter">
<head>
<title>Preferences</title>
</head>
<body>
</body>
</html>

View File

@ -1,28 +0,0 @@
package com.pmease.commons.product.pages;
import com.pmease.commons.product.model.Repository;
import com.pmease.commons.product.model.User;
public class RepositoryViewer {
private User user;
private Repository repository;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Repository getRepository() {
return repository;
}
public void setRepository(Repository repository) {
this.repository = repository;
}
}

View File

@ -1,3 +0,0 @@
<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd" xmlns:p="tapestry:parameter">
${user.name} - ${repository.name}
</t:container>

View File

@ -1,42 +0,0 @@
package com.pmease.commons.product.pages;
import java.util.ArrayList;
import java.util.List;
import org.apache.tapestry5.annotations.Property;
import com.pmease.commons.product.model.Repository;
import com.pmease.commons.product.model.User;
public class UserViewer {
private User user;
@SuppressWarnings("unused")
@Property
private Repository repository;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Repository> getRepositories() {
List<Repository> repositories = new ArrayList<Repository>();
Repository repository = new Repository();
repository.setName("QuickBuild");
repository.setDescription("QuickBuild Trunk");
repositories.add(repository);
repository = new Repository();
repository.setName("Gitop");
repository.setDescription("Gitop Trunk");
repositories.add(repository);
return repositories;
}
}

View File

@ -1,8 +0,0 @@
<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd" xmlns:p="tapestry:parameter">
<div>${user.name}</div>
<t:grid source="repositories" row="repository">
<p:nameCell>
<t:pagelink page="index" context="[user.name, repository.name]">${repository.name}</t:pagelink>
</p:nameCell>
</t:grid>
</t:container>

View File

@ -27,12 +27,7 @@
<dependencies>
<dependency>
<groupId>com.pmease</groupId>
<artifactId>commons.loader</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>com.pmease</groupId>
<artifactId>commons.jetty</artifactId>
<artifactId>commons.hibernate</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
@ -50,15 +45,10 @@
</exclusion>
</exclusions>
</dependency>
<!-- dependency>
<groupId>org.apache.tapestry</groupId>
<artifactId>tapestry-hibernate</artifactId>
<version>5.3.3</version>
</dependency-->
<dependency>
<groupId>org.got5</groupId>
<artifactId>tapestry5-jquery</artifactId>
<version>3.0.0</version>
<version>3.2.0</version>
</dependency>
</dependencies>

View File

@ -11,7 +11,7 @@ import org.apache.tapestry5.TapestryFilter;
import com.pmease.commons.loader.PluginManager;
import com.pmease.commons.tapestry.extensionpoints.TapestryConfiguratorProvider;
import com.pmease.commons.tapestry.services.AppModule;
import com.pmease.commons.tapestry.services.TapestryConfigurator;
@Singleton
public class CustomTapestryFilter extends TapestryFilter {
@ -23,7 +23,7 @@ public class CustomTapestryFilter extends TapestryFilter {
protected Class<?>[] provideExtraModuleClasses(ServletContext context) {
List<Class<?>> moduleClasses = new ArrayList<Class<?>>();
moduleClasses.add(AppModule.class);
moduleClasses.add(TapestryConfigurator.class);
for (TapestryConfiguratorProvider each : pluginManager
.getExtensions(TapestryConfiguratorProvider.class))
moduleClasses.add(each.getTapestryConfigurator());

View File

@ -0,0 +1,16 @@
package com.pmease.commons.tapestry;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import com.google.inject.BindingAnnotation;
@Retention(RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@BindingAnnotation
public @interface Tapestry {
}

View File

@ -1,14 +1,47 @@
package com.pmease.commons.tapestry;
import java.util.Collection;
import java.util.EnumSet;
import javax.inject.Inject;
import javax.servlet.DispatcherType;
import org.apache.tapestry5.TapestryFilter;
import org.apache.tapestry5.internal.InternalConstants;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import com.google.common.collect.ImmutableList;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.commons.loader.AbstractPlugin;
public class TapestryPlugin extends AbstractPlugin {
private final TapestryFilter tapestryFilter;
private final Package appPackage;
@Inject
public TapestryPlugin(TapestryFilter tapestryFilter, @Tapestry Package appPackage) {
this.tapestryFilter = tapestryFilter;
this.appPackage = appPackage;
}
@Override
public Collection<?> getExtensions() {
return null;
return ImmutableList.of(
new ServletContextConfigurator() {
@Override
public void configure(ServletContextHandler context) {
FilterHolder filterHolder = new FilterHolder(tapestryFilter);
filterHolder.setName("app");
context.setInitParameter(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM, appPackage.getName());
context.addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
}
}
);
}
}

View File

@ -0,0 +1,63 @@
package com.pmease.commons.tapestry.persistence;
import java.io.Serializable;
import org.apache.tapestry5.internal.services.SessionApplicationStatePersistenceStrategy;
import org.apache.tapestry5.services.ApplicationStateCreator;
import org.apache.tapestry5.services.Request;
import org.hibernate.HibernateException;
import com.pmease.commons.hibernate.SessionProvider;
public class EntityApplicationStatePersistenceStrategy extends SessionApplicationStatePersistenceStrategy {
private final SessionProvider sessionProvider;
public EntityApplicationStatePersistenceStrategy(Request request,
SessionProvider sessionProvider) {
super(request);
this.sessionProvider = sessionProvider;
}
@SuppressWarnings("unchecked")
public <T> T get(Class<T> ssoClass, ApplicationStateCreator<T> creator) {
final Object persistedValue = getOrCreate(ssoClass, creator);
if (persistedValue instanceof PersistedEntity) {
final PersistedEntity persisted = (PersistedEntity) persistedValue;
Object restored = persisted.restore(sessionProvider.get());
// shall we maybe throw an exception instead?
if (restored == null) {
set(ssoClass, null);
return (T) getOrCreate(ssoClass, creator);
}
return (T) restored;
}
return (T) persistedValue;
}
public <T> void set(Class<T> ssoClass, T sso) {
final String key = buildKey(ssoClass);
Object entity;
if (sso != null) {
try {
final String entityName = sessionProvider.get().getEntityName(sso);
final Serializable id = sessionProvider.get().getIdentifier(sso);
entity = new PersistedEntity(entityName, id);
} catch (final HibernateException ex) {
entity = sso;
}
} else {
entity = sso;
}
getSession().setAttribute(key, entity);
}
}

View File

@ -0,0 +1,130 @@
package com.pmease.commons.tapestry.persistence;
import java.util.List;
import org.apache.tapestry5.grid.GridDataSource;
import org.apache.tapestry5.grid.SortConstraint;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
/**
* A simple implementation of {@link org.apache.tapestry5.grid.GridDataSource}
* based on a Hibernate Session and a known entity class. This implementation
* does support multiple {@link org.apache.tapestry5.grid.SortConstraint sort
* constraints}; however it assumes a direct mapping from sort constraint
* property to Hibernate property.
* <p/>
* This class is <em>not</em> thread-safe; it maintains internal state.
* <p/>
* Typically, an instance of this object is created fresh as needed (that is, it
* is not stored between requests).
*/
public class EntityDataSource implements GridDataSource {
private final Session session;
private final Class<?> entityType;
private int startIndex;
private List<?> preparedResults;
public EntityDataSource(Session session, Class<?> entityType) {
assert session != null;
assert entityType != null;
this.session = session;
this.entityType = entityType;
}
/**
* Returns the total number of rows for the configured entity type.
*/
public int getAvailableRows() {
Criteria criteria = session.createCriteria(entityType);
applyAdditionalConstraints(criteria);
criteria.setProjection(Projections.rowCount());
Number result = (Number) criteria.uniqueResult();
return result.intValue();
}
/**
* Prepares the results, performing a query (applying the sort results, and
* the provided start and end index). The results can later be obtained from
* {@link #getRowValue(int)} .
*
* @param startIndex
* index, from zero, of the first item to be retrieved
* @param endIndex
* index, from zero, of the last item to be retrieved
* @param sortConstraints
* zero or more constraints used to set the order of the returned
* values
*/
public void prepare(int startIndex, int endIndex,
List<SortConstraint> sortConstraints) {
assert sortConstraints != null;
Criteria crit = session.createCriteria(entityType);
crit.setFirstResult(startIndex)
.setMaxResults(endIndex - startIndex + 1);
for (SortConstraint constraint : sortConstraints) {
String propertyName = constraint.getPropertyModel()
.getPropertyName();
switch (constraint.getColumnSort()) {
case ASCENDING:
crit.addOrder(Order.asc(propertyName));
break;
case DESCENDING:
crit.addOrder(Order.desc(propertyName));
break;
default:
}
}
applyAdditionalConstraints(crit);
this.startIndex = startIndex;
preparedResults = crit.list();
}
/**
* Invoked after the main criteria has been set up (firstResult, maxResults
* and any sort contraints). This gives subclasses a chance to apply
* additional constraints before the list of results is obtained from the
* criteria. This implementation does nothing and may be overridden.
*/
protected void applyAdditionalConstraints(Criteria crit) {
}
/**
* Returns a row value at the given index (which must be within the range
* defined by the call to {@link #prepare(int, int, java.util.List)} ).
*
* @param index
* of object
* @return object at that index
*/
public Object getRowValue(int index) {
return preparedResults.get(index - startIndex);
}
/**
* Returns the entity type, as provided via the constructor.
*/
public Class<?> getRowType() {
return entityType;
}
}

View File

@ -0,0 +1,35 @@
package com.pmease.commons.tapestry.persistence;
import java.io.Serializable;
import org.apache.tapestry5.internal.services.AbstractSessionPersistentFieldStrategy;
import org.apache.tapestry5.services.Request;
import com.pmease.commons.hibernate.SessionProvider;
public class EntityPersistentFieldStrategy extends AbstractSessionPersistentFieldStrategy {
private final SessionProvider sessionProvider;
public EntityPersistentFieldStrategy(SessionProvider sessionProvider, Request request) {
super("entity:", request);
this.sessionProvider = sessionProvider;
}
@Override
protected Object convertApplicationValueToPersisted(Object newValue) {
String entityName = sessionProvider.get().getEntityName(newValue);
Serializable id = sessionProvider.get().getIdentifier(newValue);
return new PersistedEntity(entityName, id);
}
@Override
protected Object convertPersistedToApplicationValue(Object persistedValue) {
PersistedEntity persisted = (PersistedEntity) persistedValue;
return persisted.restore(sessionProvider.get());
}
}

View File

@ -0,0 +1,93 @@
package com.pmease.commons.tapestry.persistence;
import java.io.Serializable;
import javax.inject.Provider;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.services.PropertyAccess;
import org.apache.tapestry5.ioc.services.PropertyAdapter;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.hibernate.Session;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.slf4j.Logger;
import com.pmease.commons.hibernate.SessionProvider;
public final class EntityValueEncoder<E> implements ValueEncoder<E> {
private final Class<E> entityClass;
private final Provider<Session> sessionProvider;
private final TypeCoercer typeCoercer;
private final PropertyAdapter propertyAdapter;
private final Logger logger;
public EntityValueEncoder(Class<E> entityClass,
PersistentClass persistentClass, SessionProvider sessionProvider,
PropertyAccess propertyAccess, TypeCoercer typeCoercer,
Logger logger) {
this.sessionProvider = sessionProvider;
this.entityClass = entityClass;
this.typeCoercer = typeCoercer;
this.logger = logger;
Property property = persistentClass.getIdentifierProperty();
propertyAdapter = propertyAccess.getAdapter(this.entityClass)
.getPropertyAdapter(property.getName());
}
public String toClient(E value) {
if (value == null)
return null;
Object id = propertyAdapter.get(value);
if (id == null) {
return null;
}
return typeCoercer.coerce(id, String.class);
}
@SuppressWarnings("unchecked")
public E toValue(String clientValue) {
if (InternalUtils.isBlank(clientValue))
return null;
Object id = null;
try {
id = typeCoercer.coerce(clientValue, propertyAdapter.getType());
} catch (Exception ex) {
throw new RuntimeException(
String.format(
"Exception converting '%s' to instance of %s (id type for entity %s): %s",
clientValue, propertyAdapter.getType().getName(),
entityClass.getName(), InternalUtils.toMessage(ex)),
ex);
}
Serializable ser = (Serializable) id;
E result = (E) sessionProvider.get().get(entityClass, ser);
if (result == null) {
// We don't identify the entity type in the message because the
// logger is based on the
// entity type.
logger.error(String
.format("Unable to convert client value '%s' into an entity instance.",
clientValue));
}
return result;
}
}

View File

@ -0,0 +1,34 @@
package com.pmease.commons.tapestry.persistence;
import org.apache.tapestry5.annotations.ImmutableSessionPersistedObject;
import org.hibernate.Session;
import java.io.Serializable;
/**
* Encapsulates a Hibernate entity name with an entity id.
*/
@ImmutableSessionPersistedObject
public class PersistedEntity implements Serializable {
private static final long serialVersionUID = 1L;
private final String entityName;
private final Serializable id;
public PersistedEntity(String entityName, Serializable id) {
this.entityName = entityName;
this.id = id;
}
public Object restore(Session session) {
System.out.println("loading " + entityName + " " + id);
return session.load(entityName, id);
}
@Override
public String toString() {
return String.format("<PersistedEntity: %s(%s)>", entityName, id);
}
}

View File

@ -0,0 +1,5 @@
package com.pmease.commons.tapestry.persistence;
public class PersistenceConstants {
public static final String ENTITY = "entity";
}

View File

@ -1,73 +0,0 @@
package com.pmease.commons.tapestry.services;
import org.apache.commons.lang.StringUtils;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.ioc.Configuration;
import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.ObjectProvider;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.annotations.Local;
import org.apache.tapestry5.ioc.services.SymbolSource;
import org.apache.tapestry5.services.AssetSource;
import org.apache.tapestry5.services.LibraryMapping;
import org.apache.tapestry5.services.transform.InjectionProvider2;
import org.eclipse.jetty.servlet.ServletMapping;
import com.google.inject.Injector;
import com.pmease.commons.bootstrap.Bootstrap;
import com.pmease.commons.jetty.JettyPlugin;
import com.pmease.commons.loader.AppLoader;
import com.pmease.commons.loader.PluginManager;
import com.pmease.commons.tapestry.DisabledInjectionProvider;
import com.pmease.commons.tapestry.GuiceObjectProvider;
import com.pmease.commons.tapestry.TapestryModule;
public class AppModule {
public static void contributeApplicationDefaults(MappedConfiguration<String, Object> configuration) {
configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en");
configuration.add(SymbolConstants.PRODUCTION_MODE, !Bootstrap.isSandboxMode() || Bootstrap.isProdMode());
}
public static Injector buildGuiceInjector() {
return AppLoader.injector;
}
public static void contributeMasterObjectProvider(@Local Injector injector,
OrderedConfiguration<ObjectProvider> configuration) {
configuration.add("guiceProvider", new GuiceObjectProvider(injector), "after:*");
}
public static void contributeInjectionProvider(OrderedConfiguration<InjectionProvider2> configuration,
SymbolSource symbolSource, AssetSource assetSource) {
configuration.overrideInstance("Named", DisabledInjectionProvider.class);
}
public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration,
PluginManager pluginManager) {
configuration.add(new LibraryMapping("commons", TapestryModule.class.getPackage().getName()));
for (LibraryMapping mapping: pluginManager.getExtensions(LibraryMapping.class))
configuration.add(mapping);
}
public static void contributeIgnoredPathsFilter(Configuration<String> configuration, PluginManager pluginManager) {
JettyPlugin jettyPlugin = pluginManager.getPlugin(JettyPlugin.class);
for (ServletMapping mapping: jettyPlugin.getContext().getServletHandler().getServletMappings()) {
for (String pathSpec: mapping.getPathSpecs()) {
if (!pathSpec.equals("/") && !pathSpec.equals("/*")) {
pathSpec = pathSpec.replace(".", "\\.");
if (pathSpec.endsWith("/*")) {
pathSpec = StringUtils.stripEnd(pathSpec, "/*");
configuration.add(pathSpec);
configuration.add(pathSpec + "/.*");
} else {
pathSpec = pathSpec.replace("*", ".*");
configuration.add(pathSpec);
}
}
}
}
}
}

View File

@ -0,0 +1,145 @@
package com.pmease.commons.tapestry.services;
import java.util.Iterator;
import org.apache.commons.lang.StringUtils;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.ioc.Configuration;
import org.apache.tapestry5.ioc.LoggerSource;
import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.ObjectProvider;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.annotations.Local;
import org.apache.tapestry5.ioc.services.PropertyAccess;
import org.apache.tapestry5.ioc.services.SymbolSource;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.services.ApplicationStateContribution;
import org.apache.tapestry5.services.ApplicationStatePersistenceStrategy;
import org.apache.tapestry5.services.AssetSource;
import org.apache.tapestry5.services.LibraryMapping;
import org.apache.tapestry5.services.PersistentFieldStrategy;
import org.apache.tapestry5.services.ValueEncoderFactory;
import org.apache.tapestry5.services.transform.InjectionProvider2;
import org.eclipse.jetty.servlet.ServletMapping;
import org.hibernate.Session;
import org.hibernate.mapping.PersistentClass;
import com.google.inject.Injector;
import com.pmease.commons.bootstrap.Bootstrap;
import com.pmease.commons.hibernate.SessionProvider;
import com.pmease.commons.jetty.JettyPlugin;
import com.pmease.commons.loader.AppLoader;
import com.pmease.commons.loader.PluginManager;
import com.pmease.commons.tapestry.DisabledInjectionProvider;
import com.pmease.commons.tapestry.GuiceObjectProvider;
import com.pmease.commons.tapestry.TapestryModule;
import com.pmease.commons.tapestry.persistence.EntityApplicationStatePersistenceStrategy;
import com.pmease.commons.tapestry.persistence.EntityPersistentFieldStrategy;
import com.pmease.commons.tapestry.persistence.EntityValueEncoder;
import com.pmease.commons.tapestry.persistence.PersistenceConstants;
public class TapestryConfigurator {
public static void contributeApplicationDefaults(MappedConfiguration<String, Object> configuration) {
configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en");
configuration.add(SymbolConstants.PRODUCTION_MODE, !Bootstrap.isSandboxMode() || Bootstrap.isProdMode());
}
public static Injector buildGuiceInjector() {
return AppLoader.injector;
}
public static void contributeMasterObjectProvider(@Local Injector injector,
OrderedConfiguration<ObjectProvider> configuration) {
configuration.add("guiceProvider", new GuiceObjectProvider(injector), "after:*");
}
public static void contributeInjectionProvider(OrderedConfiguration<InjectionProvider2> configuration,
SymbolSource symbolSource, AssetSource assetSource) {
configuration.overrideInstance("Named", DisabledInjectionProvider.class);
}
public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration,
PluginManager pluginManager) {
configuration.add(new LibraryMapping("commons", TapestryModule.class.getPackage().getName()));
for (LibraryMapping mapping: pluginManager.getExtensions(LibraryMapping.class))
configuration.add(mapping);
}
public static void contributeIgnoredPathsFilter(Configuration<String> configuration, PluginManager pluginManager) {
JettyPlugin jettyPlugin = pluginManager.getPlugin(JettyPlugin.class);
for (ServletMapping mapping: jettyPlugin.getContext().getServletHandler().getServletMappings()) {
for (String pathSpec: mapping.getPathSpecs()) {
if (!pathSpec.equals("/") && !pathSpec.equals("/*")) {
pathSpec = pathSpec.replace(".", "\\.");
if (pathSpec.endsWith("/*")) {
pathSpec = StringUtils.stripEnd(pathSpec, "/*");
configuration.add(pathSpec);
configuration.add(pathSpec + "/.*");
} else {
pathSpec = pathSpec.replace("*", ".*");
configuration.add(pathSpec);
}
}
}
}
}
@SuppressWarnings("rawtypes")
public static void contributeValueEncoderSource(MappedConfiguration<Class<?>, ValueEncoderFactory> configuration,
final org.hibernate.cfg.Configuration hibernateCfg, final SessionProvider sessionProvider,
final TypeCoercer typeCoercer, final PropertyAccess propertyAccess, final LoggerSource loggerSource) {
Iterator<PersistentClass> mappings = hibernateCfg.getClassMappings();
while (mappings.hasNext()) {
final PersistentClass persistentClass = mappings.next();
final Class<?> entityClass = persistentClass.getMappedClass();
if (entityClass != null) {
ValueEncoderFactory<?> factory = new ValueEncoderFactory() {
@SuppressWarnings("unchecked")
public ValueEncoder create(Class type) {
return new EntityValueEncoder(entityClass, persistentClass, sessionProvider, propertyAccess,
typeCoercer, loggerSource.getLogger(entityClass));
}
};
configuration.add(entityClass, factory);
}
}
}
public static void contributePersistentFieldManager(MappedConfiguration<String,
PersistentFieldStrategy> configuration) {
configuration.addInstance(PersistenceConstants.ENTITY, EntityPersistentFieldStrategy.class);
}
/**
* Contributes the following strategy:
* <dl>
* <dt>entity</dt>
* <dd>Stores the id of the entity and reloads from the {@link Session}</dd>
* </dl>
*/
public void contributeApplicationStatePersistenceStrategySource(
MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration) {
configuration.addInstance(PersistenceConstants.ENTITY, EntityApplicationStatePersistenceStrategy.class);
}
@SuppressWarnings("rawtypes")
public static void contributeApplicationStateManager(
MappedConfiguration<Class, ApplicationStateContribution> configuration,
org.hibernate.cfg.Configuration hibernateCfg) {
Iterator<PersistentClass> mappings = hibernateCfg.getClassMappings();
while (mappings.hasNext()) {
final PersistentClass persistentClass = mappings.next();
final Class entityClass = persistentClass.getMappedClass();
configuration.add(entityClass, new ApplicationStateContribution(PersistenceConstants.ENTITY));
}
}
}