From ff945bdca9b733f7493e4ce49eba8cf29ccbb1a1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 29 Nov 2012 23:45:33 +0100 Subject: [PATCH] Added initial unit tests for Hibernate 4 --- .../web/DelegatingServletInputStream.java | 66 + .../web/DelegatingServletOutputStream.java | 71 + .../mock/web/HeaderValueHolder.java | 96 ++ .../mock/web/MockFilterChain.java | 180 +++ .../mock/web/MockFilterConfig.java | 104 ++ .../mock/web/MockHttpServletRequest.java | 910 +++++++++++ .../mock/web/MockHttpServletResponse.java | 603 +++++++ .../mock/web/MockHttpSession.java | 261 +++ .../mock/web/MockRequestDispatcher.java | 93 ++ .../mock/web/MockServletConfig.java | 103 ++ .../mock/web/MockServletContext.java | 491 ++++++ .../mock/web/PassThroughFilterChain.java | 85 + .../HibernateTransactionManagerTests.java | 1399 +++++++++++++++++ 13 files changed, 4462 insertions(+) create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/DelegatingServletInputStream.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/DelegatingServletOutputStream.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/HeaderValueHolder.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockFilterChain.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockFilterConfig.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpServletRequest.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpServletResponse.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpSession.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockRequestDispatcher.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockServletConfig.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockServletContext.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/mock/web/PassThroughFilterChain.java create mode 100644 spring-orm-hibernate4/src/test/java/org/springframework/orm/hibernate4/HibernateTransactionManagerTests.java diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/DelegatingServletInputStream.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/DelegatingServletInputStream.java new file mode 100644 index 000000000000..b083e95b27e4 --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/DelegatingServletInputStream.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.io.IOException; +import java.io.InputStream; +import javax.servlet.ServletInputStream; + +import org.springframework.util.Assert; + +/** + * Delegating implementation of {@link javax.servlet.ServletInputStream}. + * + *

Used by {@link MockHttpServletRequest}; typically not directly + * used for testing application controllers. + * + * @author Juergen Hoeller + * @since 1.0.2 + * @see MockHttpServletRequest + */ +public class DelegatingServletInputStream extends ServletInputStream { + + private final InputStream sourceStream; + + + /** + * Create a DelegatingServletInputStream for the given source stream. + * @param sourceStream the source stream (never null) + */ + public DelegatingServletInputStream(InputStream sourceStream) { + Assert.notNull(sourceStream, "Source InputStream must not be null"); + this.sourceStream = sourceStream; + } + + /** + * Return the underlying source stream (never null). + */ + public final InputStream getSourceStream() { + return this.sourceStream; + } + + + public int read() throws IOException { + return this.sourceStream.read(); + } + + public void close() throws IOException { + super.close(); + this.sourceStream.close(); + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/DelegatingServletOutputStream.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/DelegatingServletOutputStream.java new file mode 100644 index 000000000000..66fca5df2f91 --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/DelegatingServletOutputStream.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.io.IOException; +import java.io.OutputStream; +import javax.servlet.ServletOutputStream; + +import org.springframework.util.Assert; + +/** + * Delegating implementation of {@link javax.servlet.ServletOutputStream}. + * + *

Used by {@link MockHttpServletResponse}; typically not directly + * used for testing application controllers. + * + * @author Juergen Hoeller + * @since 1.0.2 + * @see MockHttpServletResponse + */ +public class DelegatingServletOutputStream extends ServletOutputStream { + + private final OutputStream targetStream; + + + /** + * Create a DelegatingServletOutputStream for the given target stream. + * @param targetStream the target stream (never null) + */ + public DelegatingServletOutputStream(OutputStream targetStream) { + Assert.notNull(targetStream, "Target OutputStream must not be null"); + this.targetStream = targetStream; + } + + /** + * Return the underlying target stream (never null). + */ + public final OutputStream getTargetStream() { + return this.targetStream; + } + + + public void write(int b) throws IOException { + this.targetStream.write(b); + } + + public void flush() throws IOException { + super.flush(); + this.targetStream.flush(); + } + + public void close() throws IOException { + super.close(); + this.targetStream.close(); + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/HeaderValueHolder.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/HeaderValueHolder.java new file mode 100644 index 000000000000..afe74ad8f0f5 --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/HeaderValueHolder.java @@ -0,0 +1,96 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +/** + * Internal helper class that serves as value holder for request headers. + * + * @author Juergen Hoeller + * @author Rick Evans + * @since 2.0.1 + */ +class HeaderValueHolder { + + private final List values = new LinkedList(); + + + public void setValue(Object value) { + this.values.clear(); + this.values.add(value); + } + + public void addValue(Object value) { + this.values.add(value); + } + + public void addValues(Collection values) { + this.values.addAll(values); + } + + public void addValueArray(Object values) { + CollectionUtils.mergeArrayIntoCollection(values, this.values); + } + + public List getValues() { + return Collections.unmodifiableList(this.values); + } + + public List getStringValues() { + List stringList = new ArrayList(this.values.size()); + for (Object value : this.values) { + stringList.add(value.toString()); + } + return Collections.unmodifiableList(stringList); + } + + public Object getValue() { + return (!this.values.isEmpty() ? this.values.get(0) : null); + } + + public String getStringValue() { + return (!this.values.isEmpty() ? this.values.get(0).toString() : null); + } + + + /** + * Find a HeaderValueHolder by name, ignoring casing. + * @param headers the Map of header names to HeaderValueHolders + * @param name the name of the desired header + * @return the corresponding HeaderValueHolder, + * or null if none found + */ + public static HeaderValueHolder getByName(Map headers, String name) { + Assert.notNull(name, "Header name must not be null"); + for (String headerName : headers.keySet()) { + if (headerName.equalsIgnoreCase(name)) { + return headers.get(headerName); + } + } + return null; + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockFilterChain.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockFilterChain.java new file mode 100644 index 000000000000..3f5d5106c360 --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockFilterChain.java @@ -0,0 +1,180 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; + +/** + *

Mock implementation of the {@link javax.servlet.FilterChain} interface. Used + * for testing the web framework; also useful for testing custom + * {@link javax.servlet.Filter} implementations. + * + *

A {@link org.springframework.mock.web.MockFilterChain} can be configured with one or more filters and a + * Servlet to invoke. The first time the chain is called, it invokes all filters + * and the Servlet, and saves the request and response. Subsequent invocations + * raise an {@link IllegalStateException} unless {@link #reset()} is called. + * + * @author Juergen Hoeller + * @author Rob Winch + * @author Rossen Stoyanchev + * + * @since 2.0.3 + * @see org.springframework.mock.web.MockFilterConfig + * @see org.springframework.mock.web.PassThroughFilterChain + */ +public class MockFilterChain implements FilterChain { + + private ServletRequest request; + + private ServletResponse response; + + private final List filters; + + private Iterator iterator; + + + /** + * Register a single do-nothing {@link javax.servlet.Filter} implementation. The first + * invocation saves the request and response. Subsequent invocations raise + * an {@link IllegalStateException} unless {@link #reset()} is called. + */ + public MockFilterChain() { + this.filters = Collections.emptyList(); + } + + /** + * Create a FilterChain with a Servlet. + * + * @param servlet the Servlet to invoke + * @since 3.2 + */ + public MockFilterChain(Servlet servlet) { + this.filters = initFilterList(servlet); + } + + /** + * Create a {@code FilterChain} with Filter's and a Servlet. + * + * @param servlet the {@link javax.servlet.Servlet} to invoke in this {@link javax.servlet.FilterChain} + * @param filters the {@link javax.servlet.Filter}'s to invoke in this {@link javax.servlet.FilterChain} + * @since 3.2 + */ + public MockFilterChain(Servlet servlet, Filter... filters) { + Assert.notNull(filters, "filters cannot be null"); + Assert.noNullElements(filters, "filters cannot contain null values"); + this.filters = initFilterList(servlet, filters); + } + + private static List initFilterList(Servlet servlet, Filter... filters) { + Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet)); + return Arrays.asList(allFilters); + } + + /** + * Return the request that {@link #doFilter} has been called with. + */ + public ServletRequest getRequest() { + return this.request; + } + + /** + * Return the response that {@link #doFilter} has been called with. + */ + public ServletResponse getResponse() { + return this.response; + } + + /** + * Invoke registered {@link javax.servlet.Filter}s and/or {@link javax.servlet.Servlet} also saving the + * request and response. + */ + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + Assert.notNull(request, "Request must not be null"); + Assert.notNull(response, "Response must not be null"); + + if (this.request != null) { + throw new IllegalStateException("This FilterChain has already been called!"); + } + + if (this.iterator == null) { + this.iterator = this.filters.iterator(); + } + + if (this.iterator.hasNext()) { + Filter nextFilter = this.iterator.next(); + nextFilter.doFilter(request, response, this); + } + + this.request = request; + this.response = response; + } + + /** + * Reset the {@link org.springframework.mock.web.MockFilterChain} allowing it to be invoked again. + */ + public void reset() { + this.request = null; + this.response = null; + this.iterator = null; + } + + + /** + * A filter that simply delegates to a Servlet. + */ + private static class ServletFilterProxy implements Filter { + + private final Servlet delegateServlet; + + private ServletFilterProxy(Servlet servlet) { + Assert.notNull(servlet, "servlet cannot be null"); + this.delegateServlet = servlet; + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + this.delegateServlet.service(request, response); + } + + public void init(FilterConfig filterConfig) throws ServletException { + } + + public void destroy() { + } + + @Override + public String toString() { + return this.delegateServlet.toString(); + } + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockFilterConfig.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockFilterConfig.java new file mode 100644 index 000000000000..34cafd55fc1f --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockFilterConfig.java @@ -0,0 +1,104 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; + +import org.springframework.util.Assert; + +/** + * Mock implementation of the {@link javax.servlet.FilterConfig} interface. + * + *

Used for testing the web framework; also useful for testing + * custom {@link javax.servlet.Filter} implementations. + * + * @author Juergen Hoeller + * @since 1.0.2 + * @see MockFilterChain + * @see PassThroughFilterChain + */ +public class MockFilterConfig implements FilterConfig { + + private final ServletContext servletContext; + + private final String filterName; + + private final Map initParameters = new LinkedHashMap(); + + + /** + * Create a new MockFilterConfig with a default {@link MockServletContext}. + */ + public MockFilterConfig() { + this(null, ""); + } + + /** + * Create a new MockFilterConfig with a default {@link MockServletContext}. + * @param filterName the name of the filter + */ + public MockFilterConfig(String filterName) { + this(null, filterName); + } + + /** + * Create a new MockFilterConfig. + * @param servletContext the ServletContext that the servlet runs in + */ + public MockFilterConfig(ServletContext servletContext) { + this(servletContext, ""); + } + + /** + * Create a new MockFilterConfig. + * @param servletContext the ServletContext that the servlet runs in + * @param filterName the name of the filter + */ + public MockFilterConfig(ServletContext servletContext, String filterName) { + this.servletContext = (servletContext != null ? servletContext : new MockServletContext()); + this.filterName = filterName; + } + + + public String getFilterName() { + return filterName; + } + + public ServletContext getServletContext() { + return servletContext; + } + + public void addInitParameter(String name, String value) { + Assert.notNull(name, "Parameter name must not be null"); + this.initParameters.put(name, value); + } + + public String getInitParameter(String name) { + Assert.notNull(name, "Parameter name must not be null"); + return this.initParameters.get(name); + } + + public Enumeration getInitParameterNames() { + return Collections.enumeration(this.initParameters.keySet()); + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpServletRequest.java new file mode 100644 index 000000000000..88afb595afe9 --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpServletRequest.java @@ -0,0 +1,910 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.springframework.util.Assert; +import org.springframework.util.LinkedCaseInsensitiveMap; + +/** + * Mock implementation of the {@link javax.servlet.http.HttpServletRequest} interface. + * + *

Compatible with Servlet 2.5 and partially with Servlet 3.0 (notable exceptions: + * the getPart(s) and startAsync families of methods). + * + * @author Juergen Hoeller + * @author Rod Johnson + * @author Rick Evans + * @author Mark Fisher + * @author Sam Brannen + * @since 1.0.2 + */ +public class MockHttpServletRequest implements HttpServletRequest { + + /** + * The default protocol: 'http'. + */ + public static final String DEFAULT_PROTOCOL = "http"; + + /** + * The default server address: '127.0.0.1'. + */ + public static final String DEFAULT_SERVER_ADDR = "127.0.0.1"; + + /** + * The default server name: 'localhost'. + */ + public static final String DEFAULT_SERVER_NAME = "localhost"; + + /** + * The default server port: '80'. + */ + public static final int DEFAULT_SERVER_PORT = 80; + + /** + * The default remote address: '127.0.0.1'. + */ + public static final String DEFAULT_REMOTE_ADDR = "127.0.0.1"; + + /** + * The default remote host: 'localhost'. + */ + public static final String DEFAULT_REMOTE_HOST = "localhost"; + + private static final String CONTENT_TYPE_HEADER = "Content-Type"; + + private static final String CHARSET_PREFIX = "charset="; + + private boolean active = true; + + // --------------------------------------------------------------------- + // ServletRequest properties + // --------------------------------------------------------------------- + + private final Map attributes = new LinkedHashMap(); + + private String characterEncoding; + + private byte[] content; + + private String contentType; + + private final Map parameters = new LinkedHashMap(16); + + private String protocol = DEFAULT_PROTOCOL; + + private String scheme = DEFAULT_PROTOCOL; + + private String serverName = DEFAULT_SERVER_NAME; + + private int serverPort = DEFAULT_SERVER_PORT; + + private String remoteAddr = DEFAULT_REMOTE_ADDR; + + private String remoteHost = DEFAULT_REMOTE_HOST; + + /** List of locales in descending order */ + private final List locales = new LinkedList(); + + private boolean secure = false; + + private final ServletContext servletContext; + + private int remotePort = DEFAULT_SERVER_PORT; + + private String localName = DEFAULT_SERVER_NAME; + + private String localAddr = DEFAULT_SERVER_ADDR; + + private int localPort = DEFAULT_SERVER_PORT; + + // --------------------------------------------------------------------- + // HttpServletRequest properties + // --------------------------------------------------------------------- + + private String authType; + + private Cookie[] cookies; + + private final Map headers = new LinkedCaseInsensitiveMap(); + + private String method; + + private String pathInfo; + + private String contextPath = ""; + + private String queryString; + + private String remoteUser; + + private final Set userRoles = new HashSet(); + + private Principal userPrincipal; + + private String requestedSessionId; + + private String requestURI; + + private String servletPath = ""; + + private HttpSession session; + + private boolean requestedSessionIdValid = true; + + private boolean requestedSessionIdFromCookie = true; + + private boolean requestedSessionIdFromURL = false; + + + // --------------------------------------------------------------------- + // Constructors + // --------------------------------------------------------------------- + + /** + * Create a new {@code MockHttpServletRequest} with a default + * {@link org.springframework.mock.web.MockServletContext}. + * @see #MockHttpServletRequest(javax.servlet.ServletContext, String, String) + */ + public MockHttpServletRequest() { + this(null, "", ""); + } + + /** + * Create a new {@code MockHttpServletRequest} with a default + * {@link org.springframework.mock.web.MockServletContext}. + * @param method the request method (may be null) + * @param requestURI the request URI (may be null) + * @see #setMethod + * @see #setRequestURI + * @see #MockHttpServletRequest(javax.servlet.ServletContext, String, String) + */ + public MockHttpServletRequest(String method, String requestURI) { + this(null, method, requestURI); + } + + /** + * Create a new {@code MockHttpServletRequest} with the supplied {@link javax.servlet.ServletContext}. + * @param servletContext the ServletContext that the request runs in (may be + * null to use a default {@link org.springframework.mock.web.MockServletContext}) + * @see #MockHttpServletRequest(javax.servlet.ServletContext, String, String) + */ + public MockHttpServletRequest(ServletContext servletContext) { + this(servletContext, "", ""); + } + + /** + * Create a new {@code MockHttpServletRequest} with the supplied {@link javax.servlet.ServletContext}, + * {@code method}, and {@code requestURI}. + *

The preferred locale will be set to {@link java.util.Locale#ENGLISH}. + * @param servletContext the ServletContext that the request runs in (may be + * null to use a default {@link org.springframework.mock.web.MockServletContext}) + * @param method the request method (may be null) + * @param requestURI the request URI (may be null) + * @see #setMethod + * @see #setRequestURI + * @see #setPreferredLocales + * @see org.springframework.mock.web.MockServletContext + */ + public MockHttpServletRequest(ServletContext servletContext, String method, String requestURI) { + this.servletContext = (servletContext != null ? servletContext : new MockServletContext()); + this.method = method; + this.requestURI = requestURI; + this.locales.add(Locale.ENGLISH); + } + + // --------------------------------------------------------------------- + // Lifecycle methods + // --------------------------------------------------------------------- + + /** + * Return the ServletContext that this request is associated with. (Not + * available in the standard HttpServletRequest interface for some reason.) + */ + public ServletContext getServletContext() { + return this.servletContext; + } + + /** + * Return whether this request is still active (that is, not completed yet). + */ + public boolean isActive() { + return this.active; + } + + /** + * Mark this request as completed, keeping its state. + */ + public void close() { + this.active = false; + } + + /** + * Invalidate this request, clearing its state. + */ + public void invalidate() { + close(); + clearAttributes(); + } + + /** + * Check whether this request is still active (that is, not completed yet), + * throwing an IllegalStateException if not active anymore. + */ + protected void checkActive() throws IllegalStateException { + if (!this.active) { + throw new IllegalStateException("Request is not active anymore"); + } + } + + // --------------------------------------------------------------------- + // ServletRequest interface + // --------------------------------------------------------------------- + + public Object getAttribute(String name) { + checkActive(); + return this.attributes.get(name); + } + + public Enumeration getAttributeNames() { + checkActive(); + return Collections.enumeration(this.attributes.keySet()); + } + + public String getCharacterEncoding() { + return this.characterEncoding; + } + + public void setCharacterEncoding(String characterEncoding) { + this.characterEncoding = characterEncoding; + updateContentTypeHeader(); + } + + private void updateContentTypeHeader() { + if (this.contentType != null) { + StringBuilder sb = new StringBuilder(this.contentType); + if (!this.contentType.toLowerCase().contains(CHARSET_PREFIX) && this.characterEncoding != null) { + sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding); + } + doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true); + } + } + + public void setContent(byte[] content) { + this.content = content; + } + + public int getContentLength() { + return (this.content != null ? this.content.length : -1); + } + + public void setContentType(String contentType) { + this.contentType = contentType; + if (contentType != null) { + int charsetIndex = contentType.toLowerCase().indexOf(CHARSET_PREFIX); + if (charsetIndex != -1) { + String encoding = contentType.substring(charsetIndex + CHARSET_PREFIX.length()); + this.characterEncoding = encoding; + } + updateContentTypeHeader(); + } + } + + public String getContentType() { + return this.contentType; + } + + public ServletInputStream getInputStream() { + if (this.content != null) { + return new DelegatingServletInputStream(new ByteArrayInputStream(this.content)); + } + else { + return null; + } + } + + /** + * Set a single value for the specified HTTP parameter. + *

If there are already one or more values registered for the given + * parameter name, they will be replaced. + */ + public void setParameter(String name, String value) { + setParameter(name, new String[] { value }); + } + + /** + * Set an array of values for the specified HTTP parameter. + *

If there are already one or more values registered for the given + * parameter name, they will be replaced. + */ + public void setParameter(String name, String[] values) { + Assert.notNull(name, "Parameter name must not be null"); + this.parameters.put(name, values); + } + + /** + * Sets all provided parameters replacing any existing + * values for the provided parameter names. To add without replacing + * existing values, use {@link #addParameters(java.util.Map)}. + */ + @SuppressWarnings("rawtypes") + public void setParameters(Map params) { + Assert.notNull(params, "Parameter map must not be null"); + for (Object key : params.keySet()) { + Assert.isInstanceOf(String.class, key, "Parameter map key must be of type [" + String.class.getName() + "]"); + Object value = params.get(key); + if (value instanceof String) { + this.setParameter((String) key, (String) value); + } + else if (value instanceof String[]) { + this.setParameter((String) key, (String[]) value); + } + else { + throw new IllegalArgumentException("Parameter map value must be single value " + " or array of type [" + + String.class.getName() + "]"); + } + } + } + + /** + * Add a single value for the specified HTTP parameter. + *

If there are already one or more values registered for the given + * parameter name, the given value will be added to the end of the list. + */ + public void addParameter(String name, String value) { + addParameter(name, new String[] { value }); + } + + /** + * Add an array of values for the specified HTTP parameter. + *

If there are already one or more values registered for the given + * parameter name, the given values will be added to the end of the list. + */ + public void addParameter(String name, String[] values) { + Assert.notNull(name, "Parameter name must not be null"); + String[] oldArr = this.parameters.get(name); + if (oldArr != null) { + String[] newArr = new String[oldArr.length + values.length]; + System.arraycopy(oldArr, 0, newArr, 0, oldArr.length); + System.arraycopy(values, 0, newArr, oldArr.length, values.length); + this.parameters.put(name, newArr); + } + else { + this.parameters.put(name, values); + } + } + + /** + * Adds all provided parameters without replacing any + * existing values. To replace existing values, use + * {@link #setParameters(java.util.Map)}. + */ + @SuppressWarnings("rawtypes") + public void addParameters(Map params) { + Assert.notNull(params, "Parameter map must not be null"); + for (Object key : params.keySet()) { + Assert.isInstanceOf(String.class, key, "Parameter map key must be of type [" + String.class.getName() + "]"); + Object value = params.get(key); + if (value instanceof String) { + this.addParameter((String) key, (String) value); + } + else if (value instanceof String[]) { + this.addParameter((String) key, (String[]) value); + } + else { + throw new IllegalArgumentException("Parameter map value must be single value " + " or array of type [" + + String.class.getName() + "]"); + } + } + } + + /** + * Remove already registered values for the specified HTTP parameter, if + * any. + */ + public void removeParameter(String name) { + Assert.notNull(name, "Parameter name must not be null"); + this.parameters.remove(name); + } + + /** + * Removes all existing parameters. + */ + public void removeAllParameters() { + this.parameters.clear(); + } + + public String getParameter(String name) { + Assert.notNull(name, "Parameter name must not be null"); + String[] arr = this.parameters.get(name); + return (arr != null && arr.length > 0 ? arr[0] : null); + } + + public Enumeration getParameterNames() { + return Collections.enumeration(this.parameters.keySet()); + } + + public String[] getParameterValues(String name) { + Assert.notNull(name, "Parameter name must not be null"); + return this.parameters.get(name); + } + + public Map getParameterMap() { + return Collections.unmodifiableMap(this.parameters); + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getProtocol() { + return this.protocol; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + public String getScheme() { + return this.scheme; + } + + public void setServerName(String serverName) { + this.serverName = serverName; + } + + public String getServerName() { + return this.serverName; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } + + public int getServerPort() { + return this.serverPort; + } + + public BufferedReader getReader() throws UnsupportedEncodingException { + if (this.content != null) { + InputStream sourceStream = new ByteArrayInputStream(this.content); + Reader sourceReader = (this.characterEncoding != null) ? new InputStreamReader(sourceStream, + this.characterEncoding) : new InputStreamReader(sourceStream); + return new BufferedReader(sourceReader); + } + else { + return null; + } + } + + public void setRemoteAddr(String remoteAddr) { + this.remoteAddr = remoteAddr; + } + + public String getRemoteAddr() { + return this.remoteAddr; + } + + public void setRemoteHost(String remoteHost) { + this.remoteHost = remoteHost; + } + + public String getRemoteHost() { + return this.remoteHost; + } + + public void setAttribute(String name, Object value) { + checkActive(); + Assert.notNull(name, "Attribute name must not be null"); + if (value != null) { + this.attributes.put(name, value); + } + else { + this.attributes.remove(name); + } + } + + public void removeAttribute(String name) { + checkActive(); + Assert.notNull(name, "Attribute name must not be null"); + this.attributes.remove(name); + } + + /** + * Clear all of this request's attributes. + */ + public void clearAttributes() { + this.attributes.clear(); + } + + /** + * Add a new preferred locale, before any existing locales. + * @see #setPreferredLocales + */ + public void addPreferredLocale(Locale locale) { + Assert.notNull(locale, "Locale must not be null"); + this.locales.add(0, locale); + } + + /** + * Set the list of preferred locales, in descending order, effectively replacing + * any existing locales. + * @see #addPreferredLocale + * @since 3.2 + */ + public void setPreferredLocales(List locales) { + Assert.notEmpty(locales, "preferred locales list must not be empty"); + this.locales.clear(); + this.locales.addAll(locales); + } + + public Locale getLocale() { + return this.locales.get(0); + } + + public Enumeration getLocales() { + return Collections.enumeration(this.locales); + } + + public void setSecure(boolean secure) { + this.secure = secure; + } + + public boolean isSecure() { + return this.secure; + } + + public RequestDispatcher getRequestDispatcher(String path) { + return new MockRequestDispatcher(path); + } + + public String getRealPath(String path) { + return this.servletContext.getRealPath(path); + } + + public void setRemotePort(int remotePort) { + this.remotePort = remotePort; + } + + public int getRemotePort() { + return this.remotePort; + } + + public void setLocalName(String localName) { + this.localName = localName; + } + + public String getLocalName() { + return this.localName; + } + + public void setLocalAddr(String localAddr) { + this.localAddr = localAddr; + } + + public String getLocalAddr() { + return this.localAddr; + } + + public void setLocalPort(int localPort) { + this.localPort = localPort; + } + + public int getLocalPort() { + return this.localPort; + } + + // --------------------------------------------------------------------- + // HttpServletRequest interface + // --------------------------------------------------------------------- + + public void setAuthType(String authType) { + this.authType = authType; + } + + public String getAuthType() { + return this.authType; + } + + public void setCookies(Cookie... cookies) { + this.cookies = cookies; + } + + public Cookie[] getCookies() { + return this.cookies; + } + + /** + * Add a header entry for the given name. + *

If there was no entry for that header name before, the value will be used + * as-is. In case of an existing entry, a String array will be created, + * adding the given value (more specifically, its toString representation) + * as further element. + *

Multiple values can only be stored as list of Strings, following the + * Servlet spec (see getHeaders accessor). As alternative to + * repeated addHeader calls for individual elements, you can + * use a single call with an entire array or Collection of values as + * parameter. + * @see #getHeaderNames + * @see #getHeader + * @see #getHeaders + * @see #getDateHeader + * @see #getIntHeader + */ + public void addHeader(String name, Object value) { + if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name)) { + setContentType((String) value); + return; + } + doAddHeaderValue(name, value, false); + } + + @SuppressWarnings("rawtypes") + private void doAddHeaderValue(String name, Object value, boolean replace) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + Assert.notNull(value, "Header value must not be null"); + if (header == null || replace) { + header = new HeaderValueHolder(); + this.headers.put(name, header); + } + if (value instanceof Collection) { + header.addValues((Collection) value); + } + else if (value.getClass().isArray()) { + header.addValueArray(value); + } + else { + header.addValue(value); + } + } + + public long getDateHeader(String name) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + Object value = (header != null ? header.getValue() : null); + if (value instanceof Date) { + return ((Date) value).getTime(); + } + else if (value instanceof Number) { + return ((Number) value).longValue(); + } + else if (value != null) { + throw new IllegalArgumentException("Value for header '" + name + "' is neither a Date nor a Number: " + + value); + } + else { + return -1L; + } + } + + public String getHeader(String name) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + return (header != null ? header.getStringValue() : null); + } + + public Enumeration getHeaders(String name) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + return Collections.enumeration(header != null ? header.getStringValues() : new LinkedList()); + } + + public Enumeration getHeaderNames() { + return Collections.enumeration(this.headers.keySet()); + } + + public int getIntHeader(String name) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + Object value = (header != null ? header.getValue() : null); + if (value instanceof Number) { + return ((Number) value).intValue(); + } + else if (value instanceof String) { + return Integer.parseInt((String) value); + } + else if (value != null) { + throw new NumberFormatException("Value for header '" + name + "' is not a Number: " + value); + } + else { + return -1; + } + } + + public void setMethod(String method) { + this.method = method; + } + + public String getMethod() { + return this.method; + } + + public void setPathInfo(String pathInfo) { + this.pathInfo = pathInfo; + } + + public String getPathInfo() { + return this.pathInfo; + } + + public String getPathTranslated() { + return (this.pathInfo != null ? getRealPath(this.pathInfo) : null); + } + + public void setContextPath(String contextPath) { + this.contextPath = contextPath; + } + + public String getContextPath() { + return this.contextPath; + } + + public void setQueryString(String queryString) { + this.queryString = queryString; + } + + public String getQueryString() { + return this.queryString; + } + + public void setRemoteUser(String remoteUser) { + this.remoteUser = remoteUser; + } + + public String getRemoteUser() { + return this.remoteUser; + } + + public void addUserRole(String role) { + this.userRoles.add(role); + } + + public boolean isUserInRole(String role) { + return (this.userRoles.contains(role) || (this.servletContext instanceof MockServletContext && ((MockServletContext) this.servletContext).getDeclaredRoles().contains( + role))); + } + + public void setUserPrincipal(Principal userPrincipal) { + this.userPrincipal = userPrincipal; + } + + public Principal getUserPrincipal() { + return this.userPrincipal; + } + + public void setRequestedSessionId(String requestedSessionId) { + this.requestedSessionId = requestedSessionId; + } + + public String getRequestedSessionId() { + return this.requestedSessionId; + } + + public void setRequestURI(String requestURI) { + this.requestURI = requestURI; + } + + public String getRequestURI() { + return this.requestURI; + } + + public StringBuffer getRequestURL() { + StringBuffer url = new StringBuffer(this.scheme); + url.append("://").append(this.serverName).append(':').append(this.serverPort); + url.append(getRequestURI()); + return url; + } + + public void setServletPath(String servletPath) { + this.servletPath = servletPath; + } + + public String getServletPath() { + return this.servletPath; + } + + public void setSession(HttpSession session) { + this.session = session; + if (session instanceof MockHttpSession) { + MockHttpSession mockSession = ((MockHttpSession) session); + mockSession.access(); + } + } + + public HttpSession getSession(boolean create) { + checkActive(); + // Reset session if invalidated. + if (this.session instanceof MockHttpSession && ((MockHttpSession) this.session).isInvalid()) { + this.session = null; + } + // Create new session if necessary. + if (this.session == null && create) { + this.session = new MockHttpSession(this.servletContext); + } + return this.session; + } + + public HttpSession getSession() { + return getSession(true); + } + + public void setRequestedSessionIdValid(boolean requestedSessionIdValid) { + this.requestedSessionIdValid = requestedSessionIdValid; + } + + public boolean isRequestedSessionIdValid() { + return this.requestedSessionIdValid; + } + + public void setRequestedSessionIdFromCookie(boolean requestedSessionIdFromCookie) { + this.requestedSessionIdFromCookie = requestedSessionIdFromCookie; + } + + public boolean isRequestedSessionIdFromCookie() { + return this.requestedSessionIdFromCookie; + } + + public void setRequestedSessionIdFromURL(boolean requestedSessionIdFromURL) { + this.requestedSessionIdFromURL = requestedSessionIdFromURL; + } + + public boolean isRequestedSessionIdFromURL() { + return this.requestedSessionIdFromURL; + } + + public boolean isRequestedSessionIdFromUrl() { + return isRequestedSessionIdFromURL(); + } + + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return (this.userPrincipal != null && this.remoteUser != null && this.authType != null); + } + + public void login(String username, String password) throws ServletException { + throw new ServletException("Username-password authentication not supported - override the login method"); + } + + public void logout() throws ServletException { + this.userPrincipal = null; + this.remoteUser = null; + this.authType = null; + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpServletResponse.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpServletResponse.java new file mode 100644 index 000000000000..550e927a4777 --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpServletResponse.java @@ -0,0 +1,603 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.util.Assert; +import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.web.util.WebUtils; + +/** + * Mock implementation of the {@link javax.servlet.http.HttpServletResponse} interface. + * + *

Compatible with Servlet 2.5 as well as Servlet 3.0. + * + * @author Juergen Hoeller + * @author Rod Johnson + * @since 1.0.2 + */ +public class MockHttpServletResponse implements HttpServletResponse { + + private static final String CHARSET_PREFIX = "charset="; + + private static final String CONTENT_TYPE_HEADER = "Content-Type"; + + private static final String CONTENT_LENGTH_HEADER = "Content-Length"; + + private static final String LOCATION_HEADER = "Location"; + + //--------------------------------------------------------------------- + // ServletResponse properties + //--------------------------------------------------------------------- + + private boolean outputStreamAccessAllowed = true; + + private boolean writerAccessAllowed = true; + + private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING; + + private boolean charset = false; + + private final ByteArrayOutputStream content = new ByteArrayOutputStream(); + + private final ServletOutputStream outputStream = new ResponseServletOutputStream(this.content); + + private PrintWriter writer; + + private int contentLength = 0; + + private String contentType; + + private int bufferSize = 4096; + + private boolean committed; + + private Locale locale = Locale.getDefault(); + + + //--------------------------------------------------------------------- + // HttpServletResponse properties + //--------------------------------------------------------------------- + + private final List cookies = new ArrayList(); + + private final Map headers = new LinkedCaseInsensitiveMap(); + + private int status = HttpServletResponse.SC_OK; + + private String errorMessage; + + private String forwardedUrl; + + private final List includedUrls = new ArrayList(); + + + //--------------------------------------------------------------------- + // ServletResponse interface + //--------------------------------------------------------------------- + + /** + * Set whether {@link #getOutputStream()} access is allowed. + *

Default is true. + */ + public void setOutputStreamAccessAllowed(boolean outputStreamAccessAllowed) { + this.outputStreamAccessAllowed = outputStreamAccessAllowed; + } + + /** + * Return whether {@link #getOutputStream()} access is allowed. + */ + public boolean isOutputStreamAccessAllowed() { + return this.outputStreamAccessAllowed; + } + + /** + * Set whether {@link #getWriter()} access is allowed. + *

Default is true. + */ + public void setWriterAccessAllowed(boolean writerAccessAllowed) { + this.writerAccessAllowed = writerAccessAllowed; + } + + /** + * Return whether {@link #getOutputStream()} access is allowed. + */ + public boolean isWriterAccessAllowed() { + return this.writerAccessAllowed; + } + + public void setCharacterEncoding(String characterEncoding) { + this.characterEncoding = characterEncoding; + this.charset = true; + updateContentTypeHeader(); + } + + private void updateContentTypeHeader() { + if (this.contentType != null) { + StringBuilder sb = new StringBuilder(this.contentType); + if (this.contentType.toLowerCase().indexOf(CHARSET_PREFIX) == -1 && this.charset) { + sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding); + } + doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true); + } + } + + public String getCharacterEncoding() { + return this.characterEncoding; + } + + public ServletOutputStream getOutputStream() { + if (!this.outputStreamAccessAllowed) { + throw new IllegalStateException("OutputStream access not allowed"); + } + return this.outputStream; + } + + public PrintWriter getWriter() throws UnsupportedEncodingException { + if (!this.writerAccessAllowed) { + throw new IllegalStateException("Writer access not allowed"); + } + if (this.writer == null) { + Writer targetWriter = (this.characterEncoding != null ? + new OutputStreamWriter(this.content, this.characterEncoding) : new OutputStreamWriter(this.content)); + this.writer = new ResponsePrintWriter(targetWriter); + } + return this.writer; + } + + public byte[] getContentAsByteArray() { + flushBuffer(); + return this.content.toByteArray(); + } + + public String getContentAsString() throws UnsupportedEncodingException { + flushBuffer(); + return (this.characterEncoding != null) ? + this.content.toString(this.characterEncoding) : this.content.toString(); + } + + public void setContentLength(int contentLength) { + this.contentLength = contentLength; + doAddHeaderValue(CONTENT_LENGTH_HEADER, contentLength, true); + } + + public int getContentLength() { + return this.contentLength; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + if (contentType != null) { + int charsetIndex = contentType.toLowerCase().indexOf(CHARSET_PREFIX); + if (charsetIndex != -1) { + String encoding = contentType.substring(charsetIndex + CHARSET_PREFIX.length()); + this.characterEncoding = encoding; + this.charset = true; + } + updateContentTypeHeader(); + } + } + + public String getContentType() { + return this.contentType; + } + + public void setBufferSize(int bufferSize) { + this.bufferSize = bufferSize; + } + + public int getBufferSize() { + return this.bufferSize; + } + + public void flushBuffer() { + setCommitted(true); + } + + public void resetBuffer() { + if (isCommitted()) { + throw new IllegalStateException("Cannot reset buffer - response is already committed"); + } + this.content.reset(); + } + + private void setCommittedIfBufferSizeExceeded() { + int bufSize = getBufferSize(); + if (bufSize > 0 && this.content.size() > bufSize) { + setCommitted(true); + } + } + + public void setCommitted(boolean committed) { + this.committed = committed; + } + + public boolean isCommitted() { + return this.committed; + } + + public void reset() { + resetBuffer(); + this.characterEncoding = null; + this.contentLength = 0; + this.contentType = null; + this.locale = null; + this.cookies.clear(); + this.headers.clear(); + this.status = HttpServletResponse.SC_OK; + this.errorMessage = null; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } + + public Locale getLocale() { + return this.locale; + } + + + //--------------------------------------------------------------------- + // HttpServletResponse interface + //--------------------------------------------------------------------- + + public void addCookie(Cookie cookie) { + Assert.notNull(cookie, "Cookie must not be null"); + this.cookies.add(cookie); + } + + public Cookie[] getCookies() { + return this.cookies.toArray(new Cookie[this.cookies.size()]); + } + + public Cookie getCookie(String name) { + Assert.notNull(name, "Cookie name must not be null"); + for (Cookie cookie : this.cookies) { + if (name.equals(cookie.getName())) { + return cookie; + } + } + return null; + } + + public boolean containsHeader(String name) { + return (HeaderValueHolder.getByName(this.headers, name) != null); + } + + /** + * Return the names of all specified headers as a Set of Strings. + *

As of Servlet 3.0, this method is also defined HttpServletResponse. + * @return the Set of header name Strings, or an empty Set if none + */ + public Set getHeaderNames() { + return this.headers.keySet(); + } + + /** + * Return the primary value for the given header as a String, if any. + * Will return the first value in case of multiple values. + *

As of Servlet 3.0, this method is also defined in HttpServletResponse. + * As of Spring 3.1, it returns a stringified value for Servlet 3.0 compatibility. + * Consider using {@link #getHeaderValue(String)} for raw Object access. + * @param name the name of the header + * @return the associated header value, or null if none + */ + public String getHeader(String name) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + return (header != null ? header.getStringValue() : null); + } + + /** + * Return all values for the given header as a List of Strings. + *

As of Servlet 3.0, this method is also defined in HttpServletResponse. + * As of Spring 3.1, it returns a List of stringified values for Servlet 3.0 compatibility. + * Consider using {@link #getHeaderValues(String)} for raw Object access. + * @param name the name of the header + * @return the associated header values, or an empty List if none + */ + public List getHeaders(String name) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + if (header != null) { + return header.getStringValues(); + } + else { + return Collections.emptyList(); + } + } + + /** + * Return the primary value for the given header, if any. + *

Will return the first value in case of multiple values. + * @param name the name of the header + * @return the associated header value, or null if none + */ + public Object getHeaderValue(String name) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + return (header != null ? header.getValue() : null); + } + + /** + * Return all values for the given header as a List of value objects. + * @param name the name of the header + * @return the associated header values, or an empty List if none + */ + public List getHeaderValues(String name) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + if (header != null) { + return header.getValues(); + } + else { + return Collections.emptyList(); + } + } + + /** + * The default implementation returns the given URL String as-is. + *

Can be overridden in subclasses, appending a session id or the like. + */ + public String encodeURL(String url) { + return url; + } + + /** + * The default implementation delegates to {@link #encodeURL}, + * returning the given URL String as-is. + *

Can be overridden in subclasses, appending a session id or the like + * in a redirect-specific fashion. For general URL encoding rules, + * override the common {@link #encodeURL} method instead, applying + * to redirect URLs as well as to general URLs. + */ + public String encodeRedirectURL(String url) { + return encodeURL(url); + } + + public String encodeUrl(String url) { + return encodeURL(url); + } + + public String encodeRedirectUrl(String url) { + return encodeRedirectURL(url); + } + + public void sendError(int status, String errorMessage) throws IOException { + if (isCommitted()) { + throw new IllegalStateException("Cannot set error status - response is already committed"); + } + this.status = status; + this.errorMessage = errorMessage; + setCommitted(true); + } + + public void sendError(int status) throws IOException { + if (isCommitted()) { + throw new IllegalStateException("Cannot set error status - response is already committed"); + } + this.status = status; + setCommitted(true); + } + + public void sendRedirect(String url) throws IOException { + if (isCommitted()) { + throw new IllegalStateException("Cannot send redirect - response is already committed"); + } + Assert.notNull(url, "Redirect URL must not be null"); + setHeader(LOCATION_HEADER, url); + setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + setCommitted(true); + } + + public String getRedirectedUrl() { + return getHeader(LOCATION_HEADER); + } + + public void setDateHeader(String name, long value) { + setHeaderValue(name, value); + } + + public void addDateHeader(String name, long value) { + addHeaderValue(name, value); + } + + public void setHeader(String name, String value) { + setHeaderValue(name, value); + } + + public void addHeader(String name, String value) { + addHeaderValue(name, value); + } + + public void setIntHeader(String name, int value) { + setHeaderValue(name, value); + } + + public void addIntHeader(String name, int value) { + addHeaderValue(name, value); + } + + private void setHeaderValue(String name, Object value) { + if (setSpecialHeader(name, value)) { + return; + } + doAddHeaderValue(name, value, true); + } + + private void addHeaderValue(String name, Object value) { + if (setSpecialHeader(name, value)) { + return; + } + doAddHeaderValue(name, value, false); + } + + private boolean setSpecialHeader(String name, Object value) { + if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name)) { + setContentType((String) value); + return true; + } + else if (CONTENT_LENGTH_HEADER.equalsIgnoreCase(name)) { + setContentLength(Integer.parseInt((String) value)); + return true; + } + else { + return false; + } + } + + private void doAddHeaderValue(String name, Object value, boolean replace) { + HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); + Assert.notNull(value, "Header value must not be null"); + if (header == null) { + header = new HeaderValueHolder(); + this.headers.put(name, header); + } + if (replace) { + header.setValue(value); + } + else { + header.addValue(value); + } + } + + public void setStatus(int status) { + this.status = status; + } + + public void setStatus(int status, String errorMessage) { + this.status = status; + this.errorMessage = errorMessage; + } + + public int getStatus() { + return this.status; + } + + public String getErrorMessage() { + return this.errorMessage; + } + + + //--------------------------------------------------------------------- + // Methods for MockRequestDispatcher + //--------------------------------------------------------------------- + + public void setForwardedUrl(String forwardedUrl) { + this.forwardedUrl = forwardedUrl; + } + + public String getForwardedUrl() { + return this.forwardedUrl; + } + + public void setIncludedUrl(String includedUrl) { + this.includedUrls.clear(); + if (includedUrl != null) { + this.includedUrls.add(includedUrl); + } + } + + public String getIncludedUrl() { + int count = this.includedUrls.size(); + if (count > 1) { + throw new IllegalStateException( + "More than 1 URL included - check getIncludedUrls instead: " + this.includedUrls); + } + return (count == 1 ? this.includedUrls.get(0) : null); + } + + public void addIncludedUrl(String includedUrl) { + Assert.notNull(includedUrl, "Included URL must not be null"); + this.includedUrls.add(includedUrl); + } + + public List getIncludedUrls() { + return this.includedUrls; + } + + + /** + * Inner class that adapts the ServletOutputStream to mark the + * response as committed once the buffer size is exceeded. + */ + private class ResponseServletOutputStream extends DelegatingServletOutputStream { + + public ResponseServletOutputStream(OutputStream out) { + super(out); + } + + public void write(int b) throws IOException { + super.write(b); + super.flush(); + setCommittedIfBufferSizeExceeded(); + } + + public void flush() throws IOException { + super.flush(); + setCommitted(true); + } + } + + + /** + * Inner class that adapts the PrintWriter to mark the + * response as committed once the buffer size is exceeded. + */ + private class ResponsePrintWriter extends PrintWriter { + + public ResponsePrintWriter(Writer out) { + super(out, true); + } + + public void write(char buf[], int off, int len) { + super.write(buf, off, len); + super.flush(); + setCommittedIfBufferSizeExceeded(); + } + + public void write(String s, int off, int len) { + super.write(s, off, len); + super.flush(); + setCommittedIfBufferSizeExceeded(); + } + + public void write(int c) { + super.write(c); + super.flush(); + setCommittedIfBufferSizeExceeded(); + } + + public void flush() { + super.flush(); + setCommitted(true); + } + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpSession.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpSession.java new file mode 100644 index 000000000000..3caa9213ee4d --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockHttpSession.java @@ -0,0 +1,261 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionBindingListener; +import javax.servlet.http.HttpSessionContext; + +import org.springframework.util.Assert; + +/** + * Mock implementation of the {@link javax.servlet.http.HttpSession} interface. + * + *

Compatible with Servlet 2.5 as well as Servlet 3.0. + * + *

Used for testing the web framework; also useful for testing application + * controllers. + * + * @author Juergen Hoeller + * @author Rod Johnson + * @author Mark Fisher + * @author Sam Brannen + * @since 1.0.2 + */ +@SuppressWarnings("deprecation") +public class MockHttpSession implements HttpSession { + + public static final String SESSION_COOKIE_NAME = "JSESSION"; + + private static int nextId = 1; + + private final String id; + + private final long creationTime = System.currentTimeMillis(); + + private int maxInactiveInterval; + + private long lastAccessedTime = System.currentTimeMillis(); + + private final ServletContext servletContext; + + private final Map attributes = new LinkedHashMap(); + + private boolean invalid = false; + + private boolean isNew = true; + + + /** + * Create a new MockHttpSession with a default {@link org.springframework.mock.web.MockServletContext}. + * + * @see org.springframework.mock.web.MockServletContext + */ + public MockHttpSession() { + this(null); + } + + /** + * Create a new MockHttpSession. + * + * @param servletContext the ServletContext that the session runs in + */ + public MockHttpSession(ServletContext servletContext) { + this(servletContext, null); + } + + /** + * Create a new MockHttpSession. + * + * @param servletContext the ServletContext that the session runs in + * @param id a unique identifier for this session + */ + public MockHttpSession(ServletContext servletContext, String id) { + this.servletContext = (servletContext != null ? servletContext : new MockServletContext()); + this.id = (id != null ? id : Integer.toString(nextId++)); + } + + public long getCreationTime() { + return this.creationTime; + } + + public String getId() { + return this.id; + } + + public void access() { + this.lastAccessedTime = System.currentTimeMillis(); + this.isNew = false; + } + + public long getLastAccessedTime() { + return this.lastAccessedTime; + } + + public ServletContext getServletContext() { + return this.servletContext; + } + + public void setMaxInactiveInterval(int interval) { + this.maxInactiveInterval = interval; + } + + public int getMaxInactiveInterval() { + return this.maxInactiveInterval; + } + + public HttpSessionContext getSessionContext() { + throw new UnsupportedOperationException("getSessionContext"); + } + + public Object getAttribute(String name) { + Assert.notNull(name, "Attribute name must not be null"); + return this.attributes.get(name); + } + + public Object getValue(String name) { + return getAttribute(name); + } + + public Enumeration getAttributeNames() { + return Collections.enumeration(this.attributes.keySet()); + } + + public String[] getValueNames() { + return this.attributes.keySet().toArray(new String[this.attributes.size()]); + } + + public void setAttribute(String name, Object value) { + Assert.notNull(name, "Attribute name must not be null"); + if (value != null) { + this.attributes.put(name, value); + if (value instanceof HttpSessionBindingListener) { + ((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name, value)); + } + } + else { + removeAttribute(name); + } + } + + public void putValue(String name, Object value) { + setAttribute(name, value); + } + + public void removeAttribute(String name) { + Assert.notNull(name, "Attribute name must not be null"); + Object value = this.attributes.remove(name); + if (value instanceof HttpSessionBindingListener) { + ((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value)); + } + } + + public void removeValue(String name) { + removeAttribute(name); + } + + /** + * Clear all of this session's attributes. + */ + public void clearAttributes() { + for (Iterator> it = this.attributes.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + String name = entry.getKey(); + Object value = entry.getValue(); + it.remove(); + if (value instanceof HttpSessionBindingListener) { + ((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value)); + } + } + } + + /** + * Invalidates this session then unbinds any objects bound to it. + * + * @throws IllegalStateException if this method is called on an already invalidated session + */ + public void invalidate() { + if (this.invalid) { + throw new IllegalStateException("The session has already been invalidated"); + } + + // else + this.invalid = true; + clearAttributes(); + } + + public boolean isInvalid() { + return this.invalid; + } + + public void setNew(boolean value) { + this.isNew = value; + } + + public boolean isNew() { + return this.isNew; + } + + /** + * Serialize the attributes of this session into an object that can be + * turned into a byte array with standard Java serialization. + * + * @return a representation of this session's serialized state + */ + public Serializable serializeState() { + HashMap state = new HashMap(); + for (Iterator> it = this.attributes.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = it.next(); + String name = entry.getKey(); + Object value = entry.getValue(); + it.remove(); + if (value instanceof Serializable) { + state.put(name, (Serializable) value); + } + else { + // Not serializable... Servlet containers usually automatically + // unbind the attribute in this case. + if (value instanceof HttpSessionBindingListener) { + ((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value)); + } + } + } + return state; + } + + /** + * Deserialize the attributes of this session from a state object created by + * {@link #serializeState()}. + * + * @param state a representation of this session's serialized state + */ + @SuppressWarnings("unchecked") + public void deserializeState(Serializable state) { + Assert.isTrue(state instanceof Map, "Serialized state needs to be of type [java.util.Map]"); + this.attributes.putAll((Map) state); + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockRequestDispatcher.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockRequestDispatcher.java new file mode 100644 index 000000000000..9ce5f8d5c0b9 --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockRequestDispatcher.java @@ -0,0 +1,93 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.util.Assert; + +/** + * Mock implementation of the {@link javax.servlet.RequestDispatcher} interface. + * + *

Used for testing the web framework; typically not necessary for + * testing application controllers. + * + * @author Rod Johnson + * @author Juergen Hoeller + * @author Sam Brannen + * @since 1.0.2 + */ +public class MockRequestDispatcher implements RequestDispatcher { + + private final Log logger = LogFactory.getLog(getClass()); + + private final String resource; + + + /** + * Create a new MockRequestDispatcher for the given resource. + * @param resource the server resource to dispatch to, located at a + * particular path or given by a particular name + */ + public MockRequestDispatcher(String resource) { + Assert.notNull(resource, "resource must not be null"); + this.resource = resource; + } + + + public void forward(ServletRequest request, ServletResponse response) { + Assert.notNull(request, "Request must not be null"); + Assert.notNull(response, "Response must not be null"); + if (response.isCommitted()) { + throw new IllegalStateException("Cannot perform forward - response is already committed"); + } + getMockHttpServletResponse(response).setForwardedUrl(this.resource); + if (logger.isDebugEnabled()) { + logger.debug("MockRequestDispatcher: forwarding to [" + this.resource + "]"); + } + } + + public void include(ServletRequest request, ServletResponse response) { + Assert.notNull(request, "Request must not be null"); + Assert.notNull(response, "Response must not be null"); + getMockHttpServletResponse(response).addIncludedUrl(this.resource); + if (logger.isDebugEnabled()) { + logger.debug("MockRequestDispatcher: including [" + this.resource + "]"); + } + } + + /** + * Obtain the underlying {@link org.springframework.mock.web.MockHttpServletResponse}, unwrapping + * {@link javax.servlet.http.HttpServletResponseWrapper} decorators if necessary. + */ + protected MockHttpServletResponse getMockHttpServletResponse(ServletResponse response) { + if (response instanceof MockHttpServletResponse) { + return (MockHttpServletResponse) response; + } + if (response instanceof HttpServletResponseWrapper) { + return getMockHttpServletResponse(((HttpServletResponseWrapper) response).getResponse()); + } + throw new IllegalArgumentException("MockRequestDispatcher requires MockHttpServletResponse"); + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockServletConfig.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockServletConfig.java new file mode 100644 index 000000000000..c62ec1520d05 --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockServletConfig.java @@ -0,0 +1,103 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; + +import org.springframework.util.Assert; + +/** + * Mock implementation of the {@link javax.servlet.ServletConfig} interface. + * + *

Used for testing the web framework; typically not necessary for + * testing application controllers. + * + * @author Rod Johnson + * @author Juergen Hoeller + * @since 1.0.2 + */ +public class MockServletConfig implements ServletConfig { + + private final ServletContext servletContext; + + private final String servletName; + + private final Map initParameters = new LinkedHashMap(); + + + /** + * Create a new MockServletConfig with a default {@link MockServletContext}. + */ + public MockServletConfig() { + this(null, ""); + } + + /** + * Create a new MockServletConfig with a default {@link MockServletContext}. + * @param servletName the name of the servlet + */ + public MockServletConfig(String servletName) { + this(null, servletName); + } + + /** + * Create a new MockServletConfig. + * @param servletContext the ServletContext that the servlet runs in + */ + public MockServletConfig(ServletContext servletContext) { + this(servletContext, ""); + } + + /** + * Create a new MockServletConfig. + * @param servletContext the ServletContext that the servlet runs in + * @param servletName the name of the servlet + */ + public MockServletConfig(ServletContext servletContext, String servletName) { + this.servletContext = (servletContext != null ? servletContext : new MockServletContext()); + this.servletName = servletName; + } + + + public String getServletName() { + return this.servletName; + } + + public ServletContext getServletContext() { + return this.servletContext; + } + + public void addInitParameter(String name, String value) { + Assert.notNull(name, "Parameter name must not be null"); + this.initParameters.put(name, value); + } + + public String getInitParameter(String name) { + Assert.notNull(name, "Parameter name must not be null"); + return this.initParameters.get(name); + } + + public Enumeration getInitParameterNames() { + return Collections.enumeration(this.initParameters.keySet()); + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockServletContext.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockServletContext.java new file mode 100644 index 000000000000..7851a77e497a --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/MockServletContext.java @@ -0,0 +1,491 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.Vector; +import javax.activation.FileTypeMap; +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.web.util.WebUtils; + +/** + * Mock implementation of the {@link javax.servlet.ServletContext} interface. + * + *

Compatible with Servlet 2.5 and partially with Servlet 3.0. Can be configured to + * expose a specific version through {@link #setMajorVersion}/{@link #setMinorVersion}; + * default is 2.5. Note that Servlet 3.0 support is limited: servlet, filter and listener + * registration methods are not supported; neither is cookie or JSP configuration. + * We generally do not recommend to unit-test your ServletContainerInitializers and + * WebApplicationInitializers which is where those registration methods would be used. + * + *

Used for testing the Spring web framework; only rarely necessary for testing + * application controllers. As long as application components don't explicitly + * access the {@code ServletContext}, {@code ClassPathXmlApplicationContext} or + * {@code FileSystemXmlApplicationContext} can be used to load the context files + * for testing, even for {@code DispatcherServlet} context definitions. + * + *

For setting up a full {@code WebApplicationContext} in a test environment, + * you can use {@code AnnotationConfigWebApplicationContext}, + * {@code XmlWebApplicationContext}, or {@code GenericWebApplicationContext}, + * passing in an appropriate {@code MockServletContext} instance. You might want + * to configure your {@code MockServletContext} with a {@code FileSystemResourceLoader} + * in that case to ensure that resource paths are interpreted as relative filesystem + * locations. + * + *

A common setup is to point your JVM working directory to the root of your + * web application directory, in combination with filesystem-based resource loading. + * This allows to load the context files as used in the web application, with + * relative paths getting interpreted correctly. Such a setup will work with both + * {@code FileSystemXmlApplicationContext} (which will load straight from the + * filesystem) and {@code XmlWebApplicationContext} with an underlying + * {@code MockServletContext} (as long as the {@code MockServletContext} has been + * configured with a {@code FileSystemResourceLoader}). + * + * @author Rod Johnson + * @author Juergen Hoeller + * @author Sam Brannen + * @since 1.0.2 + * @see #MockServletContext(org.springframework.core.io.ResourceLoader) + * @see org.springframework.web.context.support.AnnotationConfigWebApplicationContext + * @see org.springframework.web.context.support.XmlWebApplicationContext + * @see org.springframework.web.context.support.GenericWebApplicationContext + * @see org.springframework.context.support.ClassPathXmlApplicationContext + * @see org.springframework.context.support.FileSystemXmlApplicationContext + */ +public class MockServletContext implements ServletContext { + + /** Default Servlet name used by Tomcat, Jetty, JBoss, and GlassFish: {@value}. */ + private static final String COMMON_DEFAULT_SERVLET_NAME = "default"; + + private static final String TEMP_DIR_SYSTEM_PROPERTY = "java.io.tmpdir"; + + private final Log logger = LogFactory.getLog(getClass()); + + private final Map contexts = new HashMap(); + + private final Map initParameters = new LinkedHashMap(); + + private final Map attributes = new LinkedHashMap(); + + private final Set declaredRoles = new HashSet(); + + private final Map namedRequestDispatchers = new HashMap(); + + private final ResourceLoader resourceLoader; + + private final String resourceBasePath; + + private String contextPath = ""; + + private int majorVersion = 2; + + private int minorVersion = 5; + + private int effectiveMajorVersion = 2; + + private int effectiveMinorVersion = 5; + + private String servletContextName = "MockServletContext"; + + private String defaultServletName = COMMON_DEFAULT_SERVLET_NAME; + + + /** + * Create a new MockServletContext, using no base path and a + * DefaultResourceLoader (i.e. the classpath root as WAR root). + * @see org.springframework.core.io.DefaultResourceLoader + */ + public MockServletContext() { + this("", null); + } + + /** + * Create a new MockServletContext, using a DefaultResourceLoader. + * @param resourceBasePath the root directory of the WAR (should not end with a slash) + * @see org.springframework.core.io.DefaultResourceLoader + */ + public MockServletContext(String resourceBasePath) { + this(resourceBasePath, null); + } + + /** + * Create a new MockServletContext, using the specified ResourceLoader + * and no base path. + * @param resourceLoader the ResourceLoader to use (or null for the default) + */ + public MockServletContext(ResourceLoader resourceLoader) { + this("", resourceLoader); + } + + /** + * Create a new MockServletContext using the supplied resource base path and + * resource loader. + *

Registers a {@link org.springframework.mock.web.MockRequestDispatcher} for the Servlet named + * {@value #COMMON_DEFAULT_SERVLET_NAME}. + * @param resourceBasePath the root directory of the WAR (should not end with a slash) + * @param resourceLoader the ResourceLoader to use (or null for the default) + * @see #registerNamedDispatcher + */ + public MockServletContext(String resourceBasePath, ResourceLoader resourceLoader) { + this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader()); + this.resourceBasePath = (resourceBasePath != null ? resourceBasePath : ""); + + // Use JVM temp dir as ServletContext temp dir. + String tempDir = System.getProperty(TEMP_DIR_SYSTEM_PROPERTY); + if (tempDir != null) { + this.attributes.put(WebUtils.TEMP_DIR_CONTEXT_ATTRIBUTE, new File(tempDir)); + } + + registerNamedDispatcher(this.defaultServletName, new MockRequestDispatcher(this.defaultServletName)); + } + + /** + * Build a full resource location for the given path, + * prepending the resource base path of this MockServletContext. + * @param path the path as specified + * @return the full resource path + */ + protected String getResourceLocation(String path) { + if (!path.startsWith("/")) { + path = "/" + path; + } + return this.resourceBasePath + path; + } + + public void setContextPath(String contextPath) { + this.contextPath = (contextPath != null ? contextPath : ""); + } + + /* This is a Servlet API 2.5 method. */ + public String getContextPath() { + return this.contextPath; + } + + public void registerContext(String contextPath, ServletContext context) { + this.contexts.put(contextPath, context); + } + + public ServletContext getContext(String contextPath) { + if (this.contextPath.equals(contextPath)) { + return this; + } + return this.contexts.get(contextPath); + } + + public void setMajorVersion(int majorVersion) { + this.majorVersion = majorVersion; + } + + public int getMajorVersion() { + return this.majorVersion; + } + + public void setMinorVersion(int minorVersion) { + this.minorVersion = minorVersion; + } + + public int getMinorVersion() { + return this.minorVersion; + } + + public void setEffectiveMajorVersion(int effectiveMajorVersion) { + this.effectiveMajorVersion = effectiveMajorVersion; + } + + public int getEffectiveMajorVersion() { + return this.effectiveMajorVersion; + } + + public void setEffectiveMinorVersion(int effectiveMinorVersion) { + this.effectiveMinorVersion = effectiveMinorVersion; + } + + public int getEffectiveMinorVersion() { + return this.effectiveMinorVersion; + } + + public String getMimeType(String filePath) { + return MimeTypeResolver.getMimeType(filePath); + } + + public Set getResourcePaths(String path) { + String actualPath = (path.endsWith("/") ? path : path + "/"); + Resource resource = this.resourceLoader.getResource(getResourceLocation(actualPath)); + try { + File file = resource.getFile(); + String[] fileList = file.list(); + if (ObjectUtils.isEmpty(fileList)) { + return null; + } + Set resourcePaths = new LinkedHashSet(fileList.length); + for (String fileEntry : fileList) { + String resultPath = actualPath + fileEntry; + if (resource.createRelative(fileEntry).getFile().isDirectory()) { + resultPath += "/"; + } + resourcePaths.add(resultPath); + } + return resourcePaths; + } + catch (IOException ex) { + logger.warn("Couldn't get resource paths for " + resource, ex); + return null; + } + } + + public URL getResource(String path) throws MalformedURLException { + Resource resource = this.resourceLoader.getResource(getResourceLocation(path)); + if (!resource.exists()) { + return null; + } + try { + return resource.getURL(); + } + catch (MalformedURLException ex) { + throw ex; + } + catch (IOException ex) { + logger.warn("Couldn't get URL for " + resource, ex); + return null; + } + } + + public InputStream getResourceAsStream(String path) { + Resource resource = this.resourceLoader.getResource(getResourceLocation(path)); + if (!resource.exists()) { + return null; + } + try { + return resource.getInputStream(); + } + catch (IOException ex) { + logger.warn("Couldn't open InputStream for " + resource, ex); + return null; + } + } + + public RequestDispatcher getRequestDispatcher(String path) { + if (!path.startsWith("/")) { + throw new IllegalArgumentException("RequestDispatcher path at ServletContext level must start with '/'"); + } + return new MockRequestDispatcher(path); + } + + public RequestDispatcher getNamedDispatcher(String path) { + return this.namedRequestDispatchers.get(path); + } + + /** + * Register a {@link javax.servlet.RequestDispatcher} (typically a {@link org.springframework.mock.web.MockRequestDispatcher}) + * that acts as a wrapper for the named Servlet. + * + * @param name the name of the wrapped Servlet + * @param requestDispatcher the dispatcher that wraps the named Servlet + * @see #getNamedDispatcher + * @see #unregisterNamedDispatcher + */ + public void registerNamedDispatcher(String name, RequestDispatcher requestDispatcher) { + Assert.notNull(name, "RequestDispatcher name must not be null"); + Assert.notNull(requestDispatcher, "RequestDispatcher must not be null"); + this.namedRequestDispatchers.put(name, requestDispatcher); + } + + /** + * Unregister the {@link javax.servlet.RequestDispatcher} with the given name. + * + * @param name the name of the dispatcher to unregister + * @see #getNamedDispatcher + * @see #registerNamedDispatcher + */ + public void unregisterNamedDispatcher(String name) { + Assert.notNull(name, "RequestDispatcher name must not be null"); + this.namedRequestDispatchers.remove(name); + } + + /** + * Get the name of the default {@code Servlet}. + *

Defaults to {@value #COMMON_DEFAULT_SERVLET_NAME}. + * @see #setDefaultServletName + */ + public String getDefaultServletName() { + return this.defaultServletName; + } + + /** + * Set the name of the default {@code Servlet}. + *

Also {@link #unregisterNamedDispatcher unregisters} the current default + * {@link javax.servlet.RequestDispatcher} and {@link #registerNamedDispatcher replaces} + * it with a {@link org.springframework.mock.web.MockRequestDispatcher} for the provided + * {@code defaultServletName}. + * @param defaultServletName the name of the default {@code Servlet}; + * never {@code null} or empty + * @see #getDefaultServletName + */ + public void setDefaultServletName(String defaultServletName) { + Assert.hasText(defaultServletName, "defaultServletName must not be null or empty"); + unregisterNamedDispatcher(this.defaultServletName); + this.defaultServletName = defaultServletName; + registerNamedDispatcher(this.defaultServletName, new MockRequestDispatcher(this.defaultServletName)); + } + + public Servlet getServlet(String name) { + return null; + } + + public Enumeration getServlets() { + return Collections.enumeration(new HashSet()); + } + + public Enumeration getServletNames() { + return Collections.enumeration(new HashSet()); + } + + public void log(String message) { + logger.info(message); + } + + public void log(Exception ex, String message) { + logger.info(message, ex); + } + + public void log(String message, Throwable ex) { + logger.info(message, ex); + } + + public String getRealPath(String path) { + Resource resource = this.resourceLoader.getResource(getResourceLocation(path)); + try { + return resource.getFile().getAbsolutePath(); + } + catch (IOException ex) { + logger.warn("Couldn't determine real path of resource " + resource, ex); + return null; + } + } + + public String getServerInfo() { + return "MockServletContext"; + } + + public String getInitParameter(String name) { + Assert.notNull(name, "Parameter name must not be null"); + return this.initParameters.get(name); + } + + public Enumeration getInitParameterNames() { + return Collections.enumeration(this.initParameters.keySet()); + } + + public boolean setInitParameter(String name, String value) { + Assert.notNull(name, "Parameter name must not be null"); + if (this.initParameters.containsKey(name)) { + return false; + } + this.initParameters.put(name, value); + return true; + } + + public void addInitParameter(String name, String value) { + Assert.notNull(name, "Parameter name must not be null"); + this.initParameters.put(name, value); + } + + public Object getAttribute(String name) { + Assert.notNull(name, "Attribute name must not be null"); + return this.attributes.get(name); + } + + public Enumeration getAttributeNames() { + return new Vector(this.attributes.keySet()).elements(); + } + + public void setAttribute(String name, Object value) { + Assert.notNull(name, "Attribute name must not be null"); + if (value != null) { + this.attributes.put(name, value); + } + else { + this.attributes.remove(name); + } + } + + public void removeAttribute(String name) { + Assert.notNull(name, "Attribute name must not be null"); + this.attributes.remove(name); + } + + public void setServletContextName(String servletContextName) { + this.servletContextName = servletContextName; + } + + public String getServletContextName() { + return this.servletContextName; + } + + public ClassLoader getClassLoader() { + return ClassUtils.getDefaultClassLoader(); + } + + public void declareRoles(String... roleNames) { + Assert.notNull(roleNames, "Role names array must not be null"); + for (String roleName : roleNames) { + Assert.hasLength(roleName, "Role name must not be empty"); + this.declaredRoles.add(roleName); + } + } + + public Set getDeclaredRoles() { + return Collections.unmodifiableSet(this.declaredRoles); + } + + + /** + * Inner factory class used to introduce a Java Activation Framework + * dependency when actually asked to resolve a MIME type. + */ + private static class MimeTypeResolver { + + public static String getMimeType(String filePath) { + return FileTypeMap.getDefaultFileTypeMap().getContentType(filePath); + } + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/PassThroughFilterChain.java b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/PassThroughFilterChain.java new file mode 100644 index 000000000000..b3484a0a83a2 --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/mock/web/PassThroughFilterChain.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.mock.web; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.springframework.util.Assert; + +/** + * Implementation of the {@link javax.servlet.FilterConfig} interface which + * simply passes the call through to a given Filter/FilterChain combination + * (indicating the next Filter in the chain along with the FilterChain that it is + * supposed to work on) or to a given Servlet (indicating the end of the chain). + * + * @author Juergen Hoeller + * @since 2.0.3 + * @see javax.servlet.Filter + * @see javax.servlet.Servlet + * @see MockFilterChain + */ +public class PassThroughFilterChain implements FilterChain { + + private Filter filter; + + private FilterChain nextFilterChain; + + private Servlet servlet; + + + /** + * Create a new PassThroughFilterChain that delegates to the given Filter, + * calling it with the given FilterChain. + * @param filter the Filter to delegate to + * @param nextFilterChain the FilterChain to use for that next Filter + */ + public PassThroughFilterChain(Filter filter, FilterChain nextFilterChain) { + Assert.notNull(filter, "Filter must not be null"); + Assert.notNull(nextFilterChain, "'FilterChain must not be null"); + this.filter = filter; + this.nextFilterChain = nextFilterChain; + } + + /** + * Create a new PassThroughFilterChain that delegates to the given Servlet. + * @param servlet the Servlet to delegate to + */ + public PassThroughFilterChain(Servlet servlet) { + Assert.notNull(servlet, "Servlet must not be null"); + this.servlet = servlet; + } + + + /** + * Pass the call on to the Filter/Servlet. + */ + public void doFilter(ServletRequest request, ServletResponse response) throws ServletException, IOException { + if (this.filter != null) { + this.filter.doFilter(request, response, this.nextFilterChain); + } + else { + this.servlet.service(request, response); + } + } + +} diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/orm/hibernate4/HibernateTransactionManagerTests.java b/spring-orm-hibernate4/src/test/java/org/springframework/orm/hibernate4/HibernateTransactionManagerTests.java new file mode 100644 index 000000000000..65cf39acaf5d --- /dev/null +++ b/spring-orm-hibernate4/src/test/java/org/springframework/orm/hibernate4/HibernateTransactionManagerTests.java @@ -0,0 +1,1399 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.orm.hibernate4; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.sql.Savepoint; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import javax.sql.DataSource; + +import junit.framework.TestCase; +import org.easymock.MockControl; +import org.hibernate.FlushMode; +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.dialect.HSQLDialect; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.exception.ConstraintViolationException; + +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.jdbc.datasource.ConnectionHolder; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; +import org.springframework.transaction.CannotCreateTransactionException; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.UnexpectedRollbackException; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.transaction.support.TransactionTemplate; + +/** + * @author Juergen Hoeller + * @since 3.2 + */ +public class HibernateTransactionManagerTests extends TestCase { + + public void testTransactionCommit() throws Exception { + MockControl dsControl = MockControl.createControl(DataSource.class); + final DataSource ds = (DataSource) dsControl.getMock(); + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + MockControl queryControl = MockControl.createControl(Query.class); + Query query = (Query) queryControl.getMock(); + + final List list = new ArrayList(); + list.add("test"); + con.getTransactionIsolation(); + conControl.setReturnValue(Connection.TRANSACTION_READ_COMMITTED); + con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); + conControl.setVoidCallable(1); + con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + conControl.setVoidCallable(1); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.getTransaction(); + sessionControl.setReturnValue(tx, 1); + tx.setTimeout(10); + txControl.setVoidCallable(1); + tx.begin(); + txControl.setVoidCallable(1); + session.connection(); + sessionControl.setReturnValue(con, 3); + session.createQuery("some query string"); + sessionControl.setReturnValue(query, 1); + query.list(); + queryControl.setReturnValue(list, 1); + tx.commit(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + session.close(); + sessionControl.setReturnValue(null, 1); + + dsControl.replay(); + conControl.replay(); + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + queryControl.replay(); + + LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { + @Override + protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) { + return sf; + } + }; + lsfb.afterPropertiesSet(); + final SessionFactory sfProxy = lsfb.getObject(); + + HibernateTransactionManager tm = new HibernateTransactionManager(); + tm.setSessionFactory(sfProxy); + tm.setDataSource(ds); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); + tt.setTimeout(10); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + Object result = tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sfProxy)); + assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds)); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sfProxy)).getSession(); + return session.createQuery("some query string").list(); + } + }); + assertTrue("Correct result list", result == list); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + dsControl.verify(); + conControl.verify(); + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + queryControl.verify(); + } + + public void testTransactionRollback() throws Exception { + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.close(); + sessionControl.setReturnValue(null, 1); + tx.rollback(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + session.connection(); + sessionControl.setReturnValue(con, 2); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + + PlatformTransactionManager tm = new HibernateTransactionManager(sf); + TransactionTemplate tt = new TransactionTemplate(tm); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + try { + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + throw new RuntimeException("application exception"); + } + }); + fail("Should have thrown RuntimeException"); + } + catch (RuntimeException ex) { + // expected + } + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + } + + public void testTransactionRollbackOnly() throws Exception { + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.flush(); + sessionControl.setVoidCallable(1); + session.close(); + sessionControl.setReturnValue(null, 1); + tx.rollback(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + session.connection(); + sessionControl.setReturnValue(con, 2); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + + PlatformTransactionManager tm = new HibernateTransactionManager(sf); + TransactionTemplate tt = new TransactionTemplate(tm); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + session.flush(); + status.setRollbackOnly(); + return null; + } + }); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + } + + public void testParticipatingTransactionWithCommit() throws Exception { + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.flush(); + sessionControl.setVoidCallable(1); + session.close(); + sessionControl.setReturnValue(null, 1); + tx.commit(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + session.connection(); + sessionControl.setReturnValue(con, 2); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + + LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { + @Override + protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) { + return sf; + } + }; + lsfb.afterPropertiesSet(); + final SessionFactory sfProxy = lsfb.getObject(); + + PlatformTransactionManager tm = new HibernateTransactionManager(sfProxy); + final TransactionTemplate tt = new TransactionTemplate(tm); + final List l = new ArrayList(); + l.add("test"); + + Object result = tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + return tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + session.flush(); + return l; + } + }); + } + }); + assertTrue("Correct result list", result == l); + + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + } + + public void testParticipatingTransactionWithRollback() throws Exception { + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.close(); + sessionControl.setReturnValue(null, 1); + tx.rollback(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + session.connection(); + sessionControl.setReturnValue(con, 2); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + + PlatformTransactionManager tm = new HibernateTransactionManager(sf); + final TransactionTemplate tt = new TransactionTemplate(tm); + try { + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + return tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + throw new RuntimeException("application exception"); + } + }); + } + }); + fail("Should have thrown RuntimeException"); + } + catch (RuntimeException ex) { + // expected + } + + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + } + + public void testParticipatingTransactionWithRollbackOnly() throws Exception { + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.close(); + sessionControl.setReturnValue(null, 1); + tx.rollback(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + session.connection(); + sessionControl.setReturnValue(con, 2); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + + PlatformTransactionManager tm = new HibernateTransactionManager(sf); + final TransactionTemplate tt = new TransactionTemplate(tm); + + try { + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + return tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + status.setRollbackOnly(); + return null; + } + }); + } + }); + fail("Should have thrown UnexpectedRollbackException"); + } + catch (UnexpectedRollbackException ex) { + // expected + } + + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + } + + public void testParticipatingTransactionWithWithRequiresNew() throws Exception { + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl session1Control = MockControl.createControl(ImplementingSession.class); + ImplementingSession session1 = (ImplementingSession) session1Control.getMock(); + MockControl session2Control = MockControl.createControl(ImplementingSession.class); + ImplementingSession session2 = (ImplementingSession) session2Control.getMock(); + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session1, 1); + sf.openSession(); + sfControl.setReturnValue(session2, 1); + session1.beginTransaction(); + session1Control.setReturnValue(tx, 1); + session2.beginTransaction(); + session2Control.setReturnValue(tx, 1); + session2.flush(); + session2Control.setVoidCallable(1); + session1.close(); + session1Control.setReturnValue(null, 1); + session2.close(); + session2Control.setReturnValue(null, 1); + tx.commit(); + txControl.setVoidCallable(2); + session1.isConnected(); + session1Control.setReturnValue(true, 1); + session1.connection(); + session1Control.setReturnValue(con, 2); + session2.isConnected(); + session2Control.setReturnValue(true, 1); + session2.connection(); + session2Control.setReturnValue(con, 2); + con.isReadOnly(); + conControl.setReturnValue(false, 2); + + sfControl.replay(); + session1Control.replay(); + session2Control.replay(); + conControl.replay(); + txControl.replay(); + + PlatformTransactionManager tm = new HibernateTransactionManager(sf); + final TransactionTemplate tt = new TransactionTemplate(tm); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + final SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); + assertTrue("Has thread session", holder != null); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + assertTrue("Not enclosing session", session != holder.getSession()); + session.flush(); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + return null; + } + }); + assertTrue("Same thread session as before", + holder.getSession() == ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession()); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + return null; + } + }); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + + sfControl.verify(); + session1Control.verify(); + session2Control.verify(); + conControl.verify(); + txControl.verify(); + } + + public void testParticipatingTransactionWithWithNotSupported() throws Exception { + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.close(); + sessionControl.setReturnValue(null, 1); + tx.commit(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + session.connection(); + sessionControl.setReturnValue(con, 2); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + + sfControl.replay(); + sessionControl.replay(); + conControl.replay(); + txControl.replay(); + + HibernateTransactionManager tm = new HibernateTransactionManager(sf); + final TransactionTemplate tt = new TransactionTemplate(tm); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); + assertTrue("Has thread session", holder != null); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertFalse(TransactionSynchronizationManager.isActualTransactionActive()); + return null; + } + }); + assertTrue("Same thread session as before", + holder.getSession() == ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession()); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + return null; + } + }); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + } + + public void testTransactionWithPropagationSupports() throws Exception { + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.flush(); + sessionControl.setVoidCallable(1); + session.close(); + sessionControl.setReturnValue(null, 1); + sfControl.replay(); + sessionControl.replay(); + + LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { + @Override + protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) { + return sf; + } + }; + lsfb.afterPropertiesSet(); + final SessionFactory sfProxy = lsfb.getObject(); + + PlatformTransactionManager tm = new HibernateTransactionManager(sfProxy); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy)); + + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy)); + assertTrue("Is not new transaction", !status.isNewTransaction()); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertFalse(TransactionSynchronizationManager.isActualTransactionActive()); + Session session = sf.openSession(); + session.flush(); + session.close(); + return null; + } + }); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy)); + sfControl.verify(); + sessionControl.verify(); + } + + public void testTransactionWithPropagationSupportsAndInnerTransaction() throws Exception { + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl session1Control = MockControl.createControl(ImplementingSession.class); + final ImplementingSession session1 = (ImplementingSession) session1Control.getMock(); + MockControl session2Control = MockControl.createControl(ImplementingSession.class); + final ImplementingSession session2 = (ImplementingSession) session2Control.getMock(); + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session1, 1); + session1.flush(); + session1Control.setVoidCallable(1); + session1.close(); + session1Control.setReturnValue(null, 1); + + sf.openSession(); + sfControl.setReturnValue(session2, 1); + session2.beginTransaction(); + session2Control.setReturnValue(tx, 1); + session2.connection(); + session2Control.setReturnValue(con, 2); + session2.flush(); + session2Control.setVoidCallable(1); + tx.commit(); + txControl.setVoidCallable(1); + session2.isConnected(); + session2Control.setReturnValue(true, 1); + session2.close(); + session2Control.setReturnValue(null, 1); + sfControl.replay(); + session1Control.replay(); + session2Control.replay(); + txControl.replay(); + + LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean() { + @Override + protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) { + return sf; + } + }; + lsfb.afterPropertiesSet(); + final SessionFactory sfProxy = lsfb.getObject(); + + PlatformTransactionManager tm = new HibernateTransactionManager(sfProxy); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS); + final TransactionTemplate tt2 = new TransactionTemplate(tm); + tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sfProxy)); + assertTrue("Is not new transaction", !status.isNewTransaction()); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertFalse(TransactionSynchronizationManager.isActualTransactionActive()); + Session session = sfProxy.openSession(); + assertSame(session1, session); + tt2.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + assertSame(session2, session); + session.flush(); + return null; + } + }); + session.flush(); + session.close(); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertFalse(TransactionSynchronizationManager.isActualTransactionActive()); + return null; + } + }); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + + sfControl.verify(); + session1Control.verify(); + session2Control.verify(); + txControl.verify(); + } + + public void testTransactionCommitWithReadOnly() throws Exception { + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + MockControl queryControl = MockControl.createControl(Query.class); + Query query = (Query) queryControl.getMock(); + + final List list = new ArrayList(); + list.add("test"); + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.setFlushMode(FlushMode.MANUAL); + sessionControl.setVoidCallable(1); + session.connection(); + sessionControl.setReturnValue(con, 2); + con.setReadOnly(true); + conControl.setVoidCallable(1); + session.createQuery("some query string"); + sessionControl.setReturnValue(query, 1); + query.list(); + queryControl.setReturnValue(list, 1); + tx.commit(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + con.isReadOnly(); + conControl.setReturnValue(true, 1); + con.setReadOnly(false); + conControl.setVoidCallable(1); + session.close(); + sessionControl.setReturnValue(null, 1); + + conControl.replay(); + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + queryControl.replay(); + + HibernateTransactionManager tm = new HibernateTransactionManager(sf); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setReadOnly(true); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + Object result = tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + return session.createQuery("some query string").list(); + } + }); + assertTrue("Correct result list", result == list); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + conControl.verify(); + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + queryControl.verify(); + } + + public void testTransactionCommitWithFlushFailure() throws Exception { + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + tx.commit(); + SQLException sqlEx = new SQLException("argh", "27"); + Exception rootCause = null; + ConstraintViolationException jdbcEx = new ConstraintViolationException("mymsg", sqlEx, null); + txControl.setThrowable(jdbcEx, 1); + rootCause = jdbcEx; + session.close(); + sessionControl.setReturnValue(null, 1); + tx.rollback(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + session.connection(); + sessionControl.setReturnValue(con, 2); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + conControl.replay(); + + HibernateTransactionManager tm = new HibernateTransactionManager(sf); + TransactionTemplate tt = new TransactionTemplate(tm); + final List l = new ArrayList(); + l.add("test"); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + try { + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + return l; + } + }); + fail("Should have thrown DataIntegrityViolationException"); + } + catch (DataIntegrityViolationException ex) { + // expected + assertEquals(rootCause, ex.getCause()); + assertTrue(ex.getMessage().indexOf("mymsg") != -1); + } + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + conControl.verify(); + } + + public void testTransactionCommitWithPreBound() throws Exception { + MockControl dsControl = MockControl.createControl(DataSource.class); + final DataSource ds = (DataSource) dsControl.getMock(); + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + session.getFlushMode(); + sessionControl.setReturnValue(FlushMode.AUTO, 2); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.connection(); + sessionControl.setReturnValue(con, 3); + con.getTransactionIsolation(); + conControl.setReturnValue(Connection.TRANSACTION_READ_COMMITTED); + con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); + conControl.setVoidCallable(1); + con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + conControl.setVoidCallable(1); + tx.commit(); + txControl.setVoidCallable(1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + session.disconnect(); + sessionControl.setReturnValue(null, 1); + + dsControl.replay(); + conControl.replay(); + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + + HibernateTransactionManager tm = new HibernateTransactionManager(); + tm.setSessionFactory(sf); + tm.setDataSource(ds); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); + final List l = new ArrayList(); + l.add("test"); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session)); + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + + Object result = tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds)); + SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); + assertTrue("Has thread transaction", sessionHolder.getTransaction() != null); + Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + assertEquals(session, sess); + return l; + } + }); + assertTrue("Correct result list", result == l); + + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); + assertTrue("Hasn't thread transaction", sessionHolder.getTransaction() == null); + TransactionSynchronizationManager.unbindResource(sf); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + dsControl.verify(); + conControl.verify(); + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + } + + public void testTransactionRollbackWithPreBound() throws Exception { + MockControl dsControl = MockControl.createControl(DataSource.class); + final DataSource ds = (DataSource) dsControl.getMock(); + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl tx1Control = MockControl.createControl(Transaction.class); + final Transaction tx1 = (Transaction) tx1Control.getMock(); + MockControl tx2Control = MockControl.createControl(Transaction.class); + final Transaction tx2 = (Transaction) tx2Control.getMock(); + + session.getFlushMode(); + sessionControl.setReturnValue(FlushMode.AUTO, 4); + session.beginTransaction(); + sessionControl.setReturnValue(tx1, 1); + tx1.rollback(); + tx1Control.setVoidCallable(1); + session.clear(); + sessionControl.setVoidCallable(1); + session.beginTransaction(); + sessionControl.setReturnValue(tx2, 1); + tx2.commit(); + tx2Control.setVoidCallable(1); + + session.isConnected(); + sessionControl.setReturnValue(true, 2); + session.connection(); + sessionControl.setReturnValue(con, 6); + con.isReadOnly(); + conControl.setReturnValue(false, 2); + session.disconnect(); + sessionControl.setReturnValue(null, 2); + + dsControl.replay(); + conControl.replay(); + sfControl.replay(); + sessionControl.replay(); + tx1Control.replay(); + tx2Control.replay(); + + HibernateTransactionManager tm = new HibernateTransactionManager(); + tm.setSessionFactory(sf); + tm.setDataSource(ds); + final TransactionTemplate tt = new TransactionTemplate(tm); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session)); + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + + try { + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds)); + SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); + assertEquals(tx1, sessionHolder.getTransaction()); + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + status.setRollbackOnly(); + Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + assertEquals(session, sess); + } + }); + } + }); + fail("Should have thrown UnexpectedRollbackException"); + } + catch (UnexpectedRollbackException ex) { + // expected + } + + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); + assertTrue("Hasn't thread transaction", sessionHolder.getTransaction() == null); + assertTrue("Not marked rollback-only", !sessionHolder.isRollbackOnly()); + + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds)); + SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); + assertEquals(tx2, sessionHolder.getTransaction()); + Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + assertEquals(session, sess); + } + }); + + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Hasn't thread transaction", sessionHolder.getTransaction() == null); + TransactionSynchronizationManager.unbindResource(sf); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + dsControl.verify(); + conControl.verify(); + sfControl.verify(); + sessionControl.verify(); + tx1Control.verify(); + tx2Control.verify(); + } + + public void testTransactionRollbackWithHibernateManagedSession() throws Exception { + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl tx1Control = MockControl.createControl(Transaction.class); + final Transaction tx1 = (Transaction) tx1Control.getMock(); + MockControl tx2Control = MockControl.createControl(Transaction.class); + final Transaction tx2 = (Transaction) tx2Control.getMock(); + + sf.getCurrentSession(); + sfControl.setReturnValue(session, 2); + session.getFlushMode(); + sessionControl.setReturnValue(FlushMode.AUTO, 4); + session.getTransaction(); + sessionControl.setReturnValue(tx1, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx1, 1); + tx1.isActive(); + tx1Control.setReturnValue(false, 1); + tx1.rollback(); + tx1Control.setVoidCallable(1); + session.getTransaction(); + sessionControl.setReturnValue(tx2, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx2, 1); + tx2.isActive(); + tx2Control.setReturnValue(false, 1); + tx2.commit(); + tx2Control.setVoidCallable(1); + + sfControl.replay(); + sessionControl.replay(); + tx1Control.replay(); + tx2Control.replay(); + + HibernateTransactionManager tm = new HibernateTransactionManager(); + tm.setSessionFactory(sf); + tm.setPrepareConnection(false); + tm.setHibernateManagedSession(true); + final TransactionTemplate tt = new TransactionTemplate(tm); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + + try { + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + status.setRollbackOnly(); + Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + assertEquals(session, sess); + } + }); + } + }); + fail("Should have thrown UnexpectedRollbackException"); + } + catch (UnexpectedRollbackException ex) { + // expected + } + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + Session sess = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + assertEquals(session, sess); + } + }); + + sfControl.verify(); + sessionControl.verify(); + tx1Control.verify(); + tx2Control.verify(); + } + + public void testExistingTransactionWithPropagationNestedAndRollback() throws Exception { + doTestExistingTransactionWithPropagationNestedAndRollback(false); + } + + public void testExistingTransactionWithManualSavepointAndRollback() throws Exception { + doTestExistingTransactionWithPropagationNestedAndRollback(true); + } + + private void doTestExistingTransactionWithPropagationNestedAndRollback(final boolean manualSavepoint) + throws Exception { + + MockControl dsControl = MockControl.createControl(DataSource.class); + final DataSource ds = (DataSource) dsControl.getMock(); + MockControl conControl = MockControl.createControl(Connection.class); + Connection con = (Connection) conControl.getMock(); + MockControl mdControl = MockControl.createControl(DatabaseMetaData.class); + DatabaseMetaData md = (DatabaseMetaData) mdControl.getMock(); + MockControl spControl = MockControl.createControl(Savepoint.class); + Savepoint sp = (Savepoint) spControl.getMock(); + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + MockControl queryControl = MockControl.createControl(Query.class); + Query query = (Query) queryControl.getMock(); + + final List list = new ArrayList(); + list.add("test"); + con.isReadOnly(); + conControl.setReturnValue(false, 1); + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.connection(); + sessionControl.setReturnValue(con, 3); + md.supportsSavepoints(); + mdControl.setReturnValue(true, 1); + con.getMetaData(); + conControl.setReturnValue(md, 1); + con.setSavepoint(ConnectionHolder.SAVEPOINT_NAME_PREFIX + 1); + conControl.setReturnValue(sp, 1); + con.rollback(sp); + conControl.setVoidCallable(1); + session.createQuery("some query string"); + sessionControl.setReturnValue(query, 1); + query.list(); + queryControl.setReturnValue(list, 1); + session.isConnected(); + sessionControl.setReturnValue(true, 1); + session.close(); + sessionControl.setReturnValue(null, 1); + tx.commit(); + txControl.setVoidCallable(1); + + dsControl.replay(); + conControl.replay(); + mdControl.replay(); + spControl.replay(); + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + queryControl.replay(); + + HibernateTransactionManager tm = new HibernateTransactionManager(); + tm.setNestedTransactionAllowed(true); + tm.setSessionFactory(sf); + tm.setDataSource(ds); + final TransactionTemplate tt = new TransactionTemplate(tm); + tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + Object result = tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds)); + if (manualSavepoint) { + Object savepoint = status.createSavepoint(); + status.rollbackToSavepoint(savepoint); + } + else { + tt.execute(new TransactionCallbackWithoutResult() { + protected void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds)); + status.setRollbackOnly(); + } + }); + } + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + return session.createQuery("some query string").list(); + } + }); + assertTrue("Correct result list", result == list); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + dsControl.verify(); + conControl.verify(); + mdControl.verify(); + spControl.verify(); + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + queryControl.verify(); + } + + public void testTransactionCommitWithNonExistingDatabase() throws Exception { + final DriverManagerDataSource ds = new DriverManagerDataSource(); + LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean(); + lsfb.setDataSource(ds); + Properties props = new Properties(); + props.setProperty("hibernate.dialect", HSQLDialect.class.getName()); + lsfb.setHibernateProperties(props); + lsfb.afterPropertiesSet(); + final SessionFactory sf = lsfb.getObject(); + + HibernateTransactionManager tm = new HibernateTransactionManager(); + tm.setSessionFactory(sf); + tm.afterPropertiesSet(); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); + tt.setTimeout(10); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + try { + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds)); + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + return session.createQuery("from java.lang.Object").list(); + } + }); + fail("Should have thrown CannotCreateTransactionException"); + } + catch (CannotCreateTransactionException ex) { + // expected + } + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + } + + public void testTransactionCommitWithPreBoundSessionAndNonExistingDatabase() throws Exception { + final DriverManagerDataSource ds = new DriverManagerDataSource(); + LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean(); + lsfb.setDataSource(ds); + Properties props = new Properties(); + props.setProperty("hibernate.dialect", HSQLDialect.class.getName()); + lsfb.setHibernateProperties(props); + lsfb.afterPropertiesSet(); + final SessionFactory sf = lsfb.getObject(); + + HibernateTransactionManager tm = new HibernateTransactionManager(); + tm.setSessionFactory(sf); + tm.afterPropertiesSet(); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); + tt.setTimeout(10); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + Session session = sf.openSession(); + TransactionSynchronizationManager.bindResource(sf, new SessionHolder(session)); + try { + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds)); + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + return session.createQuery("from java.lang.Object").list(); + } + }); + fail("Should have thrown CannotCreateTransactionException"); + } + catch (CannotCreateTransactionException ex) { + // expected + SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sf); + assertFalse(holder.isSynchronizedWithTransaction()); + } + finally { + TransactionSynchronizationManager.unbindResource(sf); + session.close(); + } + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + } + + public void testTransactionCommitWithNonExistingDatabaseAndLazyConnection() throws Exception { + DriverManagerDataSource dsTarget = new DriverManagerDataSource(); + final LazyConnectionDataSourceProxy ds = new LazyConnectionDataSourceProxy(); + ds.setTargetDataSource(dsTarget); + ds.setDefaultAutoCommit(true); + ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + //ds.setDefaultTransactionIsolationName("TRANSACTION_READ_COMMITTED"); + + LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean(); + lsfb.setDataSource(ds); + Properties props = new Properties(); + props.setProperty("hibernate.dialect", HSQLDialect.class.getName()); + props.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false"); + lsfb.setHibernateProperties(props); + lsfb.afterPropertiesSet(); + final SessionFactory sf = lsfb.getObject(); + + HibernateTransactionManager tm = new HibernateTransactionManager(); + tm.setSessionFactory(sf); + tm.afterPropertiesSet(); + TransactionTemplate tt = new TransactionTemplate(tm); + tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); + tt.setTimeout(10); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + tt.execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Has thread connection", TransactionSynchronizationManager.hasResource(ds)); + Session session = ((SessionHolder) TransactionSynchronizationManager.getResource(sf)).getSession(); + return session.createQuery("from java.lang.Object").list(); + } + }); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("Hasn't thread connection", !TransactionSynchronizationManager.hasResource(ds)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + } + + public void testTransactionFlush() throws Exception { + MockControl sfControl = MockControl.createControl(SessionFactory.class); + final SessionFactory sf = (SessionFactory) sfControl.getMock(); + MockControl sessionControl = MockControl.createControl(ImplementingSession.class); + final ImplementingSession session = (ImplementingSession) sessionControl.getMock(); + MockControl txControl = MockControl.createControl(Transaction.class); + Transaction tx = (Transaction) txControl.getMock(); + + sf.openSession(); + sfControl.setReturnValue(session, 1); + session.beginTransaction(); + sessionControl.setReturnValue(tx, 1); + session.flush(); + sessionControl.setVoidCallable(1); + tx.commit(); + txControl.setVoidCallable(1); + session.close(); + sessionControl.setReturnValue(null, 1); + + sfControl.replay(); + sessionControl.replay(); + txControl.replay(); + + HibernateTransactionManager tm = new HibernateTransactionManager(sf); + tm.setPrepareConnection(false); + TransactionTemplate tt = new TransactionTemplate(tm); + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + + tt.execute(new TransactionCallbackWithoutResult() { + public void doInTransactionWithoutResult(TransactionStatus status) { + assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(sf)); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + status.flush(); + } + }); + + assertTrue("Hasn't thread session", !TransactionSynchronizationManager.hasResource(sf)); + assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); + sfControl.verify(); + sessionControl.verify(); + txControl.verify(); + } + + protected void tearDown() { + assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty()); + assertFalse(TransactionSynchronizationManager.isSynchronizationActive()); + assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly()); + assertFalse(TransactionSynchronizationManager.isActualTransactionActive()); + } + + + public interface ImplementingSession extends Session, SessionImplementor { + } + +}