Author: nbelaevski
Date: 2009-02-20 17:54:18 -0500 (Fri, 20 Feb 2009)
New Revision: 12710
Modified:
trunk/framework/impl/src/main/java/org/ajax4jsf/request/MultipartRequest.java
trunk/framework/impl/src/main/java/org/ajax4jsf/webapp/BaseFilter.java
trunk/framework/impl/src/main/resources/org/ajax4jsf/javascript/scripts/form.js
Log:
https://jira.jboss.org/jira/browse/RF-4187
Modified: trunk/framework/impl/src/main/java/org/ajax4jsf/request/MultipartRequest.java
===================================================================
---
trunk/framework/impl/src/main/java/org/ajax4jsf/request/MultipartRequest.java 2009-02-20
22:52:53 UTC (rev 12709)
+++
trunk/framework/impl/src/main/java/org/ajax4jsf/request/MultipartRequest.java 2009-02-20
22:54:18 UTC (rev 12710)
@@ -38,9 +38,12 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
@@ -70,12 +73,16 @@
private Integer contentLength = 0;
+ private int bytesRead = 0;
+
private int read = 0;
private Map<String, Param> parameters = null;
private Map<String, Object> percentMap = null;
+ private Map<String, MultipartRequest> requestsMap = null;
+
private List<String> keys = new ArrayList<String>();
private enum ReadState {
@@ -354,184 +361,302 @@
}
}
- public boolean parseRequest() {
- byte[] boundaryMarker = getBoundaryMarker(super.getContentType());
- if (boundaryMarker == null) {
- throw new FileUploadException("The request was rejected because "
- + "no multipart boundary was found");
- }
+ private byte[] boundaryMarker;
+
+ private byte[] buffer;
+
+ private ReadState readState;
+
+ private InputStream input;
+
+ private int pos = 0;
- encoding = getCharacterEncoding();
+ private int zeroReadAttempts = 20; // 20 attempts to read not-readable data
+
+ private void fillBuffer() throws IOException {
+ if (!shouldStop) {
+ if (pos < read) {
+ // move the bytes that weren't read to the start of
+ // the
+ // buffer
+ int bytesNotRead = read - pos;
+
+ if (bytesNotRead != buffer.length) {
+ System.arraycopy(buffer, pos, buffer, 0,
+ bytesNotRead);
+ read = input.read(buffer, bytesNotRead,
+ buffer.length - bytesNotRead);
+
+ if (read != 0 || --zeroReadAttempts != 0) {
+ if (read > 0) {
+ bytesRead += read;
+ }
- parameters = new HashMap<String, Param>();
+ read += bytesNotRead;
+ } else {
+ //read is already zero
+ //read = 0;
+ }
+ } else {
+ read = bytesNotRead;
+ }
+ } else {
+ read = input.read(buffer);
- int loopCounter = 20; // 20 attempts to read not-readable data
+ if (read > 0) {
+ bytesRead += read;
+ }
+ }
+
+ fillProgressInfo();
+ pos = 0;
+ } else {
+ cancel();
+ read = -1;
+ }
+ }
+
+ private void readNext() throws IOException {
+ Param p = readHeader();
+ readData(p);
+ }
- this.percentMap = getProgressData();
+ private void readData(Param p) throws IOException {
+ int localRead = this.read;
+
+ while (localRead > 0) {
+ for (int i = this.pos; i < localRead; i++) {
+ // If we've encountered another boundary...
+ if (checkSequence(buffer, i - boundaryMarker.length
+ - CR_LF.length, CR_LF)
+ && checkSequence(buffer, i, boundaryMarker)) {
+ // Write any data before the boundary (that
+ // hasn't
+ // already been written) to the param
+ if (pos < i - boundaryMarker.length
+ - CR_LF.length - 1) {
+ p.appendData(buffer, pos, i - pos
+ - boundaryMarker.length
+ - CR_LF.length - 1);
+ }
- try {
- byte[] buffer = new byte[BUFFER_SIZE];
- Map<String, String> headers = new HashMap<String, String>();
+ if (p instanceof ValueParam)
+ ((ValueParam) p).complete();
- ReadState readState = ReadState.BOUNDARY;
+ if (checkSequence(buffer, i + CR_LF.length,
+ CR_LF)) {
+ i += CR_LF.length;
+ pos = i + 1;
+ } else {
+ pos = i;
+ }
- InputStream input = getInputStream();
- if (!shouldStop) {
+ readState = ReadState.HEADERS;
+ break;
+ }
+ // Otherwise write whatever data we have to the
+ // param
+ else if (i > (pos + boundaryMarker.length
+ + CHUNK_SIZE + CR_LF.length)) {
+ p.appendData(buffer, pos, CHUNK_SIZE);
+ pos += CHUNK_SIZE;
- int read = input.read(buffer);
- int pos = 0;
+ }
+ }
+
+ if (ReadState.DATA.equals(readState)) {
+ fillBuffer();
+ localRead = this.read;
+ } else {
+ break;
+ }
+ }
+ }
- Param p = null;
+ private Param readHeader() throws IOException {
+ Param p = null;
- while (read > 0 && loopCounter > 0) {
- for (int i = 0; i < read; i++) {
- switch (readState) {
- case BOUNDARY: {
- if (checkSequence(buffer, i, boundaryMarker)
- && checkSequence(buffer, i + 2, CR_LF)) {
- readState = ReadState.HEADERS;
- i += 2;
- pos = i + 1;
+ Map<String, String> headers = new HashMap<String, String>();
- }
- break;
- }
- case HEADERS: {
- if (checkSequence(buffer, i, CR_LF)) {
- String param = (encoding == null) ? new String(
- buffer, pos, i - pos - 1) : new String(
- buffer, pos, i - pos - 1, encoding);
- parseParams(param, "; ", headers);
+ int localRead = this.read;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
+
+ while (localRead > 0) {
+ for (int i = this.pos; i < localRead; i++) {
+ if (checkSequence(buffer, i, CR_LF)) {
+ baos.write(buffer, pos, i - pos - 1);
- if (checkSequence(buffer, i + CR_LF.length,
- CR_LF)) {
- readState = ReadState.DATA;
- i += CR_LF.length;
- pos = i + 1;
+ String param = (encoding == null) ?
+ new String(baos.toByteArray()) :
+ new String(baos.toByteArray(), encoding);
- String paramName = headers.get(PARAM_NAME);
- if (paramName != null) {
- if (headers.containsKey(PARAM_FILENAME)) {
- FileParam fp = new FileParam(
- paramName);
- if (createTempFiles)
- fp.createTempFile();
- fp.setContentType(headers
- .get(PARAM_CONTENT_TYPE));
- fp
- .setFilename(decodeFileName(headers
- .get(PARAM_FILENAME)));
- p = fp;
- } else {
- if (parameters
- .containsKey(paramName)) {
- p = parameters.get(paramName);
- } else {
- p = new ValueParam(paramName);
- }
- }
+ parseParams(param, "; ", headers);
- if (!parameters.containsKey(paramName)) {
- parameters.put(paramName, p);
- }
- }
+ if (checkSequence(buffer, i + CR_LF.length,
+ CR_LF)) {
+ readState = ReadState.DATA;
+ i += CR_LF.length;
+ pos = i + 1;
- headers.clear();
+ String paramName = headers.get(PARAM_NAME);
+ if (paramName != null) {
+ if (headers.containsKey(PARAM_FILENAME)) {
+ FileParam fp = new FileParam(
+ paramName);
+ if (createTempFiles)
+ fp.createTempFile();
+ fp.setContentType(headers
+ .get(PARAM_CONTENT_TYPE));
+ fp
+ .setFilename(decodeFileName(headers
+ .get(PARAM_FILENAME)));
+ p = fp;
+ } else {
+ if (parameters
+ .containsKey(paramName)) {
+ p = parameters.get(paramName);
} else {
- pos = i + 1;
+ p = new ValueParam(paramName);
}
}
- break;
+
+ if (!parameters.containsKey(paramName)) {
+ parameters.put(paramName, p);
+ }
}
- case DATA: {
- // If we've encountered another boundary...
- if (checkSequence(buffer, i - boundaryMarker.length
- - CR_LF.length, CR_LF)
- && checkSequence(buffer, i, boundaryMarker)) {
- // Write any data before the boundary (that
- // hasn't
- // already been written) to the param
- if (pos < i - boundaryMarker.length
- - CR_LF.length - 1) {
- p.appendData(buffer, pos, i - pos
- - boundaryMarker.length
- - CR_LF.length - 1);
- }
- if (p instanceof ValueParam)
- ((ValueParam) p).complete();
+ headers.clear();
+ baos.reset();
+
+ break;
+ } else {
+ pos = i + 1;
+ baos.reset();
+ }
+ }
+ }
+
+ if (ReadState.HEADERS.equals(readState)) {
+ baos.write(buffer, pos, read - pos);
+ pos = read;
+ fillBuffer();
+ localRead = this.read;
+ } else {
+ break;
+ }
+ }
+ return p;
+ }
- if (checkSequence(buffer, i + CR_LF.length,
- CR_LF)) {
- i += CR_LF.length;
- pos = i + 1;
- } else {
- pos = i;
- }
+ boolean initialized = false;
+
+ private void initialize() throws IOException {
+ if (!initialized) {
+ initialized = true;
- readState = ReadState.HEADERS;
- }
- // Otherwise write whatever data we have to the
- // param
- else if (i > (pos + boundaryMarker.length
- + CHUNK_SIZE + CR_LF.length)) {
- p.appendData(buffer, pos, CHUNK_SIZE);
- pos += CHUNK_SIZE;
+ this.boundaryMarker = getBoundaryMarker(super.getContentType());
+ if (this.boundaryMarker == null) {
+ throw new FileUploadException("The request was rejected because "
+ + "no multipart boundary was found");
+ }
- }
- break;
- }
- }
+ this.encoding = getCharacterEncoding();
+
+ this.parameters = new HashMap<String, Param>();
+
+ this.buffer = new byte[BUFFER_SIZE];
+
+ this.readState = ReadState.BOUNDARY;
+
+ this.input = getInputStream();
+
+ setupProgressData();
+
+ fillBuffer();
+ int localRead = this.read;
+
+ while (localRead > 0) {
+ for (int i = 0; i < localRead; i++) {
+ if (checkSequence(buffer, i, boundaryMarker)
+ && checkSequence(buffer, i + 2, CR_LF)) {
+ readState = ReadState.HEADERS;
+ i += 2;
+ pos = i + 1;
+
+ break;
}
+ }
+
+ if (ReadState.BOUNDARY.equals(readState)) {
+ pos = read - (boundaryMarker.length + CR_LF.length) + 1;
+ fillBuffer();
+ localRead = this.read;
+ } else {
+ break;
+ }
+ }
+
+ }
+ }
+
+ public void parseRequest() {
+ try {
+ initialize();
+
+ while (read >= 0) {
+ readNext();
+ }
+
+ } catch (IOException e) {
+ throw new FileUploadException("IO Error parsing multipart request", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void setupProgressData() {
+ if (percentMap == null || requestsMap == null) {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ if (facesContext != null) {
+ ExternalContext externalContext = facesContext.getExternalContext();
+ if (externalContext != null) {
+ Map<String, Object> sessionMap = externalContext.getSessionMap();
+ if (sessionMap != null) {
+ String uploadId = getUploadId();
- if (!shouldStop) {
- if (pos < read) {
- // move the bytes that weren't read to the start of
- // the
- // buffer
- int bytesNotRead = read - pos;
- System.arraycopy(buffer, pos, buffer, 0,
- bytesNotRead);
- read = input.read(buffer, bytesNotRead,
- buffer.length - bytesNotRead);
- if (read <= 0) {
- loopCounter--;
+ synchronized (sessionMap) {
+ if (percentMap == null) {
+ percentMap = (Map<String, Object>)
sessionMap.get(FileUploadConstants.PERCENT_BEAN_NAME);
+ if (percentMap == null) {
+ percentMap = new ConcurrentHashMap<String, Object>();
+ sessionMap.put(FileUploadConstants.PERCENT_BEAN_NAME, percentMap);
+ }
+ percentMap.put(uploadId, Double.valueOf(0));
}
- read += bytesNotRead;
- } else {
- read = input.read(buffer);
+
+ if (requestsMap == null) {
+ requestsMap = (Map<String, MultipartRequest>)
sessionMap.get(FileUploadConstants.REQUESTS_SESSIONS_BEAN_NAME);
+ if (requestsMap == null) {
+ requestsMap = new ConcurrentHashMap<String, MultipartRequest>();
+ sessionMap.put(FileUploadConstants.REQUESTS_SESSIONS_BEAN_NAME, requestsMap);
+ }
+ requestsMap.put(uploadId, this);
+ }
}
- this.read += pos;
- pos = 0;
- fillProgressInfo();
- } else {
- cancel();
- return false;
}
}
-
- return true;
- } else {
- cancel();
- return false;
}
- } catch (IOException ex) {
- throw new FileUploadException("IO Error parsing multipart request",
- ex);
}
}
- @SuppressWarnings("unchecked")
- private Map<String, Object> getProgressData() {
- percentMap = (Map<String, Object>) getSession().getAttribute(
- FileUploadConstants.PERCENT_BEAN_NAME);
- return percentMap;
- }
-
private void fillProgressInfo() {
- Double percent = (Double) (100.0 * this.read / this.contentLength);
- percentMap.put(uid, percent);
- // this.percent = percent;
+ setupProgressData();
+
+ if (percentMap != null) {
+ Double percent = (Double) (100.0 * this.bytesRead / this.contentLength);
+ percentMap.put(uid, percent);
+ // this.percent = percent;
+ }
}
private byte[] getBoundaryMarker(String contentType) {
@@ -618,9 +743,25 @@
}
private Param getParam(String name) {
- if (parameters == null)
- parseRequest();
- return parameters.get(name);
+ Param param = null;
+ if (parameters != null) {
+ param = parameters.get(name);
+ }
+
+ if (param == null) {
+ try {
+ initialize();
+
+ while (param == null && read > 0) {
+ readNext();
+ param = parameters.get(name);
+ }
+ } catch (IOException e) {
+ throw new FileUploadException("IO Error parsing multipart request", e);
+ }
+ }
+
+ return param;
}
public Integer getSize() {
@@ -689,6 +830,8 @@
@Override
public String[] getParameterValues(String name) {
+ parseRequest();
+
Param p = getParam(name);
if (p != null && p instanceof ValueParam) {
ValueParam vp = (ValueParam) p;
@@ -753,8 +896,28 @@
shouldStop = true;
}
+ public boolean isStopped() {
+ return shouldStop;
+ }
+
@Override
public String getContentType() {
return "application/x-www-form-urlencoded";
}
+
+ protected String getUploadId() {
+ return uid;
+ }
+
+ public void clearRequestData() {
+ String uploadId = getUploadId();
+
+ if (percentMap != null) {
+ percentMap.remove(uploadId);
+ }
+
+ if (requestsMap != null) {
+ requestsMap.remove(uploadId);
+ }
+ }
}
Modified: trunk/framework/impl/src/main/java/org/ajax4jsf/webapp/BaseFilter.java
===================================================================
--- trunk/framework/impl/src/main/java/org/ajax4jsf/webapp/BaseFilter.java 2009-02-20
22:52:53 UTC (rev 12709)
+++ trunk/framework/impl/src/main/java/org/ajax4jsf/webapp/BaseFilter.java 2009-02-20
22:54:18 UTC (rev 12710)
@@ -324,8 +324,6 @@
if (isMultipartRequest(httpRequest)) {
MultipartRequest multipartRequest = new MultipartRequest(httpRequest,
createTempFiles, maxRequestSize, uid);
- Map<String, MultipartRequest> sessionsMap = null;
- Map<String, Object> percentMap = null;
try {
if (isFileSizeRestricted(request, maxRequestSize)) {
@@ -343,45 +341,21 @@
printResponse(response, "<html
id=\"_richfaces_file_upload_forbidden\"></html>");
} else {
- HttpSession session = httpRequest.getSession();
- synchronized (session) {
- sessionsMap = (Map<String, MultipartRequest>)
session.getAttribute(FileUploadConstants.REQUESTS_SESSIONS_BEAN_NAME);
- percentMap = (Map<String, Object>)
session.getAttribute(FileUploadConstants.PERCENT_BEAN_NAME);
- if (sessionsMap == null) {
- sessionsMap = Collections.synchronizedMap(new HashMap<String,
MultipartRequest>());
- session.setAttribute(FileUploadConstants.REQUESTS_SESSIONS_BEAN_NAME,
sessionsMap);
+ handleRequest(multipartRequest, multipartRequest.isFormUpload() ? response :
+ new HttpServletResponseWrapper((HttpServletResponse) response){
+ @Override
+ public void setContentType(String type) {
+ super.setContentType(BaseXMLFilter.TEXT_HTML + ";charset=UTF-8");
}
- if (percentMap == null) {
- percentMap = new HashMap<String, Object>();
- session.setAttribute(FileUploadConstants.PERCENT_BEAN_NAME, percentMap);
- }
- }
+ }, chain);
- /* associate percent value with file entry uid */
- percentMap.put(uid, 0);
- sessionsMap.put(uid, multipartRequest);
-
- if (multipartRequest.parseRequest()) {
- handleRequest(multipartRequest, multipartRequest.isFormUpload() ? response :
- new HttpServletResponseWrapper(response){
- @Override
- public void setContentType(String type) {
- super.setContentType(BaseXMLFilter.TEXT_HTML + ";charset=UTF-8");
- }
- }, chain);
- } else {
+ if (multipartRequest.isStopped()) {
printResponse(response, "<html
id=\"_richfaces_file_upload_stopped\"></html>");
}
-
}
} finally {
-
- if (sessionsMap != null) {
- sessionsMap.remove(uid);
- percentMap.remove(uid);
- }
-
+ multipartRequest.clearRequestData();
}
} else {
handleRequest(request, response, chain);
Modified: trunk/framework/impl/src/main/resources/org/ajax4jsf/javascript/scripts/form.js
===================================================================
---
trunk/framework/impl/src/main/resources/org/ajax4jsf/javascript/scripts/form.js 2009-02-20
22:52:53 UTC (rev 12709)
+++
trunk/framework/impl/src/main/resources/org/ajax4jsf/javascript/scripts/form.js 2009-02-20
22:54:18 UTC (rev 12710)
@@ -34,7 +34,14 @@
input.id=param;
input.name=param;
input.value=parameters[param];
- form.appendChild(input);
+
+ if (param === "javax.faces.portletbridge.STATE_ID" /* fix for fileUpload
in portlets */
+ && form.firstChild) {
+
+ form.insertBefore(input, form.firstChild);
+ } else {
+ form.appendChild(input);
+ }
}
}
}