Uploading multiple files using Spring MVC 3.0.2 after HiddenHttpMethodFilter has been enabled - spring

Important : This question is completely useless for any Spring version higher than 3.0.4 as the issue discussed in this thread had been fixed in that version a long ago and is no longer reproducible in subsequent versions of Spring.
I'm using Spring version 3.0.2. I need to upload multiple files using the multiple="multiple" attribute of a file browser such as,
<input type="file" id="myFile" name="myFile" multiple="multiple"/>
(and not using multiple file browsers something like the one stated by this answer, it indeed works I tried).
Although no versions of Internet Explorer supports this approach unless an appropriate jQuery plugin/widget is used, I don't care about it right now (since most other browsers support this).
This works fine with commons fileupload but in addition to using RequestMethod.POST and RequestMethod.GET methods, I also want to use other request methods supported and suggested by Spring like RequestMethod.PUT and RequestMethod.DELETE in their own appropriate places. For this to be so, I have configured Spring with HiddenHttpMethodFilter which goes fine as this question indicates.
but it can upload only one file at a time even though multiple files in the file browser are chosen. In the Spring controller class, a method is mapped as follows.
#RequestMapping(method={RequestMethod.POST}, value={"admin_side/Temp"})
public String onSubmit(#RequestParam("myFile") List<MultipartFile> files, #ModelAttribute("tempBean") TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) throws IOException, FileUploadException {
for (MultipartFile file : files) {
System.out.println(file.getOriginalFilename());
}
}
Even with the request parameter #RequestParam("myFile") List<MultipartFile> files which is a List of type MultipartFile (it can always have only one file at a time).
I could find a strategy which is likely to work with multiple files on this blog. I have gone through it carefully.
The solution below the section SOLUTION 2 – USE THE RAW REQUEST says,
If however the client insists on using the same form input name such
as ‘files[]‘ or ‘files’ and then populating that name with multiple
files then a small hack is necessary as follows. As noted above Spring
2.5 throws an exception if it detects the same form input name of type file more than once. CommonsFileUploadSupport – the class which throws
that exception is not final and the method which throws that exception
is protected so using the wonders of inheritance and subclassing one
can simply fix/modify the logic a little bit as follows. The change
I’ve made is literally one word representing one method invocation
which enables us to have multiple files incoming under the same form
input name.
It attempts to override the method
protected MultipartParsingResult parseFileItems(List fileItems, String encoding){}
of the abstract class CommonsFileUploadSupport by extending the class CommonsMultipartResolver such as,
package multipartResolver;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.commons.fileupload.FileItem;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
public final class MultiCommonsMultipartResolver extends CommonsMultipartResolver {
public MultiCommonsMultipartResolver() {}
public MultiCommonsMultipartResolver(ServletContext servletContext) {
super(servletContext);
}
#Override
#SuppressWarnings("unchecked")
protected MultipartParsingResult parseFileItems(List fileItems, String encoding) {
Map<String, MultipartFile> multipartFiles = new HashMap<String, MultipartFile>();
Map multipartParameters = new HashMap();
// Extract multipart files and multipart parameters.
for (Iterator it = fileItems.iterator(); it.hasNext();) {
FileItem fileItem = (FileItem) it.next();
if (fileItem.isFormField()) {
String value = null;
if (encoding != null) {
try {
value = fileItem.getString(encoding);
} catch (UnsupportedEncodingException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not decode multipart item '" + fileItem.getFieldName()
+ "' with encoding '" + encoding + "': using platform default");
}
value = fileItem.getString();
}
} else {
value = fileItem.getString();
}
String[] curParam = (String[]) multipartParameters.get(fileItem.getFieldName());
if (curParam == null) {
// simple form field
multipartParameters.put(fileItem.getFieldName(), new String[]{value});
} else {
// array of simple form fields
String[] newParam = StringUtils.addStringToArray(curParam, value);
multipartParameters.put(fileItem.getFieldName(), newParam);
}
} else {
// multipart file field
CommonsMultipartFile file = new CommonsMultipartFile(fileItem);
if (multipartFiles.put(fileItem.getName(), file) != null) {
throw new MultipartException("Multiple files for field name [" + file.getName()
+ "] found - not supported by MultipartResolver");
}
if (logger.isDebugEnabled()) {
logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize()
+ " bytes with original filename [" + file.getOriginalFilename() + "], stored "
+ file.getStorageDescription());
}
}
}
return new MultipartParsingResult(multipartFiles, multipartParameters);
}
}
What happens is that the last line in the method parseFileItems() (the return statement) i.e.
return new MultipartParsingResult(multipartFiles, multipartParameters);
causes a compile-time error because the first parameter multipartFiles is a type of Map implemented by HashMap but in reality, it requires a parameter of type MultiValueMap<String, MultipartFile>
It is a constructor of a static class inside the abstract class CommonsFileUploadSupport,
public abstract class CommonsFileUploadSupport {
protected static class MultipartParsingResult {
public MultipartParsingResult(MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams) {}
}
}
The reason might be - this solution is about the Spring version 2.5 and I'm using the Spring version 3.0.2 which might be inappropriate for this version.
I however tried to replace the Map with MultiValueMap in various ways such as the one shown in the following segment of code,
MultiValueMap<String, MultipartFile>mul=new LinkedMultiValueMap<String, MultipartFile>();
for(Entry<String, MultipartFile>entry:multipartFiles.entrySet()) {
mul.add(entry.getKey(), entry.getValue());
}
return new MultipartParsingResult(mul, multipartParameters);
but no success. I'm not sure how to replace Map with MultiValueMap and even doing so could work either. After doing this, the browser shows the Http response,
HTTP Status 400 -
type Status report
message
description The request sent by the client was syntactically incorrect
().
Apache Tomcat/6.0.26
I have tried to shorten the question as possible as I could and I haven't included unnecessary code.
How could be made it possible to upload multiple files after Spring has been configured with HiddenHttpMethodFilter?
That blog indicates that It is a long standing, high priority bug.
If there is no solution regarding the version 3.0.2 (3 or higher) then I have to disable Spring support forever and continue to use commons-fileupolad as suggested by the third solution on that blog omitting the PUT, DELETE and other request methods forever.
Very little changes to the code in the parseFileItems() method inside the class MultiCommonsMultipartResolver might make it upload multiple files but I couldn't succeed in my attempts (again with the Spring version 3.0.2 (3 or higher)).

