diff --git a/SPR-13886/README.md b/SPR-13886/README.md new file mode 100755 index 00000000..0d65d56d --- /dev/null +++ b/SPR-13886/README.md @@ -0,0 +1,37 @@ +## Spring MVC project with XML config + +This is a simple template for creating issue reproduction projects per +the [README in the root of this repository](https://github.com/spring-projects/spring-framework-issues#readme). +Please review that document before starting. + +As described at the link above, do not edit this project directly! Rather +use the `./create-repro-project.sh` script to create a fresh copy to +a new directory having the same name as the JIRA issue you're trying +to reproduce and edit from there. + +## Deploying + +It is possible to deploy your application directly from the command-line +using maven. See the next two sections on Cargo and Jetty. + +### Cargo + +You can deploy with the [Cargo Maven plugin](http://cargo.codehaus.org/) which +supports a wide [range of servers](http://cargo.codehaus.org/Containers). +The required command is `mvn package cargo:run`. + +By default Cargo is configured to start with `Tomcat 8` but you can easily +edit the plugin settings in `pom.xml` to switch to a different server +and version. The pom.xml or to switch to debug settings. + +### Jetty + +You can also deploy with the +[Jetty Maven plugin](http://www.eclipse.org/jetty/documentation/current/jetty-maven-plugin.html). +The required command is `mvn jetty:run` or `mvnDebug jetty:run`. + +## Logging + +This project contains a `log4j.properties` file in `src/main/resources` that you +may wish to configure to emit more detailed logging. The root logger is set to +`INFO` and a custom `org.springframework.web` logger is set to `DEBUG`. \ No newline at end of file diff --git a/SPR-13886/pom.xml b/SPR-13886/pom.xml new file mode 100755 index 00000000..1572dd40 --- /dev/null +++ b/SPR-13886/pom.xml @@ -0,0 +1,398 @@ + + 4.0.0 + org.springframework.issues + SPR-13886 + 1.0-SNAPSHOT + war + + + UTF-8 + 1.6 + 4.2.4.RELEASE + 1.7.13 + + 1.8.8 + 5.0.7.Final + 5.2.2.Final + + + + + + org.springframework + spring-framework-bom + ${spring.version} + pom + import + + + + + + + + org.springframework + spring-context + + + + commons-logging + commons-logging + + + + + org.springframework + spring-jdbc + runtime + + + org.springframework + spring-orm + + + org.springframework + spring-web + runtime + + + + org.springframework + spring-tx + runtime + + + org.springframework + spring-aspects + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + runtime + + + org.slf4j + slf4j-log4j12 + ${slf4j.version} + runtime + + + log4j + log4j + 1.2.17 + runtime + + + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + + org.aspectj + aspectjrt + ${aspectj.version} + + + org.aspectj + aspectjweaver + ${aspectj.version} + runtime + + + + org.hsqldb + hsqldb + 2.3.3 + + + org.hibernate + hibernate-core + ${hibernate-orm.version} + runtime + + + org.hibernate + hibernate-entitymanager + ${hibernate-orm.version} + runtime + + + xml-apis + xml-apis + + + + + org.hibernate + hibernate-envers + ${hibernate-orm.version} + + + org.javassist + javassist + 3.20.0-GA + + + org.hibernate.javax.persistence + hibernate-jpa-2.1-api + 1.0.0.Final + + + org.jadira.usertype + usertype.core + 5.0.0.GA + runtime + + + + javax.validation + validation-api + 1.1.0.Final + + + org.hibernate + hibernate-validator + ${hibernate-validator.version} + + + org.hibernate + hibernate-validator-annotation-processor + ${hibernate-validator.version} + provided + + + + + + + + + + + + + + + + + + + junit + junit + 4.12 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + install + install + + sources + + + + + + org.apache.maven.plugins + maven-eclipse-plugin + 2.8 + + true + false + 2.0 + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + + **/*Tests.java + **/*Test.java + + + **/*Abstract*.java + + + + + org.eclipse.jetty + jetty-maven-plugin + 9.3.3.v20150827 + + + org.codehaus.cargo + cargo-maven2-plugin + 1.4.18 + + + + 8080 + medium + -Xms96m -Xmx512m -Djava.awt.headless=true + + + + + tomcat8x + + http://archive.apache.org/dist/tomcat/tomcat-8/v8.0.30/bin/apache-tomcat-8.0.30.zip + + + + + + org.codehaus.mojo + aspectj-maven-plugin + 1.8 + + 1.8 + 1.8 + 1.8 + true + true + + + org.springframework + spring-aspects + + + + + + compile + + compile + + + + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + + + + + spring-maven-snapshot + Springframework Maven Snapshot Repository + http://repo.spring.io/snapshot + + true + + + + \ No newline at end of file diff --git a/SPR-13886/src/main/java/org/springframework/issues/.gitignore b/SPR-13886/src/main/java/org/springframework/issues/.gitignore new file mode 100755 index 00000000..e69de29b diff --git a/SPR-13886/src/main/java/org/springframework/issues/entity/Manager.java b/SPR-13886/src/main/java/org/springframework/issues/entity/Manager.java new file mode 100755 index 00000000..db09b6dd --- /dev/null +++ b/SPR-13886/src/main/java/org/springframework/issues/entity/Manager.java @@ -0,0 +1,50 @@ +package org.springframework.issues.entity; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.Size; + +@Entity +@Table(name = "managers") +public final class Manager implements Serializable { + @Id + @Column(name = "user_id", length = 15, nullable = false) + @Size(max = 15) + private String userId; + + @Column(name = "name", length = 255, nullable = false) + @Size(max = 255) + private String name; + + @Column(name = "email", length = 255, nullable = false) + @Size(max = 255) + private String email; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} \ No newline at end of file diff --git a/SPR-13886/src/main/java/org/springframework/issues/entity/Person.java b/SPR-13886/src/main/java/org/springframework/issues/entity/Person.java new file mode 100755 index 00000000..a1cfce7d --- /dev/null +++ b/SPR-13886/src/main/java/org/springframework/issues/entity/Person.java @@ -0,0 +1,71 @@ +package org.springframework.issues.entity; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.PostUpdate; +import javax.persistence.Table; +import javax.validation.constraints.Size; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Configurable; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.issues.event.PersonUpdatedEvent; + +@Entity +@Table(name = "persons") +@Configurable +public final class Person implements Serializable { + + // injected by AspectJ + @Autowired + private transient ApplicationEventPublisher eventPublisher; + + @PostUpdate + void postUpdate() { + // the only way I could find to make sure this event is triggered when the entity really has 'dirty' properties + eventPublisher.publishEvent(new PersonUpdatedEvent(this)); + } + + @Id + @Column(name = "user_id", length = 15, nullable = false) + @Size(max = 15) + private String userId; + + @Column(name = "email", length = 255, nullable = false) + @Size(max = 255) + private String email; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "manager_id", nullable = true) + private Manager manager; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Manager getManager() { + return manager; + } + + public void setManager(Manager manager) { + this.manager = manager; + } +} \ No newline at end of file diff --git a/SPR-13886/src/main/java/org/springframework/issues/event/PersonUpdatedEvent.java b/SPR-13886/src/main/java/org/springframework/issues/event/PersonUpdatedEvent.java new file mode 100755 index 00000000..8f7d1cf0 --- /dev/null +++ b/SPR-13886/src/main/java/org/springframework/issues/event/PersonUpdatedEvent.java @@ -0,0 +1,15 @@ +package org.springframework.issues.event; + +import org.springframework.context.ApplicationEvent; +import org.springframework.issues.entity.Person; + +public class PersonUpdatedEvent extends ApplicationEvent { + + public PersonUpdatedEvent(Person instance) { + super(instance); + } + + public Person getPerson() { + return (Person) super.getSource(); + } +} \ No newline at end of file diff --git a/SPR-13886/src/main/java/org/springframework/issues/hibernate/HibernateClassBypassingLoadTimeWeaver.java b/SPR-13886/src/main/java/org/springframework/issues/hibernate/HibernateClassBypassingLoadTimeWeaver.java new file mode 100755 index 00000000..6a046193 --- /dev/null +++ b/SPR-13886/src/main/java/org/springframework/issues/hibernate/HibernateClassBypassingLoadTimeWeaver.java @@ -0,0 +1,61 @@ +package org.springframework.issues.hibernate; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.security.ProtectionDomain; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.instrument.classloading.LoadTimeWeaver; +import org.springframework.util.Assert; + +public class HibernateClassBypassingLoadTimeWeaver implements LoadTimeWeaver { + + private final LoadTimeWeaver loadTimeWeaver; + + public HibernateClassBypassingLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) { + Assert.notNull(loadTimeWeaver); + this.loadTimeWeaver = loadTimeWeaver; + } + + @Override + public ClassLoader getInstrumentableClassLoader() { + return loadTimeWeaver.getInstrumentableClassLoader(); + } + + @Override + public ClassLoader getThrowawayClassLoader() { + return loadTimeWeaver.getThrowawayClassLoader(); + } + + @Override + public void addTransformer(ClassFileTransformer transformer) { + loadTimeWeaver.addTransformer(new HibernateClassBypassingClassFileTransformer(transformer)); + } + + /** + * ClassFileTransformer decorator that suppresses processing of Hibernate + * classes in order to avoid potential LinkageErrors. + * @see org.springframework.context.annotation.LoadTimeWeavingConfiguration + */ + private static class HibernateClassBypassingClassFileTransformer implements ClassFileTransformer { + private static final Logger LOGGER = LoggerFactory.getLogger(HibernateClassBypassingClassFileTransformer.class); + + private final ClassFileTransformer delegate; + + public HibernateClassBypassingClassFileTransformer(ClassFileTransformer delegate) { + this.delegate = delegate; + } + + @Override + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) + throws IllegalClassFormatException { + + if (className.startsWith("org.hibernate") || className.startsWith("org/hibernate") || className.startsWith("javassist")) { + LOGGER.trace("Bypassing class: {}", className); + return classfileBuffer; + } + return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); + } + } +} \ No newline at end of file diff --git a/SPR-13886/src/main/java/org/springframework/issues/hibernate/HibernateLoadTimeWeaverAwareProcessor.java b/SPR-13886/src/main/java/org/springframework/issues/hibernate/HibernateLoadTimeWeaverAwareProcessor.java new file mode 100755 index 00000000..6d99e446 --- /dev/null +++ b/SPR-13886/src/main/java/org/springframework/issues/hibernate/HibernateLoadTimeWeaverAwareProcessor.java @@ -0,0 +1,40 @@ +package org.springframework.issues.hibernate; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.weaving.LoadTimeWeaverAware; +import org.springframework.instrument.classloading.LoadTimeWeaver; +import org.springframework.orm.jpa.EntityManagerFactoryInfo; +import org.springframework.orm.jpa.JpaDialect; +import org.springframework.orm.jpa.vendor.HibernateJpaDialect; + +public class HibernateLoadTimeWeaverAwareProcessor implements BeanPostProcessor { + private static final Logger LOGGER = LoggerFactory.getLogger(HibernateLoadTimeWeaverAwareProcessor.class); + + private final LoadTimeWeaver loadTimeWeaver; + + public HibernateLoadTimeWeaverAwareProcessor(LoadTimeWeaver loadTimeWeaver) { + this.loadTimeWeaver = new HibernateClassBypassingLoadTimeWeaver(loadTimeWeaver); + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof LoadTimeWeaverAware && bean instanceof EntityManagerFactoryInfo) { + JpaDialect jpaDialect = ((EntityManagerFactoryInfo) bean).getJpaDialect(); + boolean isHibernate = jpaDialect instanceof HibernateJpaDialect; + LOGGER.info("EMF {} has Hibernate dialect: {}", bean, isHibernate); + if (isHibernate) { + LOGGER.info("Injecting custom LTW: {}", loadTimeWeaver); + ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(loadTimeWeaver); + } + } + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } +} \ No newline at end of file diff --git a/SPR-13886/src/main/resources/log4j.properties b/SPR-13886/src/main/resources/log4j.properties new file mode 100755 index 00000000..17a835fa --- /dev/null +++ b/SPR-13886/src/main/resources/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootCategory=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n + +log4j.category.org.springframework.web=DEBUG diff --git a/SPR-13886/src/main/webapp/WEB-INF/spring/root-context.xml b/SPR-13886/src/main/webapp/WEB-INF/spring/root-context.xml new file mode 100755 index 00000000..b90fae64 --- /dev/null +++ b/SPR-13886/src/main/webapp/WEB-INF/spring/root-context.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.springframework.issues.entity + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SPR-13886/src/main/webapp/WEB-INF/views/home.jsp b/SPR-13886/src/main/webapp/WEB-INF/views/home.jsp new file mode 100755 index 00000000..1696c2dd --- /dev/null +++ b/SPR-13886/src/main/webapp/WEB-INF/views/home.jsp @@ -0,0 +1,11 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> + + + + +Home + + +

Home

+ + diff --git a/SPR-13886/src/main/webapp/WEB-INF/web.xml b/SPR-13886/src/main/webapp/WEB-INF/web.xml new file mode 100755 index 00000000..63ff9a57 --- /dev/null +++ b/SPR-13886/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,20 @@ + + + + + contextConfigLocation + /WEB-INF/spring/root-context.xml + + + + org.springframework.web.context.ContextLoaderListener + + + + + + + \ No newline at end of file diff --git a/SPR-13886/src/test/java/org/springframework/issues/.gitignore b/SPR-13886/src/test/java/org/springframework/issues/.gitignore new file mode 100755 index 00000000..e69de29b diff --git a/SPR-13886/src/test/resources/log4j.properties b/SPR-13886/src/test/resources/log4j.properties new file mode 100755 index 00000000..17a835fa --- /dev/null +++ b/SPR-13886/src/test/resources/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootCategory=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n + +log4j.category.org.springframework.web=DEBUG