I am using the following code snippet for a file upload in an embedded form:
File Upload
On the server side I am printing all process variables onto the console with an ExecutionListener:
Map<String, Object> vars = de.getVariables();
for (Map.Entry<String, Object> entry : vars.entrySet()){
System.out.println(entry.getKey() + “/” + entry.getValue());
}
In the case when no file was chosen in the embedded form and the form is sent to the server by clicking “complete” a java.lang.NullPointerException is thrown.
What am I doing wrong?
20-Jun-2017 16:32:17.236 SEVERE [http-nio-8080-exec-532] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [Engine Api] in context with path [/camunda] threw exception
at org.camunda.bpm.webapp.impl.security.auth.AuthenticationFilter$1.execute(AuthenticationFilter.java:61)
at org.camunda.bpm.webapp.impl.security.auth.AuthenticationFilter$1.execute(AuthenticationFilter.java:56)
at org.camunda.bpm.webapp.impl.security.SecurityActions.runWithAuthentications(SecurityActions.java:38)
at org.camunda.bpm.webapp.impl.security.auth.AuthenticationFilter.doFilter(AuthenticationFilter.java:56)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1527)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1484)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
at org.jboss.resteasy.core.SynchronousDispatcher.handleApplicationException(SynchronousDispatcher.java:365)
at org.jboss.resteasy.core.SynchronousDispatcher.handleException(SynchronousDispatcher.java:233)
at org.jboss.resteasy.core.SynchronousDispatcher.handleInvokerException(SynchronousDispatcher.java:209)
at org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:557)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:524)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:126)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:208)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:55)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:50)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.camunda.bpm.engine.rest.filter.CacheControlFilter.doFilter(CacheControlFilter.java:41)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.camunda.bpm.webapp.impl.security.filter.SecurityFilter.doFilterSecure(SecurityFilter.java:67)
at org.camunda.bpm.webapp.impl.security.filter.SecurityFilter.doFilter(SecurityFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.camunda.bpm.webapp.impl.security.auth.AuthenticationFilter$1.execute(AuthenticationFilter.java:59)
... 21 more
Caused by: java.lang.NullPointerException
at org.camunda.bpm.engine.variable.impl.type.FileValueTypeImpl.createValue(FileValueTypeImpl.java:44)
at org.camunda.bpm.engine.rest.dto.VariableValueDto.toTypedValue(VariableValueDto.java:123)
at org.camunda.bpm.engine.rest.dto.VariableValueDto.toMap(VariableValueDto.java:146)
at org.camunda.bpm.engine.rest.sub.repository.impl.ProcessDefinitionResourceImpl.submitForm(ProcessDefinitionResourceImpl.java:165)
at sun.reflect.GeneratedMethodAccessor955.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:167)
at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:257)
at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:222)
at org.jboss.resteasy.core.ResourceLocator.invokeOnTargetObject(ResourceLocator.java:159)
at org.jboss.resteasy.core.ResourceLocator.invoke(ResourceLocator.java:107)
at org.jboss.resteasy.core.ResourceLocator.invokeOnTargetObject(ResourceLocator.java:154)
at org.jboss.resteasy.core.ResourceLocator.invoke(ResourceLocator.java:92)
at org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:542)
... 40 more
well, code on this line corresponds to Object filename = valueInfo.get(VALUE_INFO_FILE_NAME);
SInce you are not setting any value it get deserialized as null I assume, which leads to the exception. I think you could remove it using JS if it’s not set. Here is a method reference https://github.com/camunda/camunda-bpm-sdk-js/blob/7.7/lib/forms/variable-manager.js#L44
one thing which you could do to really delete the variable from the variable manager:
Change the way you check if the variable exists, e.g. by explicitly checking if the field has a value inside the camForm.fields (camForm.fields[n].element.value==null) on submit of the form.
If the value is null, you can then do two things:
camForm.variableManager.destroyVariable(‘document’);
delete camForm.fields[n];
Besides that I usually display file uploads only if they are required or expected from the user. You could display the file upload also only if a specific scope variable is set (e.g. display file upload field after the click of some upload button)
I tried it out. But this doesn’t help. It still will give an internal server error. I double checked that camForm.fields does not contain the upload variable anymore.
I wonder why just two people face this error. Isn’t it very common to upload files?
Maybe there is someone else who has solved this issue?
I’m also facing the same problem because when I try to submit my task form I see an internal server error which is the following (like @thkeller’s):
org.camunda.bpm.engine.rest.exception.ExceptionHandler.toResponse java.lang.NullPointerException
at org.camunda.bpm.engine.variable.impl.type.FileValueTypeImpl.createValue(FileValueTypeImpl.java:44)
at org.camunda.bpm.engine.rest.dto.VariableValueDto.toTypedValue(VariableValueDto.java:123)
In my case, I add products from a Form to a Table. The user must fill in all of the input fields of the Form in order to add the product to the Table. One of those fields is a file field. Although I fill in the file field, I take again and again the same error as if it isn’t filled in in the Form.
Does anyone have any idea on this please?
My code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Insert products and specifications</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" /> <!-- We include Bootstrap from a CDN (Content Delivery Network). -->
</head>
<body>
<form role="form" name="insertForm" accept-charset="utf-8">
<script cam-script type="text/form-script">
var product = $scope.product = []; // Custom JavaScript creates a JavaScript Object and binds it to the current AngularJS $scope of the form as a variable named "product".
$scope.addProduct = function () { // We make a function named "addProduct".
var product = {}; // We add a new "product" to the Array.
product.Category = $scope.Category;
product.Description = $scope.Description;
if (!!$scope.camForm.fields[0].element[0].files[0]) { // If the file is uploaded,
product.Details = $scope.camForm.fields[0].element[0].files[0].name; // it returns file's "name".
} else { // If the file is not uploaded,
return; // it returns "undefined".
}
product.Price = $scope.Price;
$scope.product.push(product); // We use the value of the "product" input field to add a new "product" to the Array.
$scope.Category = ""; // We clear the TextBox "Κατηγορία".
$scope.Description = ""; // We clear the TextBox "Περιγραφή".
$scope.Details = ""; // We clear file's "name".
$scope.Price = ""; // We clear the TextBox "Τιμή (€)".
};
$scope.clear = function () { // We make a function named "clear".
angular.element("input[type='file']").val(null);
};
$scope.removeProduct = function (index) { // We make a function named "removeProduct".
var category = $scope.product[index].Category; // We find product's "Category" using "index" from the Array and binds it to the current AngularJS $scope of the form as a variable named "category".
$scope.product.splice(index, 1); // We use an "index" to remove a "product" from the Array.
}
$scope.isAddFormValid = function () { // We make a function named "isAddFormValid".
return ($scope.Category &&
$scope.Description &&
$scope.camForm.fields[0].element[0].files[0] &&
$scope.Price) ? true : false; // If all of the 4 input fields of variable "product" are filled in, the "isAddFormValid" function (expression) returns "true", otherwise the function returns "false".
}
camForm.on('form-loaded', function() { // We hook into the lifecycle of Camunda SDK JS Form.
camForm.variableManager.createVariable ({ // We "create" (declare) a new "process variable"
name:'product', // named 'product' and
type:'json', // provide as type information 'json' used for serialization.
value:product
});
});
camForm.on('submit', function(evt) { // We hook into the lifecycle of Camunda SDK JS Form.
if (product.length<1) { // If no "product" is added to the Array,
evt.submitPrevented = true; // an event handler prevents the form from being submitted by setting the property "submitPrevented" to 'true'.
}
});
</script>
<h2><b>Λίστα Προϊόντων</b></h2> <!-- We set the heading of the HTML Table. -->
<div>
<table style="width:100%;">
<thead> <!-- We group the "header" content in the HTML Table. -->
<tr> <!-- The "header" content of the HTML Table is not repeated. -->
<th>Κατηγορία</th>
<th>Περιγραφή</th>
<th>Λεπτομέρειες</th>
<th style="width:75px;">Τιμή (€)</th>
<th></th>
</tr>
</thead>
<tbody ng-repeat="x in product track by $index"> <!-- The HTML Table is populated from the JSON Array "product", using a "ng-repeat" directive which is assigned to each row of the Table in order to repeat all the objects of the Array. -->
<tr> <!-- Each row of the HTML Table consists of 4 HTML fields and 1 button. -->
<td><input type="text" value="{{x.Category}}" /></td>
<td style="width:100%; padding:0px 0px 0px 0px"><input style="width:100%;" type="text" value="{{x.Description}}" /></td>
<td><input type="text" value="{{x.Details}}" /></td>
<td><input style="width:75px;" type="number" value="{{x.Price}}" /></td>
<td><input type="button" ng-click="removeProduct($index)" value="Remove" /></td> <!-- The "ng-click" directive is assigned to the "Remove" button and calls the function named "removeProduct" with the current "$index" when this button is clicked. -->
</tr>
</tbody>
</table>
</div>
<hr> <!-- We separate the HTML content of the page. -->
<div>
<h2><b>Καταχώρησε νέο προϊόν</b></h2> <!-- We set the heading of the HTML Form. -->
<div class="row"> <!-- We set the "1st row" of the HTML Form. -->
<div class="col-md-6"> <!-- We use "md" for "medium" screen devices of width "equal to or greater than" 992px and "6" for adding 6 columns. -->
<div class="form-group"> <!-- We use "form-group" for optimum spacing. -->
<label class="control-label" for="category">Επίλεξε Κατηγορία:</label>
<div class="controls">
<input list="category" name="categories" ng-model="Category" /> <!-- When the value of the input field "Επίλεξε Κατηγορία" changes, is bound to the created variable "Category" in AngularJS by the "ng-model" directive. -->
<datalist id="category">
<option value="Desktop">
<option value="Laptop">
<option value="Tablet">
<option value="Οθόνη Υπολογιστή">
<option value="Οθόνη Προβολής">
<option value="Εκτυπωτής laser">
<option value="Φωτοτυπικό Μηχάνημα">
<option value="Scanner">
<option value="UPS">
<option value="Διαδικτυακή Συσκευή Αποθήκευσης">
<option value="Εξωτερικός Σκληρός Δίσκος">
<option value="Προτζέκτορας">
<option value="Βιντεοπροτζέκτορας">
</datalist>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="control-label" for="description">Περιγραφή</label>
<div class="controls">
<input id="description" type="text" onkeypress="this.style.width = ((this.value.length + 1) * 8) + 'px';" ng-model="Description" /> <!-- When the value of the input field "Περιγραφή" changes, is bound to the created variable "Description" in AngularJS by the "ng-model" directive. -->
</div>
</div>
<div class="form-group">
<label class="control-label" for="details">Λεπτομέρειες</label>
<div class="controls">
<input id="details"
type="file"
cam-variable-name="Details"
cam-variable-type="File"
cam-max-filesize="10000000" ng-model="Details" /> <!-- When the value of the input field "Λεπτομέρειες" changes, is bound to the created variable "Details" in AngularJS by the "ng-model" directive. -->
</div>
</div>
<div class="form-group">
<label class="control-label" for="price">Τιμή (€)</label>
<div class="controls">
<input style="width:75px;" id="price" type="number" min="0" ng-model="Price" /> <!-- When the value of the input field "Τιμή (€)" changes, is bound to the created variable "Price" in AngularJS by the "ng-model" directive. -->
</div>
</div>
<div class="controls">
<input type="button" ng-click="addProduct();clear()" ng-show="isAddFormValid()" value="Add" /> <!-- The "ng-show" directive shows the input element ("Add" button) only if the "isAddFormValid()" function (expression) returns "true". The "ng-click" directive is assigned to the "Add" button and calls the functions named "addProduct()" and "clear()" when this button is clicked. -->
</div>
</div>
</div>
</div>
</form>
</body>
</html>