For upload multiple files in one request I used this code:
I have such jsp:
<p>Select files to upload. Press Add button to add more file inputs.</p>
<table>
<tr>
<td><input name="files" type="file" multiple="true"/></td>
</tr>
<tr>
<td><input name="files" type="file" multiple="true"/></td>
</tr>
</table>
<br/><input type="submit" value="Upload" />
File upload class bean:
import org.springframework.web.multipart.commons.CommonsMultipartFile;
public class FileUploadForm {
private CommonsMultipartFile [] files;
public CommonsMultipartFile[] getFiles() {
return files;
}
public void setFiles( CommonsMultipartFile[] files ) {
this.files = files;
}
}
Controller:
#Controller
#RequestMapping("/upload")
public class FileUploadController {
#RequestMapping(method = RequestMethod.GET)
public String displayForm(ModelMap modelMap) {
modelMap.addAttribute( new FileUploadForm() );
return "uploadForm.jsp";
}
#RequestMapping(method = RequestMethod.POST)
public String save(FileUploadForm uploadForm) {
CommonsMultipartFile[] files = uploadForm.getFiles();
if(files != null && files.length != 0) {
for(MultipartFile file : files) {
System.out.println( file.getOriginalFilename() );
}
}
return "success.jsp";
}
}
This code allows to upload multiple files in one request,
and be possible to get instance of CommonsMultipartFile for each file.

The issue as mentioned in the question was fixed as of Spring 3.0.4. As such, if you happened to use that version or higher (yes, it is 4.x.x now), you would not need to read this question/answer(s) anymore.

Related

How to fix issue RestControllerAdivse not working?

