I am working on a Java application, and the functionality I am working on is to download a file after populating it's content from the database when the user clicks on export button, the file is generated correctly, the problem I have is when I try to set ByteArrayResource into the ResponseEntity, when I call the webservice from angular, I get Byte array resource [resource loaded from byte array] cannot be resolved to absolute file path, here is my code :
Controller method :
#GetMapping(value = "/exportods/{id}")
public ResponseEntity<ExportDTO> exportODSFile(#PathVariable String id)
throws InvalidationRequestNotFoundException, IOException, ExportInvalidationRequestException {
File odsFfile = null;
ByteArrayResource resource = null;
ExportInvalidationRequestDTO exportInvalidationRequestDTO = new ExportInvalidationRequestDTO();
try {
//getting the file
odsFfile = exportService.exportInvalidationRequest(id);
Path pathObj = Paths.get(odsFfile.getCanonicalPath());
//creating the resource
resource = new ByteArrayResource(Files.readAllBytes(pathObj));
exportDTO.setResource(resource);
} catch (IOException e) {
exportDTO.setError(new ErrorDTO(HttpStatus.NOT_FOUND.value()));
}
return ResponseEntity.ok(exportDTO);
}
ExportDTO :
public class ExportDTO extends AbstractReturnDTO {
private ByteArrayResource resource;
public ExportDTO (ByteArrayResource resource) {
this.resource = resource;
}
public ExportDTO () {
}
public ByteArrayResource getResource() {
return resource;
}
public void setResource(ByteArrayResource resource) {
this.resource = resource;
}
}
Angular component method:
async downloadODSFile(id) {
await this.requestService.downloadODSFile(id).subscribe((requestExportODS: RequestExportODS) => {
if (!requestExportODS) {
if(requestExportODS.error == null){
//handle error here
}
else{
saveAs(new Blob([requestExportODS.file], { type: \'application/vnd.oasis.opendocument.spreadsheet\'' }), id );
}
}
});
}
Once I click on the export button, I get an error :
error: "Internal Server Error"
message: "Byte array resource [resource loaded from byte array] cannot be resolved to absolute file path"
Set the observe as response in the http service method
//requestService
downloadODSFile(id) {
return this.http.get<HttpResponse<any>>(`/exportods/${id}`, { observe: 'response' })
}
And use it like this
async downloadODSFile(id) {
await this.requestService.downloadODSFile(id).subscribe(res => {
console.log(res.body) //Byte array
});
}
BTW async and await are redundant in your code, as you haven't converted observable to promise.
Related
I have used ThreadPoolTaskExecutor class to call my #Async annotated method as number of api calls are more then 130k+ so I am trying to achieve it through async api calls using executor framework, but once the list through which I am streaming and making async calls gets completed then next flow is getting executed, but here I want to wait until for all async calls gets completed. Which means I want to wait until I will get api response for all 130k+ call which has been made async while streaming the list
public void downloadData(Map.Entry<String, String> entry, String downloadPath,
Locale locale, ApiClient apiClient, Task task,
Set<Locale> downloadFailedLocales) {
String targetFileName = entry.getKey() + ".xml";
Path filePath = null;
try {
filePath = getTargetDestination(downloadPath, "2", entry.getKey(), targetFileName);
MultiValueMap<String, String> queryParameters = restelApiClient.fetchQueryParameters();
if (downloadPath != null && !downloadFileService.localFileExists(filePath)) {
fetchCountryAndHotelList(entry.getValue(), filePath, task, downloadFailedLocales, locale, queryParameters);
//After fetching hotelList proceed for fetching hotelInfo from hotelList xml Data
if (entry.getKey().equals(HotelConstants.HOTEL_LIST)) {
//fetching hotelCodes from downloaded xml of hotelList, to make API calls for hotelInfo
List<String> hotelInfoArray = getHotelCodeList(filePath);
AtomicInteger hotelCounter = new AtomicInteger();
String hotelInfoXml = apiClient.getApiClientSettings().getEndpoints()
.get(HotelConstants.HOTEL_INFO);
/*Fetching data from HotelInfo API Async but once it will stream the hotelinfo list then next flow of code execute and it won't wait all api calls to be made and get the response back. */
hotelInfoArray.stream().forEach(hotel -> {
StringBuilder fileName = new StringBuilder();
fileName.append(HotelConstants.HOTEL_INFO).append(hotelCounter.getAndIncrement()).append(".xml");
Path path = getTargetDestination(downloadPath, "2", HotelConstants.HOTEL_INFO,
fileName.toString());
StringBuilder hotelCode = new StringBuilder();
hotelCode.append("<codigo>").append(hotel).append("</codigo>");
String xml = String.format(hotelInfoXml).replace("<codigo></codigo>", hotelCode);
try {
hotelDataFetchThreadService.fetchHotelInfo(xml, path, task, downloadFailedLocales, locale, queryParameters);
} catch (DownloadFailedException e) {
log.info("Download failed for hotel code {} with exception {}", hotel, e);
downloadFileService.deleteIncompleteFiles(path);
}
});
}
} else {
log.info("file already exist skipping downloading again");
}
} catch (DownloadException e) {
downloadFileService.deleteIncompleteFiles(filePath);
log.info("Download failed for endpoint {} with exception {}", entry.getKey(), e);
} catch (DownloadFailedException e) {
throw new RuntimeException(e);
}
}
/*
This method make api call and write the xml response in local file in async way
*/
#Async("TestExecutor")
public void fetchHotelInfo(String xml, Path path, Task task, Set<Locale> downloadFailedLocales, Locale locale,
MultiValueMap<String, String> queryParameters) throws DownloadFailedException {
Flux<DataBuffer> bufferedData;
try {
// log.info("using thread {}", Thread.currentThread().getName());
bufferedData = apiClient.getWebClient()
.uri(uriBuilder -> uriBuilder
.queryParams(queryParameters)
.queryParam(HotelConstants.XML, xml.trim())
.build()
).retrieve()
.bodyToFlux(DataBuffer.class)
.retryWhen(Retry.fixedDelay(maxRetryAttempts, Duration.ofSeconds(maxRetryDelay))
.onRetryExhaustedThrow(
(RetryBackoffSpec retryBackoffSpec, Retry.RetrySignal retrySignal) -> {
throw new DownloadException(
"External Service failed to process after max retries");
}));
writeBufferDataToFile(bufferedData, path);
} catch (DownloadException e) {
downloadFileService.deleteIncompleteFiles(path);
downloadFailedLocales.add(locale);
if (locale.equals(task.getJob().getProvider().getDefaultLocale().getLocale())) {
throw new DownloadFailedException(
String.format("Network issue during download, Max retry reached: %s", e.getMessage()), e);
}
log.info("Download failed for with exception ", e);
}
}
Below is the service method (JsonObjectBuilderService) that converts an object (FeatureCollectionForGeoJson) to a jsonStr. This service method is used in the Get RequestMapping to send a response to the front-end.
The FeatureCollectionForGeoJson object is a class mapped for GeoJson FeatureCollection.
The GeometryForGeoJson is another class that contains the string type with "Point" value and the array that contains the latitude and longitude for the point.
The PropertyForGeoJson class contains information/properties about that pin that will be displayed in the pop-up when the pin is clicked on on the map.
#Getter
#Setter
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class FeatureForGeoJson {
private final String type = "Feature";
private GeometryForGeoJson geometry;
private PropertyForGeoJson properties;
}
#Service
public class JsonObjectBuilderService {
public String transformObjectToGeoJson(FeatureCollectionForGeoJson featureCollectionForGeoJson){
ObjectMapper Obj = new ObjectMapper();
String jsonStr = null;
try {
jsonStr = Obj.writeValueAsString(featureCollectionForGeoJson);
} catch (JsonProcessingException e) {
e.printStackTrace();
} //catch (IOException e) {
return jsonStr;
}
}
This is the GetMapping that sends the response to Angular
#GetMapping("/power-plants")
public ResponseEntity<String> getAllPowerPlants() {
try {
FeatureCollectionForGeoJson powerPlantsToFeatureCollectionForGeoJson ;
//jpa query for the database to return the information
List<PowerPlant> powerPlantList = powerPlantJpaService.findAll();
if (powerPlantList.isEmpty()) {
logger.info("The power plant list is empty.");
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
logger.info("The power plant list is populated and has been returned successfully.");
powerPlantsToFeatureCollectionForGeoJson = transformPowerPlantsToFeaturesCollection.transformPowerPlantToGeoJsonElements(powerPlantList);
String objectToGeoJson = jsonObjectBuilderService.transformObjectToGeoJson(powerPlantsToFeatureCollectionForGeoJson);
logger.info(objectToGeoJson);
return new ResponseEntity<>(objectToGeoJson, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
This is how the response looks like in the browser
This is the Angular method that fetches the response.
This is the Angular component where I call the service method that fetches the response and where I want to add the pins to the map with the pop-ups.
How do I take that response from the API (line 27 from Home.component.ts -right above- or the getAll() method from the PowerPlantService) and process it to extract the Point Geometry, to create a pin with it and extract the properties to add to a pop-up to the pin?
if you use angular you should use Observables and not Promises, also avoid to post images of code, now I can't copy/paste you code.
what you want to do is return an observable in getAll(), something like this:
// in component
this.powerPlantService.getAll$().subscribe(
res => this.featureCollection = res,
err => console.log(err)
);
// in service
getAll$(): Observable<any[]> {
return this.http.get(baseUrl).pipe(
map(data => {
// transform your data here, or remove this pipe if you don't need it
return data;
})
);
}
you can transform your features in a flat object like this:
return this.http.get(baseUrl).pipe(
map(features => {
return features.map(f => {
const pointGeometry: any = {
...f.geometry,
...f.properties
};
return pointGeometry;
});
})
);
If you want to know how the back end formats and sends the response, please check in the body of the question.
Below is the service method that performs a GET request to the back end.
export class PowerPlantService {
constructor(private http: HttpClient) { }
getAll() {
return this.http.get(baseUrl);
}
Below is the component method that subscribes to the answer and adds the elements to the map.
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
private latitude: number = 45.6427;
private longitude: number = 25.5887;
private map!: L.Map;
private centroid: L.LatLngExpression = [this.latitude, this.longitude];
ngOnInit(): void {
this.initMap();
}
constructor(private powerPlantService: PowerPlantService) {
}
private initMap(): void {
this.map = L.map('map', {
center: this.centroid,
zoom: 2.8
});
const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
minZoom: 2.8,
attribution: '© OpenStreetMap'
});
tiles.addTo(this.map);
this.powerPlantService.getAll().subscribe((data: any)=>{
console.log(data);
L.geoJSON(data).addTo(this.map)
})
I am using Blueimp and server side is Java, Struts2. I couldn't find examples using Java, anyway I managed to use the sample code, but I am getting "Empty file upload result" when I am trying to upload a single file also. The HTML part is the same, I am not pasting here as it may go lengthy.
The jQuery is:
$(document).ready(function () {
'use strict';
// Initialize the jQuery File Upload widget:
$('#fileupload').fileupload();
// Enable iframe cross-domain access via redirect option:
$('#fileupload').fileupload(
'option',
'redirect',
window.location.href.replace(
/\/[^\/]*$/,
'/cors/result.html?%s'
)
);
if (window.location.hostname === 'blueimp.github.com') {
// Demo settings:
$('#fileupload').fileupload('option', {
url: '//jquery-file-upload.appspot.com/',
maxFileSize: 5000000,
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
process: [
{
action: 'load',
fileTypes: /^image\/(gif|jpeg|png)$/,
maxFileSize: 20000000 // 20MB
},
{
action: 'resize',
maxWidth: 1440,
maxHeight: 900
},
{
action: 'save'
}
]
});
// Upload server status check for browsers with CORS support:
if ($.support.cors) {
$.ajax({
url: '//jquery-file-upload.appspot.com/',
type: 'HEAD'
}).fail(function () {
$('<span class="alert alert-error"/>')
.text('Upload server currently unavailable - ' +
new Date())
.appendTo('#fileupload');
});
}
} else {
// Load existing files:
$('#fileupload').each(function () {
var that = this;
$.getJSON(this.action, function (result) {
if (result && result.length) {
$(that).fileupload('option', 'done')
.call(that, null, {result: result});
}
});
});
}
});
The action:
#Namespace("/")
#InterceptorRefs({
#InterceptorRef("fileUpload"),
#InterceptorRef("basicStack")
})
public class UploadAction extends ActionSupport implements ServletRequestAware, ServletResponseAware{
HttpServletRequest req;
HttpServletResponse res;
// private File fileUploadPath=new File("c:\\temp\\");
private List<File> uploads = new ArrayList<File>();
private List<String> uploadFileNames = new ArrayList<String>();
private List<String> uploadContentTypes = new ArrayList<String>();
public List<File> getUploads() {
return uploads;
}
public void setUploads(List<File> uploads) {
this.uploads = uploads;
}
public List<String> getUploadFileNames() {
return uploadFileNames;
}
public void setUploadFileNames(List<String> uploadFileNames) {
this.uploadFileNames = uploadFileNames;
}
public List<String> getUploadContentTypes() {
return uploadContentTypes;
}
public void setUploadContentTypes(List<String> uploadContentTypes) {
this.uploadContentTypes = uploadContentTypes;
}
#Action(value="upload", results = { #Result(name="success", type="json")
})
public String uploadFiles() throws IOException
{
System.out.println("upload1");
System.out.println("files:");
for (File u: uploads) {
System.out.println("*** "+u+"\t"+u.length());
}
System.out.println("filenames:");
for (String n: uploadFileNames) {
System.out.println("*** "+n);
}
System.out.println("content types:");
for (String c: uploadContentTypes) {
System.out.println("*** "+c);
}
System.out.println("\n\n");
if (!ServletFileUpload.isMultipartContent(req)) {
throw new IllegalArgumentException("Request is not multipart, please 'multipart/form-data' enctype for your form.");
}
return SUCCESS;
}
#Override
public void setServletRequest(HttpServletRequest hsr) {
this.req=hsr;
}
#Override
public void setServletResponse(HttpServletResponse hsr) {
this.res=hsr;
}
}
As I said, I have changed the action file, but I still get all empty values for files, and in the Firebug's GET response I see "Request is not multipart, please 'multipart/form-data' enctype for your form".
You may use fileUpload interceptor to parse your "multipart/form-data" requests. It uses the same commons-fileupload implementation wrapped by the MultipartRequestWrapper in prepare operations by the Struts2 dispatcher. More about how to file upload with examples you could find here.
EDIT: This occurs during any component AJAX call.
I am building a web application using ICEFaces 3.2.0 community along with Spring Security 3.2 Everything has been going very well up until a few days ago. I have an ACE AutoCompleteEntry component in the page with a backing bean attached to the value as the following example:
<ace:autoCompleteEntry id="autoCompleteState"
label="State"
labelPosition="top"
value="#{autoCompleteEntry.selectedText}"
rows="10" width="160"
filterMatchMode="startsWith">
<f:selectItems value="#{autoCompleteEntry.states}"/>
</ace:autoCompleteEntry>
The backing bean attached is as follows:
#ManagedBean(name=AutoCompleteEntry.BEAN_NAME)
#SessionScoped
public class AutoCompleteEntry implements Serializable {
public static final String BEAN_NAME = "autoCompleteEntry";
public static final String STATE_FILENAME = "State_Names.txt";
public static final String RESOURCE_PATH = "/resources/selectinputtext/";
public AutoCompleteEntry() {
}
public List<SelectItem> states;
public List<SelectItem> getStates() {
if(states == null) {
states = new ArrayList<SelectItem>();
for(String state : readStateFile()) {
states.add(new SelectItem(state));
}
}
return states;
}
private String selectedText = null;
public String getSelectedText() {return selectedText;}
public void setSelectedText(String selectedText) {this.selectedText = selectedText;}
private static List<String> readStateFile() {
InputStream fileIn = null;
BufferedReader in = null;
try {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
fileIn = ec.getResourceAsStream(AutoCompleteEntry.RESOURCE_PATH + STATE_FILENAME);
if(fileIn != null) {
in = new BufferedReader(new InputStreamReader(fileIn));
List<String> loadedStates = new ArrayList<String>(53);
String read;
while((read = in.readLine()) != null) {
loadedStates.add(read);
}
return loadedStates;
}
}catch (IOException failedRead) {
failedRead.printStackTrace();
}finally {
try {
if(in != null) {
in.close();
}
}catch (IOException failedClose) {
failedClose.printStackTrace();
}
}
List<String> errorReturn = new ArrayList<String>(1);
errorReturn.add("Error Loading State List");
return errorReturn;
}
}
The problem is that each time I attempt to test the component instead of bringing up a list of the States it redirects to an absolute path of my main page, which results in a 404. In developer tools I see an error of:
> Uncaught TypeError: Cannot read property 'value' of undefined
bridge.uncompressed.js.jsf:2701
namespace.onAfterUpdate.viewIDElement bridge.uncompressed.js.jsf:2701
apply bridge.uncompressed.js.jsf:122
(anonymous function) bridge.uncompressed.js.jsf:484
(anonymous function) bridge.uncompressed.js.jsf:363
(anonymous function) bridge.uncompressed.js.jsf:240
broadcast bridge.uncompressed.js.jsf:483
(anonymous function) bridge.uncompressed.js.jsf:1928
sendEvent jsf.js.jsf:1447
AjaxEngine.req.sendRequest jsf.js.jsf:1333
request jsf.js.jsf:1834
fullSubmit bridge.uncompressed.js.jsf:2309
submit bridge.uncompressed.js.jsf:2314
iceSubmit compat.uncompressed.js.jsf:1523
onclick
The developer tools log shows:
> [window] persisted focus for element "autoCompleteState_input"
bridge.uncompressed.js.jsf:1252
[window] full submit to localhost:8181/HHCA_Portal/pages/secure/HHCA.jsf
javax.faces.execute: #all
javax.faces.render: patientRecordsForm
javax.faces.source: autoCompleteState_input
view ID: v33tl98j
event type: unknown bridge.uncompressed.js.jsf:1252
XHR finished loading: "localhost:8181/HHCA_Portal/pages/secure/HHCA.jsf".
jsf.js.jsf:1334
AjaxEngine.req.sendRequest jsf.js.jsf:1334
request jsf.js.jsf:1834
fullSubmit bridge.uncompressed.js.jsf:2309
ice.ace.AjaxRequest ace-jquery.uncompressed.js.jsf:20854
ice.ace.ab ace-jquery.uncompressed.js.jsf:20779
ice.ace.Autocompleter.getUpdatedChoices autocompleteentry.js.jsf:695
ice.ace.Autocompleter.onObserverEvent autocompleteentry.js.jsf:637
(anonymous function)
I have spent many hours working on this and other issues, and I have run out of ideas. If someone has some kind of assistance I would really appreciate the help.
If you are using JSF 2 then you can add your own exception handler , this should be able to capture ajax requests.
<factory>
<exception-handler-factory>
test.MyExceptionHandlerFactory
</exception-handler-factory>
</factory>
see the examples here ,
http://balusc.blogspot.com/2012/03/full-ajax-exception-handler.html
http://wmarkito.wordpress.com/2012/04/05/adding-global-exception-handling-using-jsf-2-x-exceptionhandler/
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();
...
}