diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedGraphicImageRenderer.java b/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedGraphicImageRenderer.java index eee862e..162d398 100644 --- a/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedGraphicImageRenderer.java +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedGraphicImageRenderer.java @@ -1,23 +1,24 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *
+ * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package be.rubus.web.jsf.primefaces; +import be.rubus.web.jsf.primefaces.util.StringEncrypter; import org.primefaces.component.graphicimage.GraphicImage; import org.primefaces.component.graphicimage.GraphicImageRenderer; import org.primefaces.model.StreamedContent; @@ -29,80 +30,97 @@ import javax.faces.component.UIParameter; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; /** */ public class AdvancedGraphicImageRenderer extends GraphicImageRenderer { - private boolean determineIfAdvancedRendering(GraphicImage image) { - boolean result = false; - - boolean isStreamedContent = image.getValue() instanceof StreamedContent; - Boolean advancedMarker = (Boolean) image.getAttributes().get(AdvancedRendererHandler.ADVANCED_RENDERING); - - if (isStreamedContent && advancedMarker == null) { - result = determineSpecificParents(image); - } - - if (!result && advancedMarker != null && advancedMarker) { - result = true; - } - - return result; - } - - private boolean determineSpecificParents(GraphicImage image) { - boolean result = false; - UIComponent current = image; - while (!result && !(current instanceof UIViewRoot)) { - result = current instanceof UIData; - if (!result) { - result = UIComponent.isCompositeComponent(current); - } - current = current.getParent(); - } - return result; - } - - @Override - protected String getImageSrc(FacesContext context, GraphicImage image) { - if (determineIfAdvancedRendering(image)) { - String src; - Object value = image.getValue(); - StreamedContent streamedContent = (StreamedContent) value; - - - Resource resource = context.getApplication().getResourceHandler() - .createResource("dynamiccontent", "advancedPrimefaces", streamedContent.getContentType()); - String resourcePath = resource.getRequestPath(); - String rid = createUniqueContentId(context); - StringBuilder builder = new StringBuilder(resourcePath); - GraphicImageManager graphicImageManager = GraphicImageUtil.retrieveManager(context); - graphicImageManager.registerImage(streamedContent, rid); - - builder.append("&").append(Constants.DYNAMIC_CONTENT_PARAM).append("=").append(rid); - - for (UIComponent kid : image.getChildren()) { - if (kid instanceof UIParameter) { - UIParameter param = (UIParameter) kid; - - builder.append("&").append(param.getName()).append("=").append(param.getValue()); - } - } - - src = builder.toString(); - - if (!image.isCache()) { - src += src.contains("?") ? "&" : "?"; - src += "primefaces_image=" + UUID.randomUUID().toString(); - } - - src = context.getExternalContext().encodeResourceURL(src); - return src; - - } else { - return super.getImageSrc(context, image); - } - } + private static final Logger LOG = Logger.getLogger(AdvancedGraphicImageRenderer.class.getName()); + private static final StringEncrypter ENCRYPTER = new StringEncrypter(AdvancedGraphicImageRenderer.class.getName()); + + private boolean determineIfAdvancedRendering(GraphicImage image) { + boolean result = false; + + boolean isStreamedContent = image.getValue() instanceof StreamedContent; + Boolean advancedMarker = (Boolean) image.getAttributes().get(AdvancedRendererHandler.ADVANCED_RENDERING); + + if (isStreamedContent && advancedMarker == null) { + result = determineSpecificParents(image); + } + + if (!result && advancedMarker != null && advancedMarker) { + result = true; + } + + return result; + } + + private boolean determineSpecificParents(GraphicImage image) { + boolean result = false; + UIComponent current = image; + while (!result && !(current instanceof UIViewRoot)) { + result = current instanceof UIData; + if (!result) { + result = UIComponent.isCompositeComponent(current); + } + current = current.getParent(); + } + return result; + } + + @Override + protected String getImageSrc(FacesContext context, GraphicImage image) { + if (determineIfAdvancedRendering(image)) { + String src; + Object value = image.getValue(); + StreamedContent streamedContent = (StreamedContent) value; + + + Resource resource = context.getApplication().getResourceHandler() + .createResource("dynamiccontent", "advancedPrimefaces", streamedContent.getContentType()); + String resourcePath = resource.getRequestPath(); + + String uniqueId = String.format("%1$s_%2$s", image.getClientId(context), image.getValueExpression("value").getExpressionString()); + String encrypted = ENCRYPTER.encrypt(uniqueId); + String rid = null; + try { + rid = URLEncoder.encode(encrypted, "UTF-8"); + } catch (UnsupportedEncodingException e) { + // Should never happen + LOG.log(Level.SEVERE, e.getMessage()); + } + + StringBuilder builder = new StringBuilder(resourcePath); + GraphicImageManager graphicImageManager = GraphicImageUtil.retrieveManager(context); + graphicImageManager.registerImage(streamedContent, rid); + + builder.append("&").append(Constants.DYNAMIC_CONTENT_PARAM).append("=").append(rid); + + for (UIComponent kid : image.getChildren()) { + if (kid instanceof UIParameter) { + UIParameter param = (UIParameter) kid; + + builder.append("&").append(param.getName()).append("=").append(param.getValue()); + } + } + + src = builder.toString(); + + if (!image.isCache()) { + src += src.contains("?") ? "&" : "?"; + src += "primefaces_image=" + UUID.randomUUID().toString(); + } + + src = context.getExternalContext().encodeResourceURL(src); + return src; + + } else { + return super.getImageSrc(context, image); + } + } } diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedRendererHandler.java b/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedRendererHandler.java index cd07aa4..16d5830 100644 --- a/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedRendererHandler.java +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/AdvancedRendererHandler.java @@ -1,20 +1,20 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package be.rubus.web.jsf.primefaces; @@ -29,18 +29,18 @@ */ public class AdvancedRendererHandler extends TagHandler { - public static final String ADVANCED_RENDERING = "ADVANCED_RENDERING"; + public static final String ADVANCED_RENDERING = "ADVANCED_RENDERING"; - private Boolean needAdvancedRendering; + private Boolean needAdvancedRendering; - public AdvancedRendererHandler(TagConfig config) { - super(config); - TagAttribute advancedRendering = config.getTag().getAttributes().get("value"); - needAdvancedRendering = Boolean.valueOf(advancedRendering.getValue()); - } + public AdvancedRendererHandler(TagConfig config) { + super(config); + TagAttribute advancedRendering = config.getTag().getAttributes().get("value"); + needAdvancedRendering = Boolean.valueOf(advancedRendering.getValue()); + } - @Override - public void apply(FaceletContext ctx, UIComponent parent) throws IOException { - parent.getAttributes().put(ADVANCED_RENDERING, needAdvancedRendering); - } + @Override + public void apply(FaceletContext ctx, UIComponent parent) throws IOException { + parent.getAttributes().put(ADVANCED_RENDERING, needAdvancedRendering); + } } diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageManager.java b/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageManager.java index 703fb57..0ec6500 100644 --- a/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageManager.java +++ b/core/src/main/java/be/rubus/web/jsf/primefaces/GraphicImageManager.java @@ -1,20 +1,20 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package be.rubus.web.jsf.primefaces; @@ -25,19 +25,15 @@ import javax.faces.bean.SessionScoped; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** */ @@ -45,74 +41,82 @@ @ManagedBean(name = "GraphicImageManager") public class GraphicImageManager implements HttpSessionBindingListener { - private Mapsun.misc.Encoder()/Decoder()
.String
this
+ * version is about three times as fast due to the fact that the Commons Codec result has to be recoded
+ * to a String
from byte[]
, which is very expensive.sun.misc.Encoder()/Decoder()
produce temporary arrays but since performance
+ * is quite low it probably does.char[]
representation i accordance with RFC 2045.
+ *
+ * @param sArr The bytes to convert. If null
or length 0 an empty array will be returned.
+ * @param lineSep Optional "\r\n" after 76 characters, unless end of file.null
.
+ */
+ public final static char[] encodeToChar(byte[] sArr, boolean lineSep) {
+ // Check special case
+ int sLen = sArr != null ? sArr.length : 0;
+ if (sLen == 0) {
+ return new char[0];
+ }
+
+ int eLen = (sLen / 3) * 3; // Length of even 24-bits.
+ int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
+ int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
+ char[] dArr = new char[dLen];
+
+ // Encode even 24-bits
+ for (int s = 0, d = 0, cc = 0; s < eLen; ) {
+ // Copy next three bytes into lower 24 bits of int, paying attension to sign.
+ int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
+
+ // Encode the int into four chars
+ dArr[d++] = CA[(i >>> 18) & 0x3f];
+ dArr[d++] = CA[(i >>> 12) & 0x3f];
+ dArr[d++] = CA[(i >>> 6) & 0x3f];
+ dArr[d++] = CA[i & 0x3f];
+
+ // Add optional line separator
+ if (lineSep && ++cc == 19 && d < dLen - 2) {
+ dArr[d++] = '\r';
+ dArr[d++] = '\n';
+ cc = 0;
+ }
+ }
+
+ // Pad and encode last bits if source isn't even 24 bits.
+ int left = sLen - eLen; // 0 - 2.
+ if (left > 0) {
+ // Prepare the int
+ int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
+
+ // Set last four chars
+ dArr[dLen - 4] = CA[i >> 12];
+ dArr[dLen - 3] = CA[(i >>> 6) & 0x3f];
+ dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '=';
+ dArr[dLen - 1] = '=';
+ }
+ return dArr;
+ }
+
+ /**
+ * Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with
+ * and without line separators.
+ *
+ * @param sArr The source array. null
or length 0 will return an empty array.
+ * @return The decoded array of bytes. May be of length 0. Will be null
if the legal characters
+ * (including '=') isn't divideable by 4. (I.e. definitely corrupted).
+ */
+ public final static byte[] decode(char[] sArr) {
+ // Check special case
+ int sLen = sArr != null ? sArr.length : 0;
+ if (sLen == 0) {
+ return new byte[0];
+ }
+
+ // Count illegal characters (including '\r', '\n') to know what size the returned array will be,
+ // so we don't have to reallocate & copy it later.
+ int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
+ for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
+ if (IA[sArr[i]] < 0) {
+ sepCnt++;
+ }
+
+ // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
+ if ((sLen - sepCnt) % 4 != 0) {
+ return null;
+ }
+
+ int pad = 0;
+ for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0; )
+ if (sArr[i] == '=') {
+ pad++;
+ }
+
+ int len = ((sLen - sepCnt) * 6 >> 3) - pad;
+
+ byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
+
+ for (int s = 0, d = 0; d < len; ) {
+ // Assemble three bytes into an int from four "valid" characters.
+ int i = 0;
+ for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
+ int c = IA[sArr[s++]];
+ if (c >= 0) {
+ i |= c << (18 - j * 6);
+ } else {
+ j--;
+ }
+ }
+ // Add the bytes
+ dArr[d++] = (byte) (i >> 16);
+ if (d < len) {
+ dArr[d++] = (byte) (i >> 8);
+ if (d < len) {
+ dArr[d++] = (byte) i;
+ }
+ }
+ }
+ return dArr;
+ }
+
+ /**
+ * Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as
+ * fast as {@link #decode(char[])}. The preconditions are:null
will throw an exception.
+ * @return The decoded array of bytes. May be of length 0.
+ */
+ public final static byte[] decodeFast(char[] sArr) {
+ // Check special case
+ int sLen = sArr.length;
+ if (sLen == 0) {
+ return new byte[0];
+ }
+
+ int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
+
+ // Trim illegal chars from start
+ while (sIx < eIx && IA[sArr[sIx]] < 0)
+ sIx++;
+
+ // Trim illegal chars from end
+ while (eIx > 0 && IA[sArr[eIx]] < 0)
+ eIx--;
+
+ // get the padding count (=) (0, 1 or 2)
+ int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
+ int cCnt = eIx - sIx + 1; // Content count including possible separators
+ int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
+
+ int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
+ byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
+
+ // Decode all but the last 0 - 2 bytes.
+ int d = 0;
+ for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) {
+ // Assemble three bytes into an int from four "valid" characters.
+ int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
+
+ // Add the bytes
+ dArr[d++] = (byte) (i >> 16);
+ dArr[d++] = (byte) (i >> 8);
+ dArr[d++] = (byte) i;
+
+ // If line separator, jump over it.
+ if (sepCnt > 0 && ++cc == 19) {
+ sIx += 2;
+ cc = 0;
+ }
+ }
+
+ if (d < len) {
+ // Decode last 1-3 bytes (incl '=') into 1-3 bytes
+ int i = 0;
+ for (int j = 0; sIx <= eIx - pad; j++)
+ i |= IA[sArr[sIx++]] << (18 - j * 6);
+
+ for (int r = 16; d < len; r -= 8)
+ dArr[d++] = (byte) (i >> r);
+ }
+
+ return dArr;
+ }
+
+ // ****************************************************************************************
+ // * byte[] version
+ // ****************************************************************************************
+
+ /**
+ * Encodes a raw byte array into a BASE64 byte[]
representation i accordance with RFC 2045.
+ *
+ * @param sArr The bytes to convert. If null
or length 0 an empty array will be returned.
+ * @param lineSep Optional "\r\n" after 76 characters, unless end of file.null
.
+ */
+ public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) {
+ return encodeToByte(sArr, 0, sArr != null ? sArr.length : 0, lineSep);
+ }
+
+ /**
+ * Encodes a raw byte array into a BASE64 byte[]
representation i accordance with RFC 2045.
+ *
+ * @param sArr The bytes to convert. If null
an empty array will be returned.
+ * @param sOff The starting position in the bytes to convert.
+ * @param sLen The number of bytes to convert. If 0 an empty array will be returned.
+ * @param lineSep Optional "\r\n" after 76 characters, unless end of file.null
.
+ */
+ public final static byte[] encodeToByte(byte[] sArr, int sOff, int sLen, boolean lineSep) {
+ // Check special case
+ if (sArr == null || sLen == 0) {
+ return new byte[0];
+ }
+
+ int eLen = (sLen / 3) * 3; // Length of even 24-bits.
+ int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
+ int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
+ byte[] dArr = new byte[dLen];
+
+ // Encode even 24-bits
+ for (int s = sOff, d = 0, cc = 0; s < sOff + eLen; ) {
+ // Copy next three bytes into lower 24 bits of int, paying attension to sign.
+ int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
+
+ // Encode the int into four chars
+ dArr[d++] = (byte) CA[(i >>> 18) & 0x3f];
+ dArr[d++] = (byte) CA[(i >>> 12) & 0x3f];
+ dArr[d++] = (byte) CA[(i >>> 6) & 0x3f];
+ dArr[d++] = (byte) CA[i & 0x3f];
+
+ // Add optional line separator
+ if (lineSep && ++cc == 19 && d < dLen - 2) {
+ dArr[d++] = '\r';
+ dArr[d++] = '\n';
+ cc = 0;
+ }
+ }
+
+ // Pad and encode last bits if source isn't an even 24 bits.
+ int left = sLen - eLen; // 0 - 2.
+ if (left > 0) {
+ // Prepare the int
+ int i = ((sArr[sOff + eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sOff + sLen - 1] & 0xff) << 2) : 0);
+
+ // Set last four chars
+ dArr[dLen - 4] = (byte) CA[i >> 12];
+ dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f];
+ dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '=';
+ dArr[dLen - 1] = '=';
+ }
+ return dArr;
+ }
+
+ /**
+ * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with
+ * and without line separators.
+ *
+ * @param sArr The source array. Length 0 will return an empty array. null
will throw an exception.
+ * @return The decoded array of bytes. May be of length 0. Will be null
if the legal characters
+ * (including '=') isn't divideable by 4. (I.e. definitely corrupted).
+ */
+ public final static byte[] decode(byte[] sArr) {
+ return decode(sArr, 0, sArr.length);
+ }
+
+ /**
+ * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with
+ * and without line separators.
+ *
+ * @param sArr The source array. null
will throw an exception.
+ * @param sOff The starting position in the source array.
+ * @param sLen The number of bytes to decode from the source array. Length 0 will return an empty array.
+ * @return The decoded array of bytes. May be of length 0. Will be null
if the legal characters
+ * (including '=') isn't divideable by 4. (I.e. definitely corrupted).
+ */
+ public final static byte[] decode(byte[] sArr, int sOff, int sLen) {
+ // Count illegal characters (including '\r', '\n') to know what size the returned array will be,
+ // so we don't have to reallocate & copy it later.
+ int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
+ for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
+ if (IA[sArr[sOff + i] & 0xff] < 0) {
+ sepCnt++;
+ }
+
+ // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
+ if ((sLen - sepCnt) % 4 != 0) {
+ return null;
+ }
+
+ int pad = 0;
+ for (int i = sLen; i > 1 && IA[sArr[sOff + --i] & 0xff] <= 0; )
+ if (sArr[sOff + i] == '=') {
+ pad++;
+ }
+
+ int len = ((sLen - sepCnt) * 6 >> 3) - pad;
+
+ byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
+
+ for (int s = 0, d = 0; d < len; ) {
+ // Assemble three bytes into an int from four "valid" characters.
+ int i = 0;
+ for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
+ int c = IA[sArr[sOff + s++] & 0xff];
+ if (c >= 0) {
+ i |= c << (18 - j * 6);
+ } else {
+ j--;
+ }
+ }
+
+ // Add the bytes
+ dArr[d++] = (byte) (i >> 16);
+ if (d < len) {
+ dArr[d++] = (byte) (i >> 8);
+ if (d < len) {
+ dArr[d++] = (byte) i;
+ }
+ }
+ }
+
+ return dArr;
+ }
+
+
+ /**
+ * Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as
+ * fast as {@link #decode(byte[])}. The preconditions are:null
will throw an exception.
+ * @return The decoded array of bytes. May be of length 0.
+ */
+ public final static byte[] decodeFast(byte[] sArr) {
+ // Check special case
+ int sLen = sArr.length;
+ if (sLen == 0) {
+ return new byte[0];
+ }
+
+ int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
+
+ // Trim illegal chars from start
+ while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0)
+ sIx++;
+
+ // Trim illegal chars from end
+ while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0)
+ eIx--;
+
+ // get the padding count (=) (0, 1 or 2)
+ int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
+ int cCnt = eIx - sIx + 1; // Content count including possible separators
+ int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
+
+ int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
+ byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
+
+ // Decode all but the last 0 - 2 bytes.
+ int d = 0;
+ for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) {
+ // Assemble three bytes into an int from four "valid" characters.
+ int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
+
+ // Add the bytes
+ dArr[d++] = (byte) (i >> 16);
+ dArr[d++] = (byte) (i >> 8);
+ dArr[d++] = (byte) i;
+
+ // If line separator, jump over it.
+ if (sepCnt > 0 && ++cc == 19) {
+ sIx += 2;
+ cc = 0;
+ }
+ }
+
+ if (d < len) {
+ // Decode last 1-3 bytes (incl '=') into 1-3 bytes
+ int i = 0;
+ for (int j = 0; sIx <= eIx - pad; j++)
+ i |= IA[sArr[sIx++]] << (18 - j * 6);
+
+ for (int r = 16; d < len; r -= 8)
+ dArr[d++] = (byte) (i >> r);
+ }
+
+ return dArr;
+ }
+
+ // ****************************************************************************************
+ // * String version
+ // ****************************************************************************************
+
+ /**
+ * Encodes a raw byte array into a BASE64 String
representation i accordance with RFC 2045.
+ *
+ * @param sArr The bytes to convert. If null
or length 0 an empty array will be returned.
+ * @param lineSep Optional "\r\n" after 76 characters, unless end of file.null
.
+ */
+ public final static String encodeToString(byte[] sArr, boolean lineSep) {
+ // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower.
+ return new String(encodeToChar(sArr, lineSep));
+ }
+
+ /**
+ * Decodes a BASE64 encoded String
. All illegal characters will be ignored and can handle both strings with
+ * and without line separators.decode(str.toCharArray())
instead. That
+ * will create a temporary array though. This version will use str.charAt(i)
to iterate the string.
+ *
+ * @param str The source string. null
or length 0 will return an empty array.
+ * @return The decoded array of bytes. May be of length 0. Will be null
if the legal characters
+ * (including '=') isn't divideable by 4. (I.e. definitely corrupted).
+ */
+ public final static byte[] decode(String str) {
+ // Check special case
+ int sLen = str != null ? str.length() : 0;
+ if (sLen == 0) {
+ return new byte[0];
+ }
+
+ // Count illegal characters (including '\r', '\n') to know what size the returned array will be,
+ // so we don't have to reallocate & copy it later.
+ int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
+ for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
+ if (IA[str.charAt(i)] < 0) {
+ sepCnt++;
+ }
+
+ // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
+ if ((sLen - sepCnt) % 4 != 0) {
+ return null;
+ }
+
+ // Count '=' at end
+ int pad = 0;
+ for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0; )
+ if (str.charAt(i) == '=') {
+ pad++;
+ }
+
+ int len = ((sLen - sepCnt) * 6 >> 3) - pad;
+
+ byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
+
+ for (int s = 0, d = 0; d < len; ) {
+ // Assemble three bytes into an int from four "valid" characters.
+ int i = 0;
+ for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
+ int c = IA[str.charAt(s++)];
+ if (c >= 0) {
+ i |= c << (18 - j * 6);
+ } else {
+ j--;
+ }
+ }
+ // Add the bytes
+ dArr[d++] = (byte) (i >> 16);
+ if (d < len) {
+ dArr[d++] = (byte) (i >> 8);
+ if (d < len) {
+ dArr[d++] = (byte) i;
+ }
+ }
+ }
+ return dArr;
+ }
+
+ /**
+ * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as
+ * fast as {@link #decode(String)}. The preconditions are:null
will throw an exception.
+ * @return The decoded array of bytes. May be of length 0.
+ */
+ public final static byte[] decodeFast(String s) {
+ // Check special case
+ int sLen = s.length();
+ if (sLen == 0) {
+ return new byte[0];
+ }
+
+ int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
+
+ // Trim illegal chars from start
+ while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0)
+ sIx++;
+
+ // Trim illegal chars from end
+ while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0)
+ eIx--;
+
+ // get the padding count (=) (0, 1 or 2)
+ int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
+ int cCnt = eIx - sIx + 1; // Content count including possible separators
+ int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
+
+ int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
+ byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
+
+ // Decode all but the last 0 - 2 bytes.
+ int d = 0;
+ for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) {
+ // Assemble three bytes into an int from four "valid" characters.
+ int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)];
+
+ // Add the bytes
+ dArr[d++] = (byte) (i >> 16);
+ dArr[d++] = (byte) (i >> 8);
+ dArr[d++] = (byte) i;
+
+ // If line separator, jump over it.
+ if (sepCnt > 0 && ++cc == 19) {
+ sIx += 2;
+ cc = 0;
+ }
+ }
+
+ if (d < len) {
+ // Decode last 1-3 bytes (incl '=') into 1-3 bytes
+ int i = 0;
+ for (int j = 0; sIx <= eIx - pad; j++)
+ i |= IA[s.charAt(sIx++)] << (18 - j * 6);
+
+ for (int r = 16; d < len; r -= 8)
+ dArr[d++] = (byte) (i >> r);
+ }
+
+ return dArr;
+ }
+}
+
diff --git a/core/src/main/java/be/rubus/web/jsf/primefaces/util/StringEncrypter.java b/core/src/main/java/be/rubus/web/jsf/primefaces/util/StringEncrypter.java
new file mode 100644
index 0000000..257b099
--- /dev/null
+++ b/core/src/main/java/be/rubus/web/jsf/primefaces/util/StringEncrypter.java
@@ -0,0 +1,127 @@
+package be.rubus.web.jsf.primefaces.util;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.faces.FacesException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.KeySpec;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ */
+public class StringEncrypter {
+
+ private static final Logger LOG = Logger.getLogger(StringEncrypter.class.getName());
+
+ private Cipher ecipher;
+ private Cipher dcipher;
+
+ /**
+ * Constructor used to create this object. Responsible for setting and initializing this object's encrypter and decrypter Chipher instances given a Secret
+ * Key and algorithm.
+ *
+ * @param key Secret Key used to initialize both the encrypter and decrypter instances.
+ * @param algorithm Which algorithm to use for creating the encrypter and decrypter instances.
+ */
+ public StringEncrypter(SecretKey key, String algorithm) {
+ try {
+ ecipher = Cipher.getInstance(algorithm);
+ dcipher = Cipher.getInstance(algorithm);
+ ecipher.init(Cipher.ENCRYPT_MODE, key);
+ dcipher.init(Cipher.DECRYPT_MODE, key);
+ } catch (Exception e) {
+ throw new FacesException("Could not initialize Cipher objects", e);
+ }
+ }
+
+ /**
+ * Constructor used to create this object. Responsible for setting and initializing this object's encrypter and decrypter Chipher instances given a Pass
+ * Phrase and algorithm.
+ *
+ * @param passPhrase Pass Phrase used to initialize both the encrypter and decrypter instances.
+ */
+ public StringEncrypter(String passPhrase) {
+
+ // 8-bytes Salt
+ byte[] salt = {
+ (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
+ (byte) 0x56, (byte) 0x34, (byte) 0xE3, (byte) 0x03
+ };
+
+ // Iteration count
+ int iterationCount = 19;
+
+ try {
+
+ KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
+ SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
+
+ ecipher = Cipher.getInstance("PBEWithMD5AndDES");
+ dcipher = Cipher.getInstance("PBEWithMD5AndDES");
+
+ // Prepare the parameters to the cipthers
+ AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
+
+ ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
+ dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
+
+ } catch (Exception e) {
+ throw new FacesException("Could not initialize Cipher objects", e);
+ }
+ }
+
+ /**
+ * Takes a single String as an argument and returns an Encrypted version of that String.
+ *
+ * @param str String to be encrypted
+ * @return String
Encrypted version of the provided String
+ */
+ public String encrypt(String str) {
+ try {
+ // Encode the string into bytes using utf-8
+ byte[] utf8 = str.getBytes("UTF8");
+
+ // Encrypt
+ byte[] enc = ecipher.doFinal(utf8);
+
+ // Encode bytes to base64 to get a string
+ return Base64.encodeToString(enc, false);
+
+ } catch (Exception e) {
+ LOG.log(Level.WARNING, "Could not encrypt string", e);
+ }
+
+ return null;
+ }
+
+ /**
+ * Takes a encrypted String as an argument, decrypts and returns the decrypted String.
+ *
+ * @param str Encrypted String to be decrypted
+ * @return String
Decrypted version of the provided String
+ */
+ public String decrypt(String str) {
+
+ try {
+
+ // Decode base64 to get bytes
+ byte[] dec = Base64.decode(str);
+
+ // Decrypt
+ byte[] utf8 = dcipher.doFinal(dec);
+
+ // Decode using utf-8
+ return new String(utf8, "UTF8");
+
+ } catch (Exception e) {
+ LOG.log(Level.WARNING, "Could not decrypt string", e);
+ }
+
+ return null;
+ }
+}
diff --git a/demo/src/main/java/be/rubus/web/jsf/view/Bean.java b/demo/src/main/java/be/rubus/web/jsf/view/Bean.java
index 8a3c13d..22c3a91 100644
--- a/demo/src/main/java/be/rubus/web/jsf/view/Bean.java
+++ b/demo/src/main/java/be/rubus/web/jsf/view/Bean.java
@@ -24,6 +24,7 @@
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.SessionScoped;
+import javax.faces.bean.ViewScoped;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import java.io.InputStream;
@@ -31,7 +32,7 @@
import java.util.List;
@ManagedBean(name = "imageBean")
-@RequestScoped
+@ViewScoped
public class Bean {
private StreamedContent image;
private List dataList;
diff --git a/demo/src/main/webapp/index.xhtml b/demo/src/main/webapp/index.xhtml
index 0215606..b601ab2 100644
--- a/demo/src/main/webapp/index.xhtml
+++ b/demo/src/main/webapp/index.xhtml
@@ -39,6 +39,9 @@
+