I have an issue related to RestControllerAdvice.
I have built an internal jar file as my own library and I implement some exception handler.
Anyway, that RestControllerAdvice is not working when have throw exception error.
RestControllerAdvice
#RestControllerAdvice
public class ApiControllerHandler {
#ExceptionHandler(ApiException.class)
public #ResponseBody
ApiResponse handleApiRequestException(ApiException e) {
ApiResponse response = new ApiResponse();
response.setCode(e.response.getCode());
response.setMessage(e.response.getMessage());
return response;
}
}
validator method
public static void request(JSONObject jsonReq, String requestKey) throws ApiException{
if (requestKey.isEmpty()) {
throw new ApiException("01", "Please input request validate key");
}
String key = jsonReq.getString(requestKey);
if (StringUtils.isEmpty(key)) {
throw new ApiException("01", requestKey + " Can not be null or empty.");
}
}
RestController
#PostMapping("/")
public String index(#RequestBody Map<String, Object> map){
JSONObject jsonObject = new JSONObject(map);
SPNValidator.request(jsonObject, "username");
return "Hello";
}
Request
{
"username" : ""
When post this request, exception will be throw because I already handled request not empty nor null
but my restControlleradvise is not working, it throws internal exception error.
Note: it works as normal if i use the same project,
but when build as jar file for other use, this function not work.
thanks.
One of the possible reason
When you build as internal JAR at that time spring dosen't know about any class in JAR so it will not search any package/Class from jar file so that's why your #RestControllerAdvice from internal JAR is not working
To solve this please use your internal JAR pacakge name in #ComponentScan
like below
#ComponentScan(basePackages = {"com.exception.base"})
so spring will inlcude ApiControllerHandler while scanning you project file.

Error casting object MethodSignature. Spring AOP

Thanks in advance for your support.
Currently I´m stuck in the next problem. I developed an Aspect class to validate my input JSON from al the pkg of RestController.
Complying with certain characteristics.
Each method of my controllers returns a different DTO object.
I created a new generic object to return it from my aspect, when my logic is not fulfilled. When I do tests, I get an error of CannotCastClass "xxxxDTO" to newErrorResponseDTO.
Currently I already can obtain the method signature or the object type. My idea is to cast the return type (from methodSignature) to my new DTOResponse. The object response is always different.
I mention that the architecture and design of the total project was already developed. I only did the aspect
At the moment, I have not succeeded.
I attach evidence. Thanks
I tried ResponseAdvice, and multiple ways to cast objects.
I prefer to stay in the aspect. I get the solution changing all the response DTO in controller to Object generic. Asumming that doing is bad practice, i prefer real solution
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
// Other imports missing...
#Aspect
#Component("validateParameterAspect")
public class ValidatorParameterAspect {
public static final Logger logger = Logger.getLogger(ValidatorParameterAspect.class);
#Autowired
ServiciosRest servicio;
#Pointcut("execution(* com.actinver.rest.*.* (..))")
public void executeController() {}
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void logRequestMapping() {}
#Around("logRequestMapping() && executeController() && args(..,#RequestBody requestBody) ")
public Object logRequestBody(ProceedingJoinPoint joinPoint, Object requestBody) throws Throwable {
String vlDataDecrypt = "";
try {
// output = joinPoint.proceed();
System.out.println("--------------123------------");
logger.warn("Entering in Method : " + joinPoint.getSignature().getName());
logger.warn("Class Name : " + joinPoint.getSignature().getDeclaringTypeName());
logger.warn("Arguments : " + Arrays.toString(joinPoint.getArgs()));
logger.warn("Target class : " + joinPoint.getTarget().getClass().getName());
SimpleJSONDataContainer args = (SimpleJSONDataContainer) joinPoint.getArgs()[0];
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Class<?> ret = sign.getReturnType();
String returnString = sign.getReturnType().getName();
logger.warn("Signature : " + ret);
vlDataDecrypt = AESHelper.decrypt(servicio.getSeedWord(), args.getData());
logger.info(" Decrypt -> " + vlDataDecrypt);
logger.info("args " + args.getData());
ErrorDataResponse res = validDataEmpty(args.getData());
if (res.getResult() == "2") {
return res; // or cast Class<?>
//return ret.cast(res);
}
} catch (Exception e) {
logger.error("Stack trace -> ", e);
}
return joinPoint.proceed();
}
public ErrorDataResponse validDataEmpty(String vlDataDecrypt) {
ErrorDataResponse errorDto = new ErrorDataResponse();
if (vlDataDecrypt == null || vlDataDecrypt.hashCode() == "77631826690E45839D7B49B932CBC81B".hashCode()
&& vlDataDecrypt.equalsIgnoreCase("77631826690E45839D7B49B932CBC81B")) {
errorDto.setResult("2");
errorDto.setMensaje(RestValidatorUtil.EnumErrors.ERROR_INPUT.getMsg());
logger.info("JSON null" + errorDto.getResult());
return errorDto;
}
return errorDto;
}
}
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
// Other imports missing...
#RestController
#RequestMapping("inicio")
public class Bursanet {
public final static Logger logger = Logger.getLogger(Bursanet.class);
#RequestMapping(
value = "cashByDate",
method = { RequestMethod.GET, RequestMethod.POST },
consumes = "application/json",
produces = "application/json"
)
public CashByDateDTO cashByDate(
#RequestBody SimpleJSONDataContainer simpleJSONDataContainer,
Authentication authentication
) {
String vlDataDecrypt = "";
CashByDateDTO outJson = new CashByDateDTO();
CashByDateRequest request = null;
try {
UsernamePasswordAuthenticationToken userPasswordAuthenticationToken =
(UsernamePasswordAuthenticationToken)
((OAuth2Authentication) authentication).getUserAuthentication();
//////example
return outJson;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
It is very difficult to analyse your code because you are not providing an MCVE:
There are no package names in your classes.
There are no imports either.
You use several project-specific classes (not part of the Spring Framework) the code of which you also don't share here.
There is no Spring configuration either.
So I have to make some educated guesses here. From what I can see, I can tell you this:
If you expect ValidatorParameterAspect.logRequestBody(..) to intercept execution of Bursanet.cashByDate(..), it should not work because
in args(.., #RequestBody requestBody) you are expecting that parameter to be the last one in the target method's signature, but actually in Bursanet.cashByDate(..) it is the first one. So the pointcut should never match.
Again in args(.., #RequestBody requestBody) you ought to use a fully qualified class name, i.e. args(.., #org.springframework.web.bind.annotation.RequestBody requestBody).
Please also note that execution(* com.actinver.rest.*.* (..)) only matches methods in classes residing directly in the com.actinver.rest package, not in any subpackages. If you want to include those too, you need to change the pointcut to execution(* com.actinver.rest..* (..)).
In your question you mention you only want to intercept REST controllers, but you do not limit pointcut matching to classes with a #RestController annotation. You could do that via #within(org.springframework.web.bind.annotation.RestController). Right now you are doing it indirectly by only relying on methods with #annotation(org.springframework.web.bind.annotation.RequestMapping), which will also work as long as those methods only occur in #RequestController classes. Probably this is the case in your application, I am just mentioning it as a detail.
Instead of SimpleJSONDataContainer args = (SimpleJSONDataContainer) joinPoint.getArgs()[0];, why don't you bind the first argument to a SimpleJSONDataContainer parameter via args() and then just use the currently unused requestBody advice method parameter in your code? Something like this:
#Around("logRequestMapping() && executeController() && args(#org.springframework.web.bind.annotation.RequestBody requestBody, ..)")
public Object logRequestBody(ProceedingJoinPoint joinPoint, SimpleJSONDataContainer requestBody) throws Throwable {
// (...)
vlDataDecrypt = AESHelper.decrypt(servicio.getSeedWord(), requestBody.getData());
logger.info(" Decrypt -> " + vlDataDecrypt);
logger.info("args " + requestBody.getData());
ErrorDataResponse res = validDataEmpty(requestBody.getData());
// (...)
}
You define MethodSignature sign = (MethodSignature) joinPoint.getSignature(); but don't use it above several times where you repeatedly call joinPoint.getSignature(), too. Instead you could just reorganise the code like this:
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
System.out.println("--------------123------------");
logger.warn("Entering in Method : " + methodSignature.getName());
logger.warn("Class Name : " + methodSignature.getDeclaringTypeName());
logger.warn("Arguments : " + Arrays.toString(joinPoint.getArgs()));
logger.warn("Target class : " + joinPoint.getTarget().getClass().getName());
Class<?> ret = methodSignature.getReturnType();
String returnString = methodSignature.getReturnType().getName();
I never understood why so many people call many JoinPoint methods in order to extract details for logging if instead they could simply log the joinpoint instance. This would show the type of pointcut (e.g. execution()) as well as the target method signature. Okay, if you want to list all method arguments, you can do this additionally, but how about this, wouldn't that be enough?
logger.warn(joinPoint);
// logger.warn("Entering in Method : " + methodSignature.getName());
// logger.warn("Class Name : " + methodSignature.getDeclaringTypeName());
logger.warn("Arguments : " + Arrays.toString(joinPoint.getArgs()));
// logger.warn("Target class : " + joinPoint.getTarget().getClass().getName());
This whole code block I guess you can also remove. It even prints wrong information and calls the return type "signature":
Class<?> ret = methodSignature.getReturnType();
String returnString = methodSignature.getReturnType().getName();
logger.warn("Signature : " + ret);
Now for the part which is probably your problem:
ErrorDataResponse res = validDataEmpty(requestBody.getData());
if (res.getResult() == "2") {
return res; // or cast Class<?>
//return ret.cast(res);
}
Here you are making the aspect advice skip the joinPoint.proceed() call and return another object instead. The method you intercept has the signature public CashByDateDTO cashByDate(..), i.e. it returns a specific DTO type. If you want to return an ErrorDataResponse instead, this would only work if ErrorDataResponse was a subtype of CashByDateDTO, which probably it is not. From the class names I would even say that a *Response and a *DTO are completely different object types. Your advice cannot just change or ignore the method signature. You have to return a CashByDateDTO object, no matter what. If you cannot do that here, maybe you are intercepting the wrong method or trying to do the wrong thing in your aspect.
Sorry for the lengthy reply, but there is so much chaos in your code, I had to point out some details.

How to use uploaded images inside JSP pages (Spring MVC)

I'm implementing a web application using Spring MVC. I'm trying to implement the module that allows to upload images. I'm using Apache Commons FileUpload and this is the controller that handle the post request:
/**
* Upload single file using Spring Controller
*/
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public String uploadFileHandler(#RequestParam("name") String name,
#RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
String fileContentType = file.getContentType();
if (contentTypes.contains(fileContentType)) {
// You have the correct extension
// rest of your code here
try {
byte[] bytes = file.getBytes();
// Creating the directory to store file
String rootPath = System.getProperty("catalina.home");
File dir = new File(rootPath + File.separator + "bills");
if (!dir.exists())
dir.mkdirs();
// Create the file on server
File serverFile = new File(dir.getAbsolutePath()
+ File.separator + name);
BufferedOutputStream stream = new BufferedOutputStream(
new FileOutputStream(serverFile));
stream.write(bytes);
stream.close();
System.out.println("Server File Location="
+ serverFile.getAbsolutePath());
return "redirect:/";
} catch (Exception e) {
//TODO handle error
}
} else {
//TODO handle error
}
} else {
//TODO handle error
}
}
My first doubt is where should i save the images uploaded? Right now the directory is inside a GlassFish folder, is it ok? And I don't know why but the uploaded picture has no extension... is a simple file without any extension!
Now I want to let the user access these images but I don't know how to insert those inside the JSP page. I know that I should save the path inside the database relating it to a specified user but I don't know what to do next. Does anyone have any suggestions? Thank you very much!
About the way how to use uploaded images, I found this solution that seems working but I don't know if it's the best one or not. Inside the jsp file I have this:
<spring:url value="/file/download/" var="url"/>
<html>
<head>
<title>Title</title>
</head>
<body>
<img src="${url}${imageName}"/>
</body>
</html>
Where imageName is a value inside the model that contains, as the name clearly says, the name of the file, and url contains the path of the service that returns the stream of the image. This HTTP request is handled by the following controller:
public #ResponseBody byte[] getImageWithMediaType(#PathVariable("name") String name) throws IOException {
String rootPath = System.getProperty("catalina.home")
String partialPath = File.separator + "bills" + File.separator + name + ".png"
FileInputStream file = new FileInputStream(rootPath + partialPath);
InputStream is = new BufferedInputStream(file);
return IOUtils.toByteArray(is);
}
Do you think this is the best way to do so?
Why dont you use Spring Content? Then you don't need to implement any of that controller code at all. Assuming you are using Spring Boot (let me know if you are not) then it would look something like this:
pom.xml
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest-boot-starter</artifactId>
<version>0.0.10</version>
</dependency>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>content-fs-spring-boot-starter</artifactId>
<version>0.0.10</version>
</dependency>
SpringBootApplication.java
#SpringBootApplication
public class YourSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(YourSpringBootApplication.class, args);
}
#Configuration
#EnableFilesystemStores
public static class StoreConfig {
File filesystemRoot() {
String rootPath = System.getProperty("catalina.home");
File dir = new File(rootPath + File.separator + "bills");
if (!dir.exists())
dir.mkdirs();
}
// this bean is the spring resource loader that will be used by
// the product store
#Bean
public FileSystemResourceLoader fsResourceLoader() throws Exception
{
return new FileSystemResourceLoader(filesystemRoot().getAbsolutePath());
}
}
#StoreRestResource(path="upload")
public interface BillStore extends Store<String> {
//
}
}
Note that you are not writing any controller code here but this is enough to create a REST-based content service at /upload that actually supports full CRUD functionality (as well as video streaming in case that us useful to you). Create == POST, Read == GET (include byte-range support), Update == PUT, Delete == DELETE.
e.g.
POST /upload/my-image.jpg
will store the uploaded image to System.getProperty("catalina.home") + File.Separator + "bills" + "my-image/jpg".
I am assuming you want your users to eventually be able to view their images after upload, upload new versions and possibly delete as well. Given this /upload is probably not a great name. But it is what you used in the question so what I went with in my answer. IF you really do just want upload functionality then you can use Spring Security to make the other actions impossibly to perform.

JSF PrimeFaces - store uploaded file path in another bean's attribute

To sum things up: I want to upload file passed by user through a form with <p:fileUpload> using fileUploadController ManagedBean as its fileUploadListener to a certain path, then get this path along with filename and Store it somehow (the question is - how?) in the wydarzenieMB ManagedBean because I need filePath as a String in my addWydarzenie() method to store this path in a database and later use it.
I've got a table in my database named "Wydarzenie". It has many values like name etc. which I am specyfing in my JSF form for user to fill using primefaces. There is no problem with obtaining them, I just use ManagedBean to store input and later use this input to write into DataBase using addWydarzenie() method.
Part of utworzWydarzenie.xhtml form:
...
<h:outputLabel for="opis" value="Opis :" />
<p:inputTextarea id="opis" value="#{wydarzenieMB.opis}" label="opis">
...
<p:commandButton id="addWydarzenie" value="Zatwierdź" action="#{wydarzenieMB.addWydarzenie}" ajax="false"/>
My addWydarzenie() method in wydarzenieManagedBean:
public String addWydarzenie() {
try {
Wydarzenie wydarzenie = new Wydarzenie();
...
wydarzenie.setOpis(getOpis());
...
getWydarzenieServiceImpl().addWydarzenie(wydarzenie);
return SUCCESS;
} catch (DataAccessException e) {
e.printStackTrace();
}
return ERROR;
}
There is no problem with values like that where I use p:inputText for example, because I specify value of bean (for example: value="#{wydarzenieMB.opis}") in which I want to store them. Problem begins with my <p:fileUpload> field because:
1. I am not specyfing value there because there is no value returned(?) - only file uploaded
2. I am using fileUploadController ManagedBean to handle file uploading BUT I want the filePath String of uploaded file that it possess to be stored in my wydarzenieMB ManagedBean.
Here's my uploading file code from utworzWydarzenie.xhtml form:
...
<h:outputLabel for="plakat" value="Plakat :" />
<p:fileUpload id="plakat" update="messages" fileUploadListener="#{fileUploadController.handleFileUpload}" multiple="false" sizeLimit="1000000" allowTypes="/(\.|\/)(gif|jpe?g|png)$/" />
<p:growl id="messages" showDetail="true"/>
...
And my fileUploadController class:
#ManagedBean(name="fileUploadController")
#RequestScoped
public class FileUploadController
{
private String destination="E:/PROJEKT ZESPOLOWY/events/WebContent/resources/plakaty/";
private String sciezkaPliku = ""; // complete file path including destination and file name
public void handleFileUpload(FileUploadEvent event) throws IOException {
FacesMessage msg = new FacesMessage("Plik: ", event.getFile().getFileName() + " został poprawnie wysłany na serwer.");
FacesContext.getCurrentInstance().addMessage(null, msg);
String path = destination;
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddHHmmss");
String name = fmt.format(new Date())
+ event.getFile().getFileName().substring(
event.getFile().getFileName().lastIndexOf('.'));
File file = new File(path + name);
sciezkaPliku += path + name; // I set file path here
InputStream is = event.getFile().getInputstream();
OutputStream out = new FileOutputStream(file);
byte buf[] = new byte[1024];
int len;
while ((len = is.read(buf)) > 0)
out.write(buf, 0, len);
is.close();
out.close();
}
public String getSciezkaPliku() {
return sciezkaPliku;
}
public void setSciezkaPliku(String sciezkaPliku) {
this.sciezkaPliku = sciezkaPliku;
}
}
It is declared as a ManagedBean because I've tried to access its sciezkaPliku variable which is filepath from wydarzenieMB like this:
#ManagedProperty(value="#{fileUploadController.sciezkaPliku}")
private String plakat;
But it was always null. I also tried to get whole wydarzenieMB inside fileUploadController and use it's setter method:
#ManagedBean(name="fileUploadController")
#RequestScoped
public class FileUploadController
{
#ManagedProperty(value="#{wydarzenieMB}")
WydarzenieManagedBean w;
public void handleFileUpload(FileUploadEvent event) throws IOException {
...
sciezkaPliku += path + name; // I set file path here
w.setPlakat(sciezkaPliku);
...
}
...
}
But it was unsuccessful either. File is uploaded and saved in a specified folder without a problem, when I print sciezkaPliku from handleFileUpload it is ok, but when I am creating my new Wydarzenie using addWydarzenie() it is always null.
Any ideas?
If I've understood your question clearly, I suggest you to change your scope to "#SessionScoped" in FileUploadController. I had a same problem and it was solved by this way.
check it out and inform me of the result ;)

