I'm using Struts2 2.5.20 and have written a file upload action that almost works. I can see in the debugger that setReport(), setReportContentType() and setReportFileName() are being called and the parameter values are correct. However, the execute() method is never invoked. I tried following the call stack to see where it should have been called, but there's just layer after layer of interceptors that I can't find the bottom.
Does anyone know what the reason for execute() might be?
public class ImportReportAction extends CampaignerActionSupport implements ValidationAware
{
private File report;
private String reportContentType;
private String reportFileName;
#Override
public String execute() throws Exception
{
...
return SUCCESS;
}
public void setReport(
File report)
{
this.report = report;
}
public void setReportContentType(
String reportContentType)
{
this.reportContentType = reportContentType;
}
#RequiredFieldValidator(type = ValidatorType.FIELD, key = "errors.required", messageParams = { "getText('labels.report.file')" })
public void setReportFileName(
String reportFileName)
{
this.reportFileName = reportFileName;
}
}
Update: I've discovered that the problem is in having the RequiredFieldValidator annotation. When that is present, the validation always fails but the s:actionerror tag is not displaying any message. When the annotation is not present, it allows a user to not pick a file and still calls execute().
The new question is: how to I correctly annotate my class for validation of a file upload?
I had to remove the #RequiredFieldValidator annotation, make my action implement ValidationAware and then implement a validate() method and manually put the validations in there.
Related
I have a controller that has a few methods that get an optional of entity from service, checks if is present and proceeds with some other actions or redirects with message "Entity not found".
It looks like that:
#GetMapping("action")
public String method(#PathVariable Long id,
final RedirectAttributes redirectAttributes){
Optional<Entity> eOpt = entityService.findById(id);
if(eOpt.isEmpty()){
alertHandler.set(redirectAttributes, Status.ENTITY_NOT_FOUND);
return "redirect:/entity/list"
}
Entity e = eOpt.get();
// other actions that are using e
return "view-name";
}
The six lines repeat in a few methods and for different entities too. Is there a way to assign it to some private method? The only thing I came up with is using a private method like:
private Optional<Entity> getEntityOpt(Long id){
Optional<Entity> eOpt = entityService.findById(id);
if(eOpt.isEmpty()){
alertHandler.set(redirectAttributes, Status.ENTITY_NOT_FOUND);
}
return Optional.empty();
}
This only saves me one line in mapped methods, so I don't have to set up alert message. Otherwise I still have to check again if the Optional is empty to redirect it.
So I guess the question really is - can I set up the private method to either return entity or redirect like:
Entity e = getEntityOrRedirect(Long id);
or maybe you have different ways to handle that problem. Or maybe it is what it is and you have to repeat yourself...
You may treat empty Optional as an exceptional situation.
In that case you may provide your own RuntimeException containing path to redirect.
public class EntityNotFoundException extends RuntimeException {
private final String fallbackView;
public EntityNotFoundException(final String fallbackView) {
this.fallbackView = fallbackView;
}
public String getFallbackView() {
return fallbackView;
}
Then provide a method annotated with #ExceptionHandler to your controller class (or if the situation is common for multiple controllers then provide such method to class annotated with #ControllerAdvice). Your exception handler should catch just defined exception and do a redirect.
#ExceptionHandler(EntityNotFoundException.class)
public String redirectOnEntityNotFoundException(final EntityNotFoundException exception,
final RedirectAttributes redirectAttributes) {
alertHandler.set(redirectAttributes, Status.ENTITY_NOT_FOUND);
return exception.getFallbackView();
}
Finally you achieved some kind of getEntityOrRedirect. Now you may use the above setup as following:
#GetMapping("action")
public String method(#PathVariable Long id){
Entity e = entityService.findById(id)
.orElseThrow(() -> new EntityNotFoundException("redirect:/entity/list"));
// other actions that are using e
return "view-name";
}
Code not tested so apologize for typos in advance.
Note I believe it would work for Spring >= 4.3.5 as otherwise RedirectAttributes wouldn't be resolved for #ExceptionHandler (as stated here)
I have a Controller class with the below two methods for finding a doctors (context changed). Getting the
Mass Assignment: Insecure Binder Configuration (API Abuse, Structural) error on both methods.
#Controller
#RequestMapping(value = "/findDocSearch")
public class Controller {
#Autowired
private IFindDocService findDocService;
#RequestMapping(value = "/byName", method = RequestMethod.GET)
#ResponseBody
public List<FindDocDTO> findDocByName(FindDocBean bean) {
return findDocService.retrieveDocByName(bean.getName());
}
#RequestMapping(value = "/byLoc", method = RequestMethod.GET)
#ResponseBody
public List<FindDocDTO> findDocByLocation(FindDocBean bean) {
return findDocService.retrieveDocByZipCode(bean.getZipcode(),
bean.getDistance());
}
}
and my Bean is :
public class FindDocBean implements Serializable {
private static final long serialVersionUID = -1212xxxL;
private String name;
private String zipcode;
private int distance;
#Override
public String toString() {
return String.format("FindDocBean[name: %s, zipcode:%s, distance:%s]",
name, zipcode, distance);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public int getDistance() {
return distance;
}
public void setDistance(int distance) {
this.distance = distance;
}
As per all the suggestions found so far, they are suggesting to restrict the bean with required parameters only by something like below :
final String[] DISALLOWED_FIELDS = new String[]{"bean.name", "bean.zipcode", };
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields(DISALLOWED_FIELDS);
But my problem is all the 3 parameters of the bean will be used in either of the method supplied on Controller.
Can someone please suggest some solution for this. Thanks in advance.
InitBinder can be used for methods. You can try this.
#InitBinder("findDocByName")
public void initBinderByName(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{"distance","zipcode"});
}
#InitBinder("findDocByLocation")
public void initBinderByZipCode(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{"distance","name"});
}
i was facing same issue, then i added below code in same rest controller class:
#InitBinder
public void populateCustomerRequest(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{});
}
now its working fine for me and mass assignment issue was fixed.
Simple question - how your mapper can instantionate the bean? Here is answer / example. You can pass that data by query parameter, or in header. However that would be strange. Better is to have that methods with #QueryParam providing location, or name. That way it will be easier to protect your application.
As a side note, query has limited length, so if your search form is big and strange, #POST can be good idea, and that way you can pass all the data. For this, simple example that would be overkill.
This looks like an unfortunate false positive. The rule behind this error is made to avoid that properties present in an object but not intended to be (unvalidated) user input are accidentally populated from a web request. An example would be a POST request creating a resource. If the request handler takes the full resource object and fills only missing properties an malicious user could populate fields that she shouldn't be able to edit.
This case however does not match the scheme. You just use the same mechanism to capture your different arguments. Additionally populated properties will not even be read. In
GET http://yourhost/findDocSearch/byName?name=Abuse&zipCode=11111
the additional zipCode would just be ignored. Therefore the assumed risk is not present here.
To fix the warning, you could mark it as a false positive (if this is possible inside your setup). If that is not possible you could also just map the query parameters to method arguments directly. As you only have limited parameters that should not harm too much. If this is also no option you probably need to figure out the exact algorithm your code analysis uses to figure out what checks it will recognize. Unfortunately most scanners are only able to discover a limited set of ways to do input validation.
I am new to the Java Play Framework and I'm trying to get the authentication to work. So I am following this tutorial: https://www.playframework.com/documentation/2.1.0/JavaGuide4
Here is my code:
public static Result authenticate()
{
Form<Login> loginForm = form(Login.class).bindFromRequest();
return ok(loginForm.toString());
}
public static class Login
{
public String email;
public String password;
public String validate()
{
return "VALIDATE "+email+password;
}
}
In the method autheticate() I can see the submitted values of the form, but the method validate() in the Login class does not see them (the variables are always null).. The output of loginForm.toString() contains:
Form(of=class controllers.Application$Login, data={email=asdf#asdf, password=asdf}, value=None, errors={=[ValidationError(,[VALIDATE nullnull],[])]})
As you can see, the data is received.. But in the validate method the data suddenly is equal to null. So how do I fix this?
You don't mention how you are calling validate() however I think this might do the trick, do something along the lines of:
public static Result authenticate() {
Form<Login> form = form(Login.class).bindFromRequest();
// handle errors
if (!form.hasErrors()) {
Login login = form.get();
Logger.debug(login.validate());
} else {
// bad request
}
}
This works for me.
Method validate in your model should return null if you think that validation has passed, otherwise you should return error message text. Then you need to check form if it contains error by "hasGlobalError" method. globalError is filled when validate() method returns String instead of null. But in your case you should use some model field annotations - https://www.playframework.com/documentation/2.3.x/api/java/play/data/validation/Constraints.html.
If you want to check if form fails on those - then you use "hasErrors" method.
public static class Login {
#Constraints.Email
public String email;
#Constraints.MinLength(value = 6)
public String password;
}
Such model will check if provided emails is really email and if password is longer or equal 6 characters.
ps. Do not use toString on template, you should use render()
I have a very strange behavior in a managed-bean where it is not retaining the values sent from the jsf and when it s going to process the POST then the properties are all null.
The JSF I have is a simple form with 2 fields and a button, the values of the two fields are received, and the button executes a POST method to process the data received from the JSF. When running a debug, I can see that after pressing the button then the setter methods are executed with the values sent to the bean (good), but when it goes to execute the mothod then suddenly all properties are null.
I have to include, that all this was working fine before, it started with this behavior when I moved all the managed-beans (backbeans) to a separated JAR file. I know that if I move the files again to the webapp then it will work, but I am looking for a way to not accumulate too many files in the same project, it's taking too long for compilation and deployment.
Here is the code of the backbean and the JSF:
#Named
#RequestScoped
public class RegisterController implements Serializable {
private String accountType;
public String getAccountTypes() {
return accountType;
}
public void setAccountTypes(String accountType) {
this.accountType = accountType; // Here it stores the value ********
}
private String businessType;
public String getBusinessType() {
return businessType;
}
public void setBusinessType(String businessType) {
this.businessType = businessType; // Here it stores the other value *******
}
// Method called with the button
public String prepareCreate() {
if ("PERSONAL".equals(getAccountTypes())) // Here is null!! *************
{
return "PersonalSignup";
}
else
if (businessType == null) // Here is also null!! ************
{
JsfUtil.addErrorMessage(
new Exception(""), ResourceBundle.getBundle(CommonUtil.bundleStr).getString("cc.signup.accounttype.invalid.businesstype"));
}
...
Any help would be really appreciated, thanks.
Found the answer here: How does JSF find beans annotated with #ManagedBean?. Certainly if you need that your webapp looks into your jar file for managed-beans, only include a faces-config.xml file in your META-INF folder of your jar file.
In Moritz Haarmann's Blog I found an example of usage of Bonjour by Java. Here is the code taken from there:
public class ServiceAnnouncer implements IServiceAnnouncer, RegisterListener {
private DNSSDRegistration serviceRecord;
private boolean registered;
public boolean isRegistered(){
return registered;
}
public void registerService() {
try {
serviceRecord = DNSSD.register(0,0,null,"_killerapp._tcp", null,null,1234,null,this);
} catch (DNSSDException e) {
// error handling here
}
}
public void unregisterService(){
serviceRecord.stop();
registered = false;
}
public void serviceRegistered(DNSSDRegistration registration, int flags,String serviceName, String regType, String domain){
registered = true;
}
public void operationFailed(DNSSDService registration, int error){
// do error handling here if you want to.
}
}
I have a question about the "serviceRegistered" method. As far as I understand it is called during (or after) registration of the service (and it sets variable "registered" to be equal to "true"). But what is not clear to me is how exactly it is called. Because the service is registered by the method "registerService". This method, in its turn, calls "DNSSD.register". And, as far as I understand, the "DNSSD.register" will call the "serviceRegister" method of the "ServiceAnnouncer" class. But how "DNSSD.register" knows that it needs to call a method of the "ServiceAnnouncer" class? Can "DNSSD.register" know that it is called from a particular class (in this case "ServiceAnnouncer" class)?
The ServiceAnnouncer has passed itself as last argument of the DNSSD.register() method, which in turn is apparently expecting any instance of RegisterListener. This way the DNSSD can have a direct handle to the ServiceAnnouncer instance.
It seems that this class is a listener - namely RegisterListener. It has been registered as a listener in DNSSD by passing itself to the register(..) method.
For more information read about the Observer pattern.