I am attempting to test a method that returns a File object using JUnit and JMockit. I am a beginner with both of these.
The problem I am having is that I can't figure out how to properly/successfully mock the implementation method returning a file, since in reality, the user has to actually select a file for the method to return. The error I keep running into is:
java.lang.IllegalStateException: Missing invocation to mocked type at this point; please make sure such invocations appear only after the declaration of a suitable mock field or parameter
Any suggestions?
Here is a recreation of my implementation:
public final class MyClass {
public static File OpenFile(Stage stage, String title, String fileTypeText, ArrayList<String> fileType) throws Exception {
File file = null;
try {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(title);
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionsFilter(fileTypeText + fileType, fileType);
fileChooser.getExtensionsFilters().add(extFilter);
file = fileChooser.showOpenDialog(stage);
return file;
}
catc (Exception e) {
if(fileType==null) {
...
}
return file;
}
}
}
Here is a recreation of my attempted JUnit test:
#Test
public void TestOpenFile(#Mocked Stage stage) throws Exception {
final ArrayList<String> extensions = new ArrayList<String>();
extensions.add(".txt");
final File file = null;
new Expectations() {{
MyClass.OpenFile(stage, anyString, anyString, extensions); returns(file);
}};
assertEquals(file, MyClass.OpenFile(stage, "some title", "some type", extensions));
}
Your solution is correct, but I would use expectations instead:
public void TestOpenFile(#Mocked FileChooser chooser) throws Exception{
new Expectations() {
{
chooser.showOpenDialog(stage); result = expectedFile;
}};
final File actualFile = MyClass.OpenFile(...);
assertEquals(expectedFile, actualFile);}
I find this easier to understand and write (my personal preference)
I realized that I was approaching the problem incorrectly at first. What I did to resolve this was:
Mock the FileChooser.showOpenDialog method to return a file instead of trying to mock my own method to return a file, which would have defeated the purpose of testing.
final File expectedFile = new File("abc");
new MockUp<FileChooser>() {
#Mock
File showOpenDialog(final Window overWindow) {
return expectedFile;
}
};
final File actualFile = MyClass.OpenFile(...);
assertEquals(expectedFile, actualFile);
Related
I have a function that save all the error in a errormessage list
public class Util {
private List<String> errorMessages = new ArrayList<>();
public void outputResult(String content) {
logger.error(content);
errorMessages.add(content);
}
}
and my compare function add all the error message to the list,
public void compare(Config source, Config target) {
if (source.getId() != target.getId()) {
util.outputResult("id not equal");
}
// ...
}
And in my main function, I call this compare function and want to save all the error message in a txt or some other file in my current directory
public class MyClass {
public void main() {
compare();
// writeToFile
}
}
This is what I'm doing right now, I convert ByteArrayOutputStream to a string and print it, there a txt file generated but is empty, and I don't want to a string, I want each error message in the list be printed, how can I do that?
ByteArrayOutputStream errorMessages = new ByteArrayOutputStream();
try (FileWriter w = new FileWriter(pathToReport)) {
w.write(errorMessages.toString());
}
File errorMessagesFile = new File(pathToReport);
errorMessagesFile.writeText(errorMessages.toString());
What logger library that you are using? If you use sl4j, you can couple it with log4j by configuring properly to just log the error messages into the file that you have specified in the configuration. I've done some lookup and I find this stackoverflow: where-does-the-slf4j-log-file-get-saved answer provided a template for you to follow on with this setup.
I added a step in my application to persist files via GridFS and added a metadata field called "processed" to work as a flag for a scheduled task that retrieves the new file and sends it on for processing. Since the Java driver for GridFS doesn't have a method allowing metadata to be updated I used MongoCollection for the "fs.files" collection to update "metadata.processing" to true.
I use GridFSBucket.find(eq("metadata.processed", false) to get the new files for processing and then update metadata.processed to true once processing is completed. This works if I add a new file while the application is running. However, if I have an existing file with "metadata.processed" set to false and start the application, the above find call returns no results. Similarly if I have a file that was already processed and I set the "metadata.processed" field back to false, the above find call also ceases working.
private static final String FILTER_STR = "'{'\"filename\" : \"{0}\"'}'";
private static final String UPDATE_STR =
"'{'\"$set\": '{'\"metadata.processed\": \"{0}\"'}}'";
#Autowired
private GridFSBucketFactory gridFSBucketFactory;
#Autowired
private MongoCollectionFactory mongoCollectionFactory;
public void storeFile(String filename, DateTime publishTime,
InputStream inputStream) {
if (fileExists(filename)) {
LOGGER.info("File named {} already exists.", filename);
} else {
uploadToGridFS(filename, publishTime, inputStream);
LOGGER.info("Stored file named {}.", filename);
}
}
public GridFSDownloadStream getFile(BsonValue id) {
return gridFSBucketFactory.getGridFSBucket().openDownloadStream(id);
}
public GridFSDownloadStream getFile(String filename) {
final GridFSFile file = getGridFSFile(filename);
return file == null ? null : getFile(file.getId());
}
public GridFSFindIterable getUnprocessedFiles() {
return gridFSBucketFactory.getGridFSBucket()
.find(eq("metadata.processed", false));
}
public void setProcessed(String filename, boolean isProcessed) {
final BasicDBObject filter =
BasicDBObject.parse(format(FILTER_STR, filename));
final BasicDBObject update =
BasicDBObject.parse(format(UPDATE_STR, isProcessed));
if (updateOne(filter, update)) {
LOGGER.info("Set metadata for {} to {}", filename, isProcessed);
}
}
private void uploadToGridFS(String filename, DateTime publishTime,
InputStream inputStream) {
gridFSBucketFactory.getGridFSBucket().uploadFromStream(filename,
inputStream, createMetadata(publishTime));
}
private GridFSUploadOptions createMetadata(DateTime publishTime) {
final Document metadata = new Document();
metadata.put("processed", false);
// metadata.put("publishTime", publishTime.toString());
return new GridFSUploadOptions().metadata(metadata);
}
private boolean fileExists(String filename) {
return getGridFSFile(filename) != null;
}
private GridFSFile getGridFSFile(String filename) {
return gridFSBucketFactory.getGridFSBucket()
.find(eq("filename", filename)).first();
}
private boolean updateOne(BasicDBObject filter, BasicDBObject update) {
try {
mongoCollectionFactory.getFsFilesCollection().updateOne(filter,
update, new UpdateOptions().upsert(true));
} catch (final MongoException e) {
LOGGER.error(
"The following failed to update, filter:{0} update:{1}",
filter, update, e);
return false;
}
return true;
}
Any idea what I can do to ensure:
GridFSBucket.find(eq("metadata.processed", false)
returns the proper results for existing files and/or files that have had the metadata changed?
The issue was due to setting the metadata.processed value as a String vs a boolean.
When initially creating the metadata I set its value with a boolean:
private GridFSUploadOptions createMetadata(DateTime publishTime) {
final Document metadata = new Document();
metadata.put("processed", false);
// metadata.put("publishTime", publishTime.toString());
return new GridFSUploadOptions().metadata(metadata);
}
And later I check for a boolean:
public GridFSFindIterable getUnprocessedFiles() {
return gridFSBucketFactory.getGridFSBucket()
.find(eq("metadata.processed", false));
}
But when updating the metadata using the "fs.files" MongoCollection I incorrectly added quotes around the boolean value here:
private static final String UPDATE_STR =
"'{'\"$set\": '{'\"metadata.processed\": \"{0}\"'}}'";
Which caused the metadata value to be saved as a String vs a boolean.
Hi I am working on a project and using PrintWriter class for opening and writing in the file. But when I am writing the test case for same it gives following error at Line 153
Wanted but not invoked:
mockPrintWriter.println("ID url1
");
-> at x.y.z.verify(ProcessImageDataTest.java:153)
Actually, there were zero interactions with this mock.
Code: (Uses Lombok Library)
ProcessImageData.java
#Setter
#RequiredArgsConstructor
public class ProcessImageData implements T {
private final File newImageDataTextFile;
#Override
public void execute() {
LineIterator inputFileIterator = null;
try {
File filteredImageDataTextFile = new File(filteredImageDataTextFilepath);
PrintWriter writer = new PrintWriter(newImageDataTextFile);
inputFileIterator = FileUtils.lineIterator(filteredImageDataTextFile, StandardCharsets.UTF_8.displayName());
while (inputFileIterator.hasNext()) {
if(someCondition)
**Line51** writer.println(imageDataFileLine);
//FileUtils.writeStringToFile(newImageDataTextFile, imageDataFileLine + NEWLINE, true);
}
}
} catch (Exception e) {
} finally {
LineIterator.closeQuietly(inputFileIterator);
**LINE63** writer.close();
}
}
ProcessImageDataTest.java
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ProcessImageData.class, FileUtils.class, Printwriter.class })
public class ProcessImageDataTest {
private ProcessImageData processImageData;
private static final String FILTERED_IMAGE_DATA_TEXT_FILE_PATH = "filteredFilepath";
private File FILTEREDFILE = new File(FILTERED_PATH);
private static final File IMAGE__FILE = new File("imageFilePath");
private LineIterator lineIterator;
#Mock
private PrintWriter mockPrintWriter;
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
processImageData = new ProcessImageData(Palettes_file, FILTERED_PATH, IMAGE_FILE);
PowerMockito.mockStatic(FileUtils.class);
PowerMockito.whenNew(PrintWriter.class).withArguments(IMAGE_FILE).thenReturn(mockPrintWriter);
PowerMockito.when(FileUtils.lineIterator(FILTERED_FILE, StandardCharsets.UTF_8.displayName())).thenReturn(lineIterator);
PowerMockito.when(lineIterator.hasNext()).thenReturn(true, true, false);
}
#Test
public void testTaskWhenIDInDBAndStale() throws IOException {
PowerMockito.when(lineIterator.nextLine()).thenReturn(ID2 + SPACE + URL1, ID1 + SPACE + URL2);
processImageData.execute();
List<String> exepctedFileContentOutput = Arrays.asList(ID2 + SPACE + URL1 + NEWLINE);
verify(exepctedFileContentOutput, 1, 1);
}
#Test
public void testTaskWhenIDNotInDB() throws IOException {
PowerMockito.when(lineIterator.nextLine()).thenReturn(ID2 + SPACE + URL1, ID3 + SPACE + URL2);
processImageData.execute();
List<String> exepctedFileContentOutput = Arrays.asList(ID3 + SPACE + URL2 + NEWLINE);
verify(exepctedFileContentOutput, 1, 1);
}
private void verify(List<String> exepctedFileContentOutput, int fileWriteTimes, int fileReadTimes) throws IOException {
for (String line : exepctedFileContentOutput){
**Line153** Mockito.verify(mockPrintWriter, Mockito.times(fileWriteTimes)).print(line);
}
PowerMockito.verifyStatic(Mockito.times(fileReadTimes));
FileUtils.lineIterator(FILTERED_IMAGE_DATA_TEXT_FILE, StandardCharsets.UTF_8.displayName());
}
}
I am mocking a new operator for PrintWriter also, injecting using beans. What is the mistake I am doing?? I am stuck on it from long time and not getting the error?
Any help is appreciated.
Updated :
I did changes suggested below and updated the code, but now I get the error:
Wanted but not invoked: mockPrintWriter.print("ASIN2 url1 "); ->
at softlines.ctl.ruleExecutor.tasks.ProcessImageDataTest.verify(ProcessImageDataTest.java:153)
However, there were other interactions with this mock: -> at softlines.ctl.ruleExecutor.tasks.ProcessImageData.execute(ProcessImageData.java:51) ->
at softlines.ctl.ruleExecutor.tasks.ProcessImageData.execute(ProcessImageData.java:51) ->
at softlines.ctl.ruleExecutor.tasks.ProcessImageData.execute(ProcessImageData.java:58) –
I see 3 issues in your test:
You don't try to mock the correct constructor, indeed in the method execute, you create your PrintWriter with only one argument of type File while you try to mock the constructor with 2 arguments one of type File and the other one of type String.
So the code should rather be:
PowerMockito.whenNew(PrintWriter.class)
.withArguments(IMAGE_FILE)
.thenReturn(mockPrintWriter);
To be able to mock a constructor you need to prepare the class creating the instance which is ProcessImageData in this case, so you need to add ProcessImageData.class in the annotation #PrepareForTest. (I'm not sure ProcessImageDataTest.class is needed there)
The field lineIterator should be annotated with #Mock.
Instead of verifying print with a new line, you should verify directly println without new line it is much less error prone.
I simplified your code to show the idea.
Assuming that ProcessImageData is:
public class ProcessImageData {
private final File newImageDataTextFile;
public ProcessImageData(final File newImageDataTextFile) {
this.newImageDataTextFile = newImageDataTextFile;
}
public void execute() throws Exception{
try (PrintWriter writer = new PrintWriter(newImageDataTextFile)) {
LineIterator inputFileIterator = FileUtils.lineIterator(
newImageDataTextFile, StandardCharsets.UTF_8.displayName()
);
while (inputFileIterator.hasNext()) {
writer.println(inputFileIterator.nextLine());
}
}
}
}
My unit test would then be:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ProcessImageData.class, FileUtils.class})
public class ProcessImageDataTest {
private File file = new File("imageFilePath");
private ProcessImageData processImageData;
#Mock
private PrintWriter mockPrintWriter;
#Mock
private LineIterator lineIterator;
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
processImageData = new ProcessImageData(file);
PowerMockito.whenNew(PrintWriter.class)
.withArguments(file)
.thenReturn(mockPrintWriter);
PowerMockito.mockStatic(FileUtils.class);
PowerMockito.when(
FileUtils.lineIterator(file, StandardCharsets.UTF_8.displayName())
).thenReturn(lineIterator);
PowerMockito.when(lineIterator.hasNext()).thenReturn(true, true, false);
}
#Test
public void testExecute() throws Exception {
PowerMockito.when(lineIterator.nextLine()).thenReturn("Foo", "Bar");
processImageData.execute();
Mockito.verify(mockPrintWriter, Mockito.times(1)).println("Foo");
Mockito.verify(mockPrintWriter, Mockito.times(1)).println("Bar");
}
}
For more details please refer to How to mock construction of new objects.
how can I add verification in unit test for writer.close?
One way could be to simply check that close() at be called once by adding the next line to your unit test:
Mockito.verify(mockPrintWriter, Mockito.times(1)).close();
Your construction of the PrintWriter doesn't match the mock. You told PowerMockito to return your mock like this:
PowerMockito.whenNew(PrintWriter.class).withArguments(IMAGE_FILE , StandardCharsets.UTF_8.name()).thenReturn(mockPrintWriter);
So you would have to say:
new PrintWriter(IMAGE_FILE, "UTF-8"); // 2 arguments
But instead in your execute method in the code that is being tested, you do:
PrintWriter writer = new PrintWriter(newImageDataTextFile); // only 1 argument
So you either need to change the PowerMockito withArguments clause, or you need to add "UTF-8" to the constructor invocation in the execute method.
I have a problem regarding java.lang.NoSuchMethodError. This program is about Compiler API (JSR 199). When I create a prototype for this it run work, but when I try to make it to become library it throw NoSuchMethodError Exception.
Here is the First Prototype:
public class DynaCompTest {
public static void main(String[] args) {
String fullName = "HelloWorld";
StringBuilder sourceCode = new StringBuilder();
sourceCode.append("public class HelloWorld {\n")
.append("\tpublic static void main(String[] args) {\n")
.append("\t\tSystem.out.println(\"Hello World\")\n")
.append("\t}\n")
.append("}");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
JavaFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
List<JavaFileObject> jFiles = new ArrayList<>();
jFiles.add(new CharSequenceJavaFileObject(fullName, sourceCode));
compiler.getTask(null, fileManager, diagnostics, null, null, jFiles).call();
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
System.out.format("Error on line %d in %s\n", diagnostic.getLineNumber(), diagnostic);
}
}
}
public class CharSequenceJavaFileObject extends SimpleJavaFileObject {
private CharSequence content;
public CharSequenceJavaFileObject(String className, CharSequence content) {
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.content = content;
}
#Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return content;
}
}
public class ClassFileManager extends ForwardingJavaFileManager {
private JavaClassObject jClassObject;
public ClassFileManager(StandardJavaFileManager standardManager) {
super(standardManager);
}
#Override
public ClassLoader getClassLoader(Location location) {
return new SecureClassLoader() {
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = jClassObject.getBytes();
return super.defineClass(name, jClassObject.getBytes(), 0, b.length);
}
};
}
#Override
public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
jClassObject = new JavaClassObject(className, kind);
return jClassObject;
}
}
public class JavaClassObject extends SimpleJavaFileObject {
protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
public JavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
}
public byte[] getBytes() {
return bos.toByteArray();
}
#Override
public OutputStream openOutputStream() {
return bos;
}
}
I changed the DynaCompTest become DynamicCompiler for the library:
public class DynamicCompiler {
private JavaCompiler compiler;
private JavaFileManager fileManager;
private List<JavaFileObject> jFiles;
private DiagnosticCollector<JavaFileObject> diagnostics;
public DiagnosticCollector<JavaFileObject> getDiagnostics() {
return diagnostics;
}
public DynamicCompiler(String className, StringBuilder sourceCode) {
compiler = ToolProvider.getSystemJavaCompiler();
fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
diagnostics = new DiagnosticCollector<>();
jFiles = new ArrayList<>();
jFiles.add(new CharSequenceJavaFileObject(className, sourceCode));
}
public boolean doCompilation() {
return compiler.getTask(null, fileManager, diagnostics, null, null, jFiles).call();
}
}
And I created Second Prototype to test the library:
public class Compiler {
private static StringBuilder sourceCode = new StringBuilder();
public static void main(String[] args) {
boolean status;
sourceCode.append("public class HelloWorld {\n")
.append("\tpublic static void main(String[] args) {\n")
.append("\t\tSystem.out.println(\"Hello World\");\n")
.append("\t}\n")
.append("}");
DynamicCompiler compiler = new DynamicCompiler("HelloWorld", sourceCode);
status = compiler.doCompilation();
StringBuilder messages = new StringBuilder();
if (!status) {
for (Diagnostic diagnostic : compiler.getDiagnostics().getDiagnostics()) {
messages.append("Error on line ")
.append(diagnostic.getLineNumber())
.append(" in ")
.append(diagnostic)
.append("\n");
}
} else {
messages.append("BUILD SUCCESSFUL ");
}
System.out.println(messages.toString());
}
}
When I test with code above it run well and print BUILD SUCCESSFUL but when I tried to make it error for example I deleted the semicolon ; like the first prototype, it throw the NoSuchMethodError Exception when access the compiler.getDiagnostics().getDiagnostics() inside the looping.
The question is, why in the First Prototype it run well when try to make an error but when I tried with my own library it become Exception?
Edit
Here is the stacktrace:
/HelloWorld.java:3: error: ';' expected
System.out.println("Hello World")
^
1 error
Exception in thread "main" java.lang.NoSuchMethodError: org.ert.lib.DynamicCompiler.getDiagnostics()Ljavax/tools/DiagnosticCollector;
at org.ert.exp.Compiler.main(Compiler.java:28)
Java Result: 1
It should be like this:
Error on line 3 in /HelloWorld.java:3: error: ';' expected
System.out.println("Hello World")
^
When trying to debug it, it shown an error:
public DiagnosticCollector<JavaFileObject> getDiagnostics() {
return diagnostics; // Set Breakpoint here
}
Here is the error message:
Not able to submit breakpoint LineBreakpoint DynamicCompiler.java : 25, reason: No executable location available at line 25 in class org.ert.lib.DynamicCompiler.
Invalid LineBreakpoint DynamicCompiler.java : 25
Update
Got the problem, this problem will occur if we add the whole project instead build the jar of the library. So when I build the library jar it works. But anyone can explain why this thing happen when I try add the whole project instead the jar file?
Note
I'm using:
JDK 1.7 from Oracle
Netbeans 7.1.1
It seems that you have similar class exists in two different libraries(jars).
e.g.
com.test.Example.class in a.jar
com.test.Example.class in b.jar
Now class loader will load the first first Example.class and it seems that you need class which is there in b.jar. Then it will not throw exception such as NoMethodFound but throw an Exception that NoSuchMethodFound because class still exists in memory but can not find required method.
Such problems can be resolved by changing library order. You need to make required library's order higher. You can do this from the eclipse
Project Setting -> Java Build Path -> Order and Export.
Hope this is helpful.
After I tried with Eclipse Indigo, I found that it works when add the Project or add the jar file. While in Netbeans 7.1.1 will get an error if add the Project, but works if add the jar file.
Maybe it one of the bugs of Netbeans...
Thank you for your attention...
I get the following problem when trying to display a list of items. For each item, I have to display an image which is dynamically loaded via a Wicket WebResource. The items are loaded step by step — 50 at a time — upon user scrolling, using an Ajax scroll.
[ERROR] 2011-04-19 09:58:18,000 btpool0-1 org.apache.wicket.RequestCycle.logRuntimeException (host=, request=, site=):
org.apache.wicket.WicketRuntimeException: component documentList:scroller:batchElem:666:content:item:3:batchItemContent:linkToPreview:imageThumbnail not found on page com.webapp.document.pages.DocumentListPage[id = 1]
listener interface = [RequestListenerInterface name=IResourceListener, method=public abstract void org.apache.wicket.IResourceListener.onResourceRequested()]
org.apache.wicket.protocol.http.request.InvalidUrlException: org.apache.wicket.WicketRuntimeException: component documentList:scroller:batchElem:666:content:item:3:batchItemContent:linkToPreview:imageThumbnail
not found on page com.webapp.document.pages.DocumentListPage[id = 1] listener interface = [RequestListenerInterface name=IResourceListener, method=public abstract void org.apache.wicket.IResourceListener.onResourceRequested()]
at org.apache.wicket.protocol.http.WebRequestCycleProcessor.resolve(WebRequestCycleProcessor.java:262)
at org.apache.wicket.RequestCycle.step(RequestCycle.java:1310)
at org.apache.wicket.RequestCycle.steps(RequestCycle.java:1428)
at org.apache.wicket.RequestCycle.request(RequestCycle.java:545)
at org.apache.wicket.protocol.http.WicketFilter.doGet(WicketFilter.java:479)
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByGuice$$51619816.CGLIB$doGet$6()
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByGuice$$51619816$$FastClassByGuice$$6d42bf5d.invoke()
at com.google.inject.internal.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:64)
at com.freiheit.monitoring.PerformanceMonitoringMethodInterceptor.invoke(PerformanceMonitoringMethodInterceptor.java:115)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:64)
at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:44)
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByGuice$$51619816.doGet()
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:312)
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByGuice$$51619816.CGLIB$doFilter$4()
How can this problem be solved?
Here is the part of the code responsible for adding the image:
previewLink.add(createThumbnailSmall("imageThumbnail", documentModel));
in
createThumbnailSmall(final String id, final IModel<BaseDocument> documentModel) {
// thumbnailResource is an object that contains the path of the image
if (thumbnailResource != null) {
final WebResource resource = getWebResource(thumbnailResource);
final Image image = new Image(id, resource);
return image;
}
return new InvisibleContainer(id);
}
WebResource getWebResource(final DocumentResource documentResource) {
return new WebResource() {
private static final long serialVersionUID = 1L;
#Override
public IResourceStream getResourceStream() {
return new BaseStreamResource(documentResource);
}
};
}
where BaseStreamResource is the following:
public class BaseStreamResource extends AbstractResourceStream {
private InputStream _fileInputStream = null;
private DocumentResource _resource = null;
public BaseStreamResource(final DocumentResource documentResource) {
_resource = documentResource;
}
#Override
public InputStream getInputStream() throws ResourceStreamNotFoundException {
if (_fileInputStream == null) {
try {
if (_resource == null) {
throw new ResourceStreamNotFoundException("Resource was null");
}
_fileInputStream = _resource.getFileInputStream();
} catch (final ResourceNotAvailableException ex) {
throw new ResourceStreamNotFoundException(ex);
}
}
return _fileInputStream;
}
In HTML:
<a wicket:id="linkToPreview" href="#">
<img wicket:id="imageThumbnail" alt="Attachment"></img></a>
The code added hasn't really added any clues for me, but maybe I can help narrow it down a bit anyway.
The stacktrace includes a reference to com.webapp.document.pages.DocumentListPage, which is likely calling some of the code you've posted. The error indicates a bad url, so debugging into that class, adding debug prints, and looking at the values of any field containing a url might be worthwhile.
It might even help to modify the code in DocumentListPage (maybe temporarily for debugging) to catch org.apache.wicket.protocol.http.request.InvalidUrlException and adding debugging prints specifically when the exception is caught.
This isn't really an answer, but it's too big for a comment, and maybe it'll help you get closer to an answer.
The following solution solved the problem:
- extend WebResource class
- add extended class as a resource to application shared resources
Ex:
public class MyWebResource extends WebResource {
final ValueMap map = new ValueMap();
#Override
public IResourceStream getResourceStream() {
String fileName = getFileName();
File file = new File(basePath, fileName);
if (!file.exists()) {
LOG.error("File does not exist: " + file);
throw new IllegalStateException("File does not exist: " + file);
}
return new FileResourceStream(file);
}
public final void addResource() {
Application.get().getSharedResources().add(getClass().getName(), this);
}
protected String getFileName() {
return getParameters().getString("id");
}
public final String urlFor(final String fileName) {
final ResourceReference resourceReference = new ResourceReference(getClass().getName());
final String encodedValue = WicketURLEncoder.QUERY_INSTANCE.encode(fileName);
map.add("id", encodedValue);
final CharSequence result = RequestCycle.get().urlFor(resourceReference, map);
if (result == null) {
throw new IllegalStateException("The resource was not added! "
+ "In your Application class add the following line:"
+ "MyConcreteResource.INSTANCE.addResource()");
}
String absoluteUrl = RequestUtils.toAbsolutePath(result.toString());
return absoluteUrl;
}
}
In Application class, in init(), I have added MyWebResource to shared resources:
public void init() {
...
new MyWebResource().addResource();
...
}