Get resteasy servlet context without annotation params

Quick project explanation: We have a built application based on JSF2 + Spring with Dynamic data sources. The data reference control is made with a spring-config:
<bean id="dataSource" class="com.xxxx.xxxx.CustomerRoutingDataSource">
....
and a class (referenced above):
public class CustomerRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return CustomerContextHolder.getCustomerType();
}
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
the CustomerContextHolder called above is as follows:
public class CustomerContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}
public static String getCustomerType() {
String manager = (String)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("dataBaseManager");
if (manager != null) {
contextHolder.set(manager);
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("dataBaseManager", null);
} else {
String base = (String)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("currentDatabBase");
if (base != null)
contextHolder.set(base);
}
return (String) contextHolder.get();
}
public static void clearCustomerType() {
contextHolder.remove();
}
}
The problem is that the last guy is calling FacesContext.getCurrentInstance() to get the servlet context. Just to explain, it uses the session Attribute dataBaseManager to tell which base it should use.
For the actual solution it was working fine, but with the implementation of a RESTEASY web service, when we make a get request the FacesContext.getCurrentInstance() is obviously returning null and crashing.
I searched a lot and could not find a way of getting the servlet-context from outside of the #GET params. I would like to know if is there any way of getting it, or if there is another solution for my dynamic datasource problem.
Thanks!
Like magic and probably not much people know.
I searched deep into the Resteasy documentation, and found a part of springmvc plugin that comes with the resteasy jars, that has a class called RequestUtil.class.
With that I was able to use the method getRequest() without the "#Context HttpServletRequest req" param.
Using that I was able to set the desired database on the request attributes, and from another thread (called by spring) get it and load the stuff from the right place!
I'm using it for a week now and it works like a charm. Only thing that I needed to do is change the determineLookupKey() above to this:
#Override
protected String determineCurrentLookupKey() {
if (FacesContext.getCurrentInstance() == null) {
//RESTEASY
HttpServletRequest hsr = RequestUtil.getRequest();
String lookUpKey = (String) hsr.getAttribute("dataBaseManager");
return lookUpKey;
}else{
//JSF
return CustomerContextHolder.getCustomerType();
}
}
Hope this helps other people!
Thiago

Resources