Merge pull request #1201 from qivicon/esh-should-work-on-CP2-with-portablebase64

esh should work on cp2 with portablebase64
This commit is contained in:
Markus Rathgeb 2016-04-04 08:09:27 +02:00
Родитель a1c56aa7c4 b2d9035aa8
Коммит 622920354d
5 изменённых файлов: 434 добавлений и 5 удалений

Просмотреть файл

@ -0,0 +1,203 @@
/**
* Copyright (c) 2016 Deutsche Telekom AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.core.internal
import static org.hamcrest.CoreMatchers.*
import static org.hamcrest.MatcherAssert.assertThat
import static org.junit.Assert.fail
import java.io.UnsupportedEncodingException
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
/**
* @author Jochen Hiller - Initial contribution
* @author Miki Jankov - Adding new tests and fixing existing ones
*/
public class PortableBase64Test {
private static boolean isJava8OrNewer = true
@Before
public void testInitialize() {
try {
Class.forName("java.util.Base64", false, PortableBase64.class.getClassLoader())
} catch (ClassNotFoundException ex) {
// not found, so we run on JavaSE 7 or older
isJava8OrNewer = false
}
PortableBase64.initialize()
}
@Test
public void testGetStaticClasses() {
PortableBase64.Decoder decoder = PortableBase64.getDecoder()
assertThat(decoder, is(not(nullValue())))
PortableBase64.Encoder encoder = PortableBase64.getEncoder()
assertThat(encoder, is(not(nullValue())))
}
@Test
public void testDecode() {
// see https://tools.ietf.org/html/rfc4648#section-10
FROM_BASE64("", "")
FROM_BASE64("Zg==", "f")
FROM_BASE64("Zm8=", "fo")
FROM_BASE64("Zm9v", "foo")
FROM_BASE64("Zm9vYg==", "foob")
FROM_BASE64("Zm9vYmE=", "fooba")
FROM_BASE64("Zm9vYmFy", "foobar")
}
@Test
public void testEncode() {
// see https://tools.ietf.org/html/rfc4648#section-10
TO_BASE64("", "")
TO_BASE64("f", "Zg==")
TO_BASE64("fo", "Zm8=")
TO_BASE64("foo", "Zm9v")
TO_BASE64("foob", "Zm9vYg==")
TO_BASE64("fooba", "Zm9vYmE=")
TO_BASE64("foobar", "Zm9vYmFy")
}
@Test(expected = java.lang.IllegalArgumentException.class)
public void testDecodeInvalidCharacterDot() {
PortableBase64.getDecoder().decode("......")
}
@Test(expected = java.lang.IllegalArgumentException.class)
public void testDecodeInvalidCharacterDash() {
PortableBase64.getDecoder().decode("---")
}
@Test(expected = java.lang.IllegalStateException.class)
public void testEncodeWhenClassNotInitialized() {
PortableBase64.isInitialized = false
PortableBase64.getEncoder().encode("".bytes)
}
@Test(expected = java.lang.IllegalStateException.class)
public void testDecodeWhenClassNotInitialized() {
PortableBase64.isInitialized = false
PortableBase64.getDecoder().decode("")
}
/** JavaSE 7 does NOT throw an IllegalArgumentException. */
@Test
public void testDecodeInvalidPaddingStart1() {
try {
PortableBase64.getDecoder().decode("=A==")
if (isJava8OrNewer) {
fail("IllegalArgumentException expected in JavaSE 8")
}
} catch (Exception ex) {
if (!isJava8OrNewer) {
fail("No exception expected in JavaSE 7")
}
}
}
/** JavaSE 7 does NOT throw an IllegalArgumentException. */
@Test
public void testDecodeInvalidPaddingStart2() {
try {
PortableBase64.getDecoder().decode("====")
if (isJava8OrNewer) {
fail("IllegalArgumentException expected in JavaSE 8")
}
} catch (Exception ex) {
if (!isJava8OrNewer) {
fail("No exception expected in JavaSE 7")
}
}
}
/** JavaSE 7 does NOT throw an IllegalArgumentException. */
@Test
public void testDecodeInvalidPaddingMiddle() {
try {
PortableBase64.getDecoder().decode("Zg=a")
if (isJava8OrNewer) {
fail("IllegalArgumentException expected in JavaSE 8")
}
} catch (Exception ex) {
if (!isJava8OrNewer) {
fail("No exception expected in JavaSE 7")
}
}
}
/**
* TODO we can not easily compare this by native calls as this would
*/
@Test
public void testPerformancePortableBase64() {
long tStart = System.nanoTime()
int N = 10000000
for (int i = 0; i < N; i++) {
PortableBase64.getEncoder().encode("foobar".getBytes())
PortableBase64.getDecoder().decode("Zm9vYmFy")
}
long tEnd = System.nanoTime()
System.out.println("testPerformancePortableBase64 took " + (tEnd - tStart) / 1000 / 1000 + " ms for " + N
+ " iterations on " + System.getProperty("java.version") + ".")
}
@Test
public void testPerformanceJavaSE7() {
long tStart = System.nanoTime()
int N = 10000000
for (int i = 0; i < N; i++) {
javax.xml.bind.DatatypeConverter.printBase64Binary("foobar".getBytes())
javax.xml.bind.DatatypeConverter.parseBase64Binary("Zm9vYmFy")
}
long tEnd = System.nanoTime()
System.out.println("testPerformanceJavaSE7 " + (tEnd - tStart) / 1000 / 1000 + " ms for " + N
+ " iterations on " + System.getProperty("java.version") + ".")
}
/**
* If you want to run the test on JavaSE 8, remove @Ignore, enable the code below and import java.util.Base64 and
* compile for Java 8.
*/
@Test
@Ignore
public void testPerformanceJavaSE8() {
long tStart = System.nanoTime()
int N = 10000000
for (int i = 0; i < N; i++) {
// enable this code to run performance tests on JavaSE 8.
// Base64.getEncoder().encode("foobar".getBytes());
// Base64.getDecoder().decode("Zm9vYmFy");
}
long tEnd = System.nanoTime()
System.out.println("testPerformanceJavaSE7 " + (tEnd - tStart) / 1000 / 1000 + " ms for " + N
+ " iterations on " + System.getProperty("java.version") + ".")
}
private void FROM_BASE64(String base64, String output) {
try {
PortableBase64.Decoder decoder = PortableBase64.getDecoder()
byte[] decodedAsByteArray = decoder.decode(base64)
String decodedAsString = new String(decodedAsByteArray, "UTF-8")
assertThat(output, is(equalTo(decodedAsString)))
} catch (UnsupportedEncodingException ex) {
fail("Should never happen")
}
}
private void TO_BASE64(String input, String res) {
PortableBase64.Encoder encoder = PortableBase64.getEncoder()
String base64 = encoder.encode(input.getBytes())
assertThat(res, is(equalTo(base64)))
}
}

