We are running Struts 2.5.14.1 and working on externalizing Tomcat session state. This requires Serializable sessions. However, our Action with the ExecuteAndWait interceptor fails. Since our original code was quite complex I wrote a simpler one below which demonstrates the exact same behavior.
The simple action is shown here:
package com.sentrylink.web.actions;
import java.util.concurrent.TimeUnit;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.InterceptorRefs;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import com.opensymphony.xwork2.ActionSupport;
#SuppressWarnings("serial")
#Results({
#Result(name="wait", location="/"),
#Result(name=ActionSupport.SUCCESS, location="/WEB-INF/content/messagePage.jsp"),
})
#InterceptorRefs({
#InterceptorRef("webStack"),
#InterceptorRef("execAndWait")
})
public class TestExecuteAndWait extends ActionSupport {
public String execute() throws Exception {
TimeUnit.SECONDS.sleep(10);
return SUCCESS;
}
}
Running this gives
WARNING: Cannot serialize session attribute __execWaittest-execute-and-wait for session 74CDB9F8D00BBC697030AFC6978E94F6
java.io.NotSerializableException: com.opensymphony.xwork2.inject.ContainerImpl$ConstructorInjector
It appears that Struts is pulling in an unwanted item for serialization. It may be related to the bug described here, although the fix put in for that bug appears to be present in 2.5.14.1 (not surprisingly, since that fix was in 2013).
I suspect this is a bug in the framework, but before I go ahead and file a report, and figure out a workaround for myself, I thought I would see if anyone had a solution or had ever gotten ExecuteAndWait to work with serialized sessions.
Related
I have configured Spock Global Extension and static class ErrorListener inside it. Works fine for test errors when I want to catch feature title and errors if they happen. But how can I add some custom information to the listener?
For example I have test that calls some API. In case it fails I want to add request/response body to the listener (and report it later). Obviously I have request/response inside the feature or I can get it. How can I pass this information to the Listener and read later in the handling code?
package org.example
import groovy.json.JsonSlurper
import org.spockframework.runtime.AbstractRunListener
import org.spockframework.runtime.extension.AbstractGlobalExtension
import org.spockframework.runtime.model.ErrorInfo
import org.spockframework.runtime.model.IterationInfo
import org.spockframework.runtime.model.SpecInfo
import spock.lang.Specification
class OpenBrewerySpec extends Specification{
def getBreweryTest(){
def breweryText = new URL('https://api.openbrewerydb.org/breweries/1').text
def breweryJson = new JsonSlurper().parseText(breweryText)
//TODO catch breweryText for test result reporting if it is possible
expect:
breweryJson.country == 'United States'
}
def cleanup() {
specificationContext.currentSpec.listeners
.findAll { it instanceof TestResultExtension.ErrorListener }
.each {
def errorInfo = (it as TestResultExtension.ErrorListener).errorInfo
if (errorInfo)
println "Test failure in feature '${specificationContext.currentIteration.name}', " +
"exception class ${errorInfo.exception.class.simpleName}"
else
println "Test passed in feature '${specificationContext.currentIteration.name}'"
}
}
}
class TestResultExtension extends AbstractGlobalExtension {
#Override
void visitSpec(SpecInfo spec) {
spec.addListener(new ErrorListener())
}
static class ErrorListener extends AbstractRunListener {
ErrorInfo errorInfo
#Override
void beforeIteration(IterationInfo iteration) {
errorInfo = null
}
#Override
void error(ErrorInfo error) {
errorInfo = error
}
}
}
Create file src/test/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalExtension
and place string "org.example.TestResultExtension" there to enable extension.
I am pretty sure you found my solution here. Then you also know that it is designed to know in a cleanup() methods if the test succeeded or failed because otherwise Spock does not make the information available. I do not understand why deliberately omitted that information and posted a fragment instead of the whole method or at least mentioned where your code snippet gets executed. That is not a helpful way of asking a question. Nobody would know except for me because I am the author of this global extension.
So now after having established that you are inside a cleanup() method, I can tell you: The information does not belong into the global extension because in the cleanup() method you have access to information from the test such as fields. Why don't you design your test in such a way that whatever information cleanup() needs it stored in a field as you would normally do without using any global extensions? The latter is only meant to help you establish the error status (passed vs. failed) as such.
BTW, I even doubt if you need additional information in the cleanup() method at all because its purpose it cleaning up, not reporting or logging anything. For that Spock has a reporting system which you can also write extensions for.
Sorry for not being more specific in my answer, but your question is equally unspecific. It is an instance of the XY problem, explaining how you think you should do something instead of explaining what you want to achieve. Your sample code omits important details, e.g. the core test code as such.
Consider the following Java code:
import java.util.LinkedList;
import java.util.List;
class Library {
List<String> loans = new LinkedList<>();
public List<String> searchUser(String name) {
List<String> usersFound = new LinkedList<>();
return loans;
}
}
and the following Rascal module:
module Mwe
import lang::java::flow::JavaToObjectFlow;
import lang::java::jdt::m3::AST;
import IO;
void m() {
ast = createAstFromEclipseFile(|project://test/src/test.java|, true);
fp = createOFG({ast});
print(fp);
}
The resulting flow program will be:
flowProgram({
attribute(|java+field:///Library/loans|),
method(|java+method:///Library/searchUser(java.lang.String)|,[|java+parameter:///Library/searchUser(java.lang.String)/scope(name)/scope(0)/name|]),
constructor(|java+constructor:///Library/Library()|,[])
},{
assign(|java+method:///Library/searchUser(java.lang.String)/return|,|id:///|,|java+field:///Library/loans|),
newAssign(|java+variable:///Library/searchUser(java.lang.String)/usersFound|,|java+class:///java/util/LinkedList|,|java+constructor:///java/util/LinkedList/LinkedList()|,[])
})
So, there is a new assignment of LinkedList to usersFound, but nothing comparable for loans. Why would that happen? Is that the intended behaviour?
Just checked the implementation, the field initializers are not included in the getStatements function (see lang::java::flow::JavaToObjectFlow on line 169). Similarly the static initializers of a class are ignored.
The best way forward would be to either report it as a bug, or fix it and turn it into a pull-request. (pull request is the quickest way to getting it fixed on unstable)
As a possible, yet work intensive workaround you rewrite the AST to put the field initializers inside all existing constructors (or add a constructor if there is none).
I have read several articles on dependency injection and cannot seem to get it working the way I think it should. From what I have read, you can use the #Injectable decorator on a class and then the metadata is created for the DI like so:
import {Hero} from './hero.model';
import {Injectable} from 'angular2/angular2'
#Injectable()
export class HeroService {
constructor() {
console.log('Hero Service Created');
}
}
Then in your component, you can use it in your constructor (with the proper import) like this:
constructor(heroService: HeroService) {
console.log('App Component Created');
}
However, I get the following error: Cannot resolve all parameters for AppComponent(?). Make sure they all have valid type or annotations.
I am able to get it working properly if I remove the #Injectable syntax from the service and instead have my constructor like this:
constructor(#Inject(HeroService) heroService: HeroService) {
console.log('App Component Created');
}
With everything that I've read, these should do the same thing, but they aren't. Any ideas as to why? I am using Typescript 1.6.2 with VS 2013, angular 2 v2.0.0-alpha.46, and systemjs v0.19.5.
Make sure that you've specified "emitDecoratorMetadata" option in your TypeScript configuration.
I'm using the newest versions of junit and jmockit and Oracle JDK 7 in Eclipse. When I try to mock java.net.URL my test won't run.
I have in my code something like:
URL url = new URL("String representing the url.");
So I figured in my test I could mock this like so:
#Mocked private URL _url;
Since this works for pretty much everything else, I know URL is final but I thought that was okay with JMockit.
When I run a test class with the above mock in eclipse the result is a grey line(as opposed to green or red.) So I'm assuming some kind of initialization problem. The rest of the test or code doesn't seem to matter, no matter what I put that #Mocked line in, this happens.
A workaround would be great, an explanation of what is actually causing this would be even better. Any help is definitely appreciated! Thanks!
Quick example. This actually gives an exception, but I think it is basically doing the same thing I have seen:
package demo;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
public class Connecting {
public boolean connectionattempt() throws IOException {
URL url = new URL("http://nowhere/");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (connection != null) {
return true;
}
else {
return false;
}
}
}
And this test:
package demo;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import org.junit.Test;
import mockit.Expectations;
import mockit.Mocked;
import mockit.Tested;
public class TestConnecting {
#Mocked URL _url;
#Mocked HttpURLConnection _connection;
#Tested Connecting _sut;
#Test
public void testConnect() throws IOException {
new Expectations() { {
_url.openConnection(); result = _connection;
} };
assertEquals(true, _sut.connectionattempt());
}
}
and the stack trace:
java.lang.NoClassDefFoundError: org/eclipse/jdt/internal/junit/runner/TestReferenceFailure
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestListener.testFailure(JUnit4TestListener.java:91)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestListener.testFailure(JUnit4TestListener.java:69)
at org.junit.runner.notification.RunNotifier$4.notifyListener(RunNotifier.java:139)
at org.junit.runner.notification.RunNotifier$SafeNotifier.run(RunNotifier.java:61)
at org.junit.runner.notification.RunNotifier.fireTestFailures(RunNotifier.java:134)
at org.junit.runner.notification.RunNotifier.fireTestFailure(RunNotifier.java:128)
at org.junit.internal.runners.model.EachTestNotifier.addFailure(EachTestNotifier.java:23)
at org.junit.runners.ParentRunner.run(ParentRunner.java:315)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
I executed the test on Eclipse Kepler SR2, IntelliJ IDEA 13.1, and Netbeans 8.0.1, using JMockit 1.13, JUnit 4.11, and Oracle JDK 1.7.0_67.
The test passes in every case, it's all green! So, I don't know what could possibly be the problem in your environment. Are you sure the "newest version" of JMockit (1.13 at this time) was the one actually used?
In my Grails app, I need to bind a request parameter to a Date field of a command object. In order to perform the String-to-Date conversion, one needs to register an appropriate PropertyEditor in grails-app\conf\spring\resources.groovy
I've added the following bean definiton:
import org.springframework.beans.propertyeditors.CustomDateEditor
import java.text.SimpleDateFormat
beans = {
paramDateEditor(CustomDateEditor, new SimpleDateFormat("dd/MM/yy"), true) {}
}
But I'm still getting an error:
java.lang.IllegalArgumentException: Could not parse date: Unparseable date: "04/01/99"]
I think there's probably just something wrong with the way I've defined the bean, but I've no idea what?
The piece you are missing is registering of the new property editor. The following worked for me when I upgraded to Grails 1.1 and had to bind dates in the MM/dd/yyyy format.
grails-app/config/spring/resources.groovy:
beans = {
customPropertyEditorRegistrar(util.CustomPropertyEditorRegistrar)
}
src/groovy/util/CustomPropertyEditorRegistrar.groovy:
package util
import java.util.Date
import java.text.SimpleDateFormat
import org.springframework.beans.propertyeditors.CustomDateEditor
import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistry
public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("dd/MM/yy"), true));
}
}