Просмотреть файл

@ -31,7 +31,7 @@ Bundle-License: http://www.eclipse.org/legal/epl-v10.html
Import-Package: com.google.common.base, Import-Package: com.google.common.base,
com.google.common.collect, com.google.common.collect,
com.google.gson, com.google.gson,
javax.xml.bind, javax.xml.bind;resolution:=optional,
org.apache.commons.lang, org.apache.commons.lang,
org.eclipse.smarthome.core.binding, org.eclipse.smarthome.core.binding,
org.eclipse.smarthome.core.binding.dto, org.eclipse.smarthome.core.binding.dto,

Просмотреть файл

@ -27,6 +27,7 @@ public class CoreActivator implements BundleActivator {
@Override @Override
public void start(BundleContext bc) throws Exception { public void start(BundleContext bc) throws Exception {
context = bc; context = bc;
PortableBase64.initialize();
logger.debug("Core bundle has been started."); logger.debug("Core bundle has been started.");
} }

Просмотреть файл

@ -0,0 +1,226 @@
/**
* Copyright (c) 2016 Deutsche Telekom AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.core.internal;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The class PortableBase64 will provide Base64 encode and decode functionality in a portable way for JavaSE 7 and
* JavaSE 8. Its design is based on the new java.util.Base64 class from JavaSE 8.
*
* The implementation checks for Java version. It will call either an included class from javax.xml.bind or use the new
* Base64 class from JavaSE 8. This has been chosen to avoid a compile dependency to Java 8.
*
* For JavaSE 7 it will use the class javax.xml.bind.DatatypeConverter for
* Base64 functionality. It will get the encode and decode methods to be able to call this methods later via reflection.
*
* For JavaSE 8 it will use the built-in class java.util.Base64, get the instances for encoder and decoder and will use
* them for calling.
*
* @author Jochen Hiller - Initial contribution
* @author Miki Jankov - Adding error checks
*
*/
public class PortableBase64 {
/**
* The encoder instance will be needed for JavaSE 8 as the encode method is an instance method. For JavaSE 7 this is
* null as it is a static method.
*/
private static Object encoderInstance;
/**
* The encode method will expect a signature of:
*
* <pre>
* String encodeMethod(byte[] b);
* </pre>
*/
private static Method encodeMethod;
/**
* The decoder instance will be needed for JavaSE 8 as the decode method is an instance method. For JavaSE 7 this is
* null as it is a static method.
*/
private static Object decoderInstance;
/**
* The decode method will expect a signature of:
*
* <pre>
* byte[] decodeMethod(String s);
* </pre>
*/
private static Method decodeMethod;
/**
* A flag to show if initialization was already performed
*/
private static volatile boolean isInitialized = false;
// static helpers to initialize
public static void initialize() {
if (isInitialized) {
logInfo("PortableBase64 class already initialized");
return;
}
// just try to get Java 8(or newer) class
boolean isJava8OrNewer = true;
try {
Class.forName("java.util.Base64", false, PortableBase64.class.getClassLoader());
} catch (ClassNotFoundException ex) {
// not found, so we run on JavaSE 7 or older
isJava8OrNewer = false;
}
logInfo("PortableBase64 class is running on JavaSE " + (isJava8OrNewer ? ">=8" : "<=7"));
try {
if (isJava8OrNewer) {
initializeJava8();
} else {
initializeJava7();
}
// make one test call for encode and decode to be sure that it is working
// see https://tools.ietf.org/html/rfc4648#section-10 for samples
String encodedAsString = (String) encodeMethod.invoke(encoderInstance, "foobar".getBytes("UTF-8"));
if (!"Zm9vYmFy".equals(encodedAsString)) {
throw new IllegalAccessError("encode does not work as expected");
}
byte[] decodedAsByteArray = (byte[]) decodeMethod.invoke(decoderInstance, "Zm9vYmFy");
String decodedAsString = new String(decodedAsByteArray, "UTF-8");
if (!"foobar".equals(decodedAsString)) {
throw new IllegalAccessError("decode does not work as expected");
}
PortableBase64.isInitialized = true;
} catch (Exception ex) {
logError(
"Could not initialize PortableBase64 class- Check your Java environment to run on Java 7 or 8 or later.",
ex);
encodeMethod = null;
decodeMethod = null;
// TODO fallback to an internal implementation? E.g. for JavaSE 6/5/4...
}
}
/**
* Initialization for JavaSE 7 using javax.xml.bind.DatatypeConverter class.
*/
private static void initializeJava7() throws Exception {
// now we know that the class DataTypeConverter is available
Class<?> datatypeConverterClass = Class.forName("javax.xml.bind.DatatypeConverter", false,
PortableBase64.class.getClassLoader());
// preserve static methods for later use
encodeMethod = datatypeConverterClass.getMethod("printBase64Binary", new Class[] { byte[].class });
decodeMethod = datatypeConverterClass.getMethod("parseBase64Binary", new Class[] { String.class });
}
/**
* Initialization for JavaSE 8 using java.util.Base64 class.
*/
private static void initializeJava8() throws Exception {
Class<?> baseClass = Class.forName("java.util.Base64", false, PortableBase64.class.getClassLoader());
// search for inner classes
Class<?>[] innerClasses = baseClass.getDeclaredClasses();
Class<?> encoderClass = null;
Class<?> decoderClass = null;
for (int i = 0; i < innerClasses.length; i++) {
Class<?> c = innerClasses[i];
if (c.getName().equals("java.util.Base64$Encoder")) {
encoderClass = c;
} else if (c.getName().equals("java.util.Base64$Decoder")) {
decoderClass = c;
} else {
// ignore, we do not need
}
}
// check if we found the classes
if (encoderClass == null) {
throw new IllegalAccessError("Could not find encoderClass java.util.Base64$Encoder");
}
if (decoderClass == null) {
throw new IllegalAccessError("Could not find decoderClass java.util.Base64$Decoder");
}
// preserve the instances of encoder and decoder
PortableBase64.encoderInstance = baseClass.getMethod("getEncoder", new Class[] {}).invoke(null,
(Object[]) null);
PortableBase64.decoderInstance = baseClass.getMethod("getDecoder", new Class[] {}).invoke(null,
(Object[]) null);
// preserve method on instances for later use
encodeMethod = encoderClass.getMethod("encodeToString", new Class[] { byte[].class });
decodeMethod = decoderClass.getMethod("decode", new Class[] { String.class });
}
private static void logError(String msg, Exception ex) {
Logger l = LoggerFactory.getLogger(PortableBase64.class);
l.error(msg, ex);
}
private static void logInfo(String msg) {
Logger l = LoggerFactory.getLogger(PortableBase64.class);
l.info(msg);
}
// PortablBase64 implementation
private static Encoder basicEncoder = new Encoder();
private static Decoder basicDecoder = new Decoder();
public static Encoder getEncoder() {
return basicEncoder;
}
public static Decoder getDecoder() {
return basicDecoder;
}
public static class Encoder {
public String encode(byte[] base64) {
try {
if (!isInitialized)
throw new IllegalStateException("PortableBase64 is not initialized");
Object res = encodeMethod.invoke(encoderInstance, base64);
return (String) res;
} catch (IllegalStateException ise) {
throw ise;
} catch (Exception ex) {
PortableBase64.logError("PortableBase64 - Could not encode", ex);
throw new IllegalArgumentException(ex.getMessage());
}
}
}
public static class Decoder {
public byte[] decode(String s) {
try {
if (!isInitialized)
throw new IllegalStateException("PortableBase64 is not initialized");
Object res = decodeMethod.invoke(decoderInstance, s);
byte[] b = (byte[]) res;
// System.out.println("'" + new String(b) + "'");
if ((b.length == 0) && (s.length() > 0)) {
throw new IllegalArgumentException("decode returned empty result");
}
return b;
} catch (IllegalStateException ise) {
throw ise;
} catch (IllegalArgumentException ex) {
throw ex;
} catch (Exception ex) {
PortableBase64.logError("PortableBase64 - Could not decode", ex);
throw new IllegalArgumentException(ex.getMessage());
}
}
}
}

Просмотреть файл

@ -9,8 +9,7 @@ package org.eclipse.smarthome.core.library.types;
import java.util.Arrays; import java.util.Arrays;
import javax.xml.bind.DatatypeConverter; import org.eclipse.smarthome.core.internal.PortableBase64;
import org.eclipse.smarthome.core.types.PrimitiveType; import org.eclipse.smarthome.core.types.PrimitiveType;
import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.State;
@ -38,12 +37,12 @@ public class RawType implements PrimitiveType, State {
} }
public static RawType valueOf(String value) { public static RawType valueOf(String value) {
return new RawType(DatatypeConverter.parseBase64Binary(value)); return new RawType(PortableBase64.getDecoder().decode(value));
} }
@Override @Override
public String toString() { public String toString() {
return DatatypeConverter.printBase64Binary(bytes); return PortableBase64.getEncoder().encode(bytes);
} }
@Override @Override