From ed4b4af2de4b02efd3f097323e9a999f905e60e6 Mon Sep 17 00:00:00 2001 From: statta Date: Fri, 17 May 2019 14:55:18 -0400 Subject: [PATCH] Fix Clob,Blob read and writes Issue-ID: MUSIC-388 Change-Id: I3ddd84f097231a21423325b04c34a4a90e4ea0e0 Signed-off-by: statta --- .../org/apache/calcite/avatica/AvaticaSite.java | 612 ++++++++ .../calcite/avatica/util/AbstractCursor.java | 1504 ++++++++++++++++++++ .../org/onap/music/logging/EELFLoggerDelegate.java | 286 +++- mdbc-server/src/main/resources/logback.xml | 49 +- 4 files changed, 2389 insertions(+), 62 deletions(-) create mode 100644 mdbc-server/src/main/java/org/apache/calcite/avatica/AvaticaSite.java create mode 100644 mdbc-server/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java diff --git a/mdbc-server/src/main/java/org/apache/calcite/avatica/AvaticaSite.java b/mdbc-server/src/main/java/org/apache/calcite/avatica/AvaticaSite.java new file mode 100644 index 0000000..5fd4e69 --- /dev/null +++ b/mdbc-server/src/main/java/org/apache/calcite/avatica/AvaticaSite.java @@ -0,0 +1,612 @@ +/* + * 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 org.apache.calcite.avatica; + +import org.apache.calcite.avatica.remote.TypedValue; +import org.apache.calcite.avatica.util.Cursor; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Calendar; + +/** + * A location that a value can be written to or read from. + */ +public class AvaticaSite { + final AvaticaParameter parameter; + + /** Calendar is not thread-safe. But calendar is only used from within one + * thread, and we have to trust that clients are not modifying calendars + * that they pass to us in a method such as + * {@link java.sql.PreparedStatement#setTime(int, Time, Calendar)}, so we do + * not need to synchronize access. */ + final Calendar calendar; + private final int index; + final TypedValue[] slots; + + /** Value that means the parameter has been set to null. + * If value is null, parameter has not been set. */ + public static final Object DUMMY_VALUE = Dummy.INSTANCE; + + public AvaticaSite(AvaticaParameter parameter, Calendar calendar, int index, + TypedValue[] slots) { + assert calendar != null; + assert parameter != null; + assert slots != null; + this.parameter = parameter; + this.calendar = calendar; + this.index = index; + this.slots = slots; + } + + private TypedValue wrap(ColumnMetaData.Rep rep, Object o, + Calendar calendar) { + return TypedValue.ofJdbc(rep, o, calendar); + } + + private TypedValue wrap(ColumnMetaData.Rep rep, Object o) { + return TypedValue.ofJdbc(rep, o, calendar); + } + + public boolean isSet(int index) { + return slots[index] != null; + } + + public void setByte(byte o) { + slots[index] = wrap(ColumnMetaData.Rep.BYTE, o); + } + + public void setChar(char o) { + slots[index] = wrap(ColumnMetaData.Rep.CHARACTER, o); + } + + public void setShort(short o) { + slots[index] = wrap(ColumnMetaData.Rep.SHORT, o); + } + + public void setInt(int o) { + slots[index] = wrap(ColumnMetaData.Rep.INTEGER, o); + } + + public void setLong(long o) { + slots[index] = wrap(ColumnMetaData.Rep.LONG, o); + } + + public void setBoolean(boolean o) { + slots[index] = wrap(ColumnMetaData.Rep.BOOLEAN, o); + } + + public void setRowId(RowId x) { + slots[index] = wrap(ColumnMetaData.Rep.OBJECT, x); + } + + public void setNString(String o) { + slots[index] = wrap(ColumnMetaData.Rep.STRING, o); + } + + public void setNCharacterStream(Reader value, long length) { + } + + public void setNClob(NClob value) { + slots[index] = wrap(ColumnMetaData.Rep.OBJECT, value); + } + + public void setClob(Reader reader, long length) { + } + + public void setBlob(InputStream inputStream, long length) { + } + + public void setNClob(Reader reader, long length) { + } + + public void setSQLXML(SQLXML xmlObject) { + slots[index] = wrap(ColumnMetaData.Rep.OBJECT, xmlObject); + } + + public void setAsciiStream(InputStream x, long length) { + } + + public void setBinaryStream(InputStream x, long length) { + } + + public void setCharacterStream(Reader reader, long length) { + } + + public void setAsciiStream(InputStream x) { + } + + public void setBinaryStream(InputStream x) { + } + + public void setCharacterStream(Reader reader) { + } + + public void setNCharacterStream(Reader value) { + } + + public void setClob(Reader reader) { + } + + public void setClob(InputStream inputStream) { + } + + public void setBlob(InputStream inputStream) { + } + + public void setNClob(Reader reader) { + } + + public void setUnicodeStream(InputStream x, int length) { + } + + public void setFloat(float x) { + slots[index] = wrap(ColumnMetaData.Rep.FLOAT, x); + } + + public void setDouble(double x) { + slots[index] = wrap(ColumnMetaData.Rep.DOUBLE, x); + } + + public void setBigDecimal(BigDecimal x) { + slots[index] = wrap(ColumnMetaData.Rep.NUMBER, x); + } + + public void setString(String x) { + slots[index] = wrap(ColumnMetaData.Rep.STRING, x); + } + + public void setBytes(byte[] x) { + slots[index] = wrap(ColumnMetaData.Rep.BYTE_STRING, x); + } + + public void setTimestamp(Timestamp x, Calendar calendar) { + slots[index] = wrap(ColumnMetaData.Rep.JAVA_SQL_TIMESTAMP, x, calendar); + } + + public void setTime(Time x, Calendar calendar) { + slots[index] = wrap(ColumnMetaData.Rep.JAVA_SQL_TIME, x, calendar); + } + + public void setDate(Date x, Calendar calendar) { + slots[index] = wrap(ColumnMetaData.Rep.JAVA_SQL_DATE, x, calendar); + } + + public void setObject(Object x, int targetSqlType) { + if (x == null || Types.NULL == targetSqlType) { + setNull(targetSqlType); + return; + } + switch (targetSqlType) { + case Types.CLOB: + if (x instanceof Clob) { + setClob((Clob) x); + break; + } else if (x instanceof InputStream) { + setClob((InputStream) x); + } + throw unsupportedCast(x.getClass(), Blob.class); + case Types.DATALINK: + case Types.NCLOB: + case Types.OTHER: + case Types.REF: + case Types.SQLXML: + case Types.STRUCT: + throw notImplemented(); + case Types.ARRAY: + setArray(toArray(x)); + break; + case Types.BIGINT: + setLong(toLong(x)); + break; + case Types.BINARY: + case Types.LONGVARBINARY: + case Types.VARBINARY: + setBytes(toBytes(x)); + break; + case Types.BIT: + case Types.BOOLEAN: + setBoolean(toBoolean(x)); + break; + case Types.BLOB: + if (x instanceof Blob) { + setBlob((Blob) x); + break; + } else if (x instanceof InputStream) { + setBlob((InputStream) x); + } + throw unsupportedCast(x.getClass(), Blob.class); + case Types.DATE: + setDate(toDate(x), calendar); + break; + case Types.DECIMAL: + case Types.NUMERIC: + setBigDecimal(toBigDecimal(x)); + break; + case Types.DISTINCT: + throw notImplemented(); + case Types.DOUBLE: + case Types.FLOAT: // yes really; SQL FLOAT is up to 8 bytes + setDouble(toDouble(x)); + break; + case Types.INTEGER: + setInt(toInt(x)); + break; + case Types.JAVA_OBJECT: + setObject(x); + break; + case Types.LONGNVARCHAR: + case Types.LONGVARCHAR: + case Types.NVARCHAR: + case Types.VARCHAR: + case Types.CHAR: + case Types.NCHAR: + setString(toString(x)); + break; + case Types.REAL: + setFloat(toFloat(x)); + break; + case Types.ROWID: + if (x instanceof RowId) { + setRowId((RowId) x); + break; + } + throw unsupportedCast(x.getClass(), RowId.class); + case Types.SMALLINT: + setShort(toShort(x)); + break; + case Types.TIME: + setTime(toTime(x), calendar); + break; + case Types.TIMESTAMP: + setTimestamp(toTimestamp(x), calendar); + break; + case Types.TINYINT: + setByte(toByte(x)); + break; + default: + throw notImplemented(); + } + } + + /** Similar logic to {@link #setObject}. */ + public static Object get(Cursor.Accessor accessor, int targetSqlType, + Calendar localCalendar) throws SQLException { + switch (targetSqlType) { + case Types.CLOB: + return accessor.getClob(); + case Types.DATALINK: + case Types.NCLOB: + case Types.REF: + case Types.SQLXML: + case Types.STRUCT: + throw notImplemented(); + case Types.ARRAY: + return accessor.getArray(); + case Types.BIGINT: + final long aLong = accessor.getLong(); + if (aLong == 0 && accessor.wasNull()) { + return null; + } + return aLong; + case Types.BINARY: + case Types.LONGVARBINARY: + case Types.VARBINARY: + return accessor.getBytes(); + case Types.BIT: + case Types.BOOLEAN: + final boolean aBoolean = accessor.getBoolean(); + if (!aBoolean && accessor.wasNull()) { + return null; + } + return aBoolean; + case Types.BLOB: + return accessor.getBlob(); + case Types.DATE: + return accessor.getDate(localCalendar); + case Types.DECIMAL: + case Types.NUMERIC: + return accessor.getBigDecimal(); + case Types.DISTINCT: + throw notImplemented(); + case Types.DOUBLE: + case Types.FLOAT: // yes really; SQL FLOAT is up to 8 bytes + final double aDouble = accessor.getDouble(); + if (aDouble == 0 && accessor.wasNull()) { + return null; + } + return aDouble; + case Types.INTEGER: + final int anInt = accessor.getInt(); + if (anInt == 0 && accessor.wasNull()) { + return null; + } + return anInt; + case Types.JAVA_OBJECT: + case Types.OTHER: + return accessor.getObject(); + case Types.LONGNVARCHAR: + case Types.LONGVARCHAR: + case Types.NVARCHAR: + case Types.VARCHAR: + case Types.CHAR: + case Types.NCHAR: + return accessor.getString(); + case Types.REAL: + final float aFloat = accessor.getFloat(); + if (aFloat == 0 && accessor.wasNull()) { + return null; + } + return aFloat; + case Types.ROWID: + throw notImplemented(); + case Types.SMALLINT: + final short aShort = accessor.getShort(); + if (aShort == 0 && accessor.wasNull()) { + return null; + } + return aShort; + case Types.TIME: + return accessor.getTime(localCalendar); + case Types.TIMESTAMP: + return accessor.getTimestamp(localCalendar); + case Types.TINYINT: + final byte aByte = accessor.getByte(); + if (aByte == 0 && accessor.wasNull()) { + return null; + } + return aByte; + default: + throw notImplemented(); + } + } + + public void setObject(Object x) { + slots[index] = TypedValue.ofJdbc(x, calendar); + } + + public void setNull(int sqlType) { + slots[index] = wrap(ColumnMetaData.Rep.OBJECT, null); + } + + public void setRef(Ref x) { + } + + public void setBlob(Blob x) { + InputStream iStream; + try { + iStream = x.getBinaryStream(); + int length =0; + while(iStream.read() != -1) + length ++; + setBytes(x.getBytes(1, length)); + + } catch (SQLException | IOException e) { + throw new RuntimeException(e); + } + + } + + public void setClob(Clob x) { + } + + public void setArray(Array x) { + slots[index] = wrap(ColumnMetaData.Rep.ARRAY, x); + } + + public void setNull(int sqlType, String typeName) { + } + + public void setURL(URL x) { + } + + public void setObject(Object x, int targetSqlType, + int scaleOrLength) { + } + + private static RuntimeException unsupportedCast(Class from, Class to) { + return new UnsupportedOperationException("Cannot convert from " + + from.getCanonicalName() + " to " + to.getCanonicalName()); + } + + private static RuntimeException notImplemented() { + return new RuntimeException("not implemented"); + } + + private static Array toArray(Object x) { + if (x instanceof Array) { + return (Array) x; + } + throw unsupportedCast(x.getClass(), Array.class); + } + + public static BigDecimal toBigDecimal(Object x) { + if (x instanceof BigDecimal) { + return (BigDecimal) x; + } else if (x instanceof BigInteger) { + return new BigDecimal((BigInteger) x); + } else if (x instanceof Number) { + if (x instanceof Double || x instanceof Float) { + return new BigDecimal(((Number) x).doubleValue()); + } else { + return new BigDecimal(((Number) x).longValue()); + } + } else if (x instanceof Boolean) { + return (Boolean) x ? BigDecimal.ONE : BigDecimal.ZERO; + } else if (x instanceof String) { + return new BigDecimal((String) x); + } + throw unsupportedCast(x.getClass(), BigDecimal.class); + } + + private static boolean toBoolean(Object x) { + if (x instanceof Boolean) { + return (Boolean) x; + } else if (x instanceof Number) { + return ((Number) x).intValue() != 0; + } else if (x instanceof String) { + String s = (String) x; + if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("yes")) { + return true; + } else if (s.equalsIgnoreCase("false") || s.equalsIgnoreCase("no")) { + return false; + } + } + throw unsupportedCast(x.getClass(), Boolean.TYPE); + } + + private static byte toByte(Object x) { + if (x instanceof Number) { + return ((Number) x).byteValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? (byte) 1 : (byte) 0; + } else if (x instanceof String) { + return Byte.parseByte((String) x); + } else { + throw unsupportedCast(x.getClass(), Byte.TYPE); + } + } + + private static byte[] toBytes(Object x) { + if (x instanceof byte[]) { + return (byte[]) x; + } + if (x instanceof String) { + return ((String) x).getBytes(StandardCharsets.UTF_8); + } + throw unsupportedCast(x.getClass(), byte[].class); + } + + private static Date toDate(Object x) { + if (x instanceof String) { + return Date.valueOf((String) x); + } + return new Date(toLong(x)); + } + + private static Time toTime(Object x) { + if (x instanceof String) { + return Time.valueOf((String) x); + } + return new Time(toLong(x)); + } + + private static Timestamp toTimestamp(Object x) { + if (x instanceof String) { + return Timestamp.valueOf((String) x); + } + return new Timestamp(toLong(x)); + } + + private static double toDouble(Object x) { + if (x instanceof Number) { + return ((Number) x).doubleValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? 1D : 0D; + } else if (x instanceof String) { + return Double.parseDouble((String) x); + } else { + throw unsupportedCast(x.getClass(), Double.TYPE); + } + } + + private static float toFloat(Object x) { + if (x instanceof Number) { + return ((Number) x).floatValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? 1F : 0F; + } else if (x instanceof String) { + return Float.parseFloat((String) x); + } else { + throw unsupportedCast(x.getClass(), Float.TYPE); + } + } + + private static int toInt(Object x) { + if (x instanceof Number) { + return ((Number) x).intValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? 1 : 0; + } else if (x instanceof String) { + return Integer.parseInt((String) x); + } else { + throw unsupportedCast(x.getClass(), Integer.TYPE); + } + } + + private static long toLong(Object x) { + if (x instanceof Number) { + return ((Number) x).longValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? 1L : 0L; + } else if (x instanceof String) { + return Long.parseLong((String) x); + } else { + throw unsupportedCast(x.getClass(), Long.TYPE); + } + } + + private static short toShort(Object x) { + if (x instanceof Number) { + return ((Number) x).shortValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? (short) 1 : (short) 0; + } else if (x instanceof String) { + return Short.parseShort((String) x); + } else { + throw unsupportedCast(x.getClass(), Short.TYPE); + } + } + + private static String toString(Object x) { + if (x instanceof String) { + return (String) x; + } else if (x instanceof Character + || x instanceof Boolean) { + return x.toString(); + } + throw unsupportedCast(x.getClass(), String.class); + } + + /** Singleton value to denote parameters that have been set to null (as + * opposed to not set). + * + *

Not a valid value for a parameter. + * + *

As an enum, it is serializable by Jackson. */ + private enum Dummy { + INSTANCE + } +} + +// End AvaticaSite.java diff --git a/mdbc-server/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java b/mdbc-server/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java new file mode 100644 index 0000000..cacbb8b --- /dev/null +++ b/mdbc-server/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java @@ -0,0 +1,1504 @@ +/* + * 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 org.apache.calcite.avatica.util; + +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.SQLDataException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Struct; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Map; +import javax.sql.rowset.serial.SerialBlob; +import org.apache.calcite.avatica.AvaticaSite; +import org.apache.calcite.avatica.AvaticaUtils; +import org.apache.calcite.avatica.ColumnMetaData; + +/** + * Base class for implementing a cursor. + * + *

Derived class needs to provide {@link Getter} and can override + * {@link org.apache.calcite.avatica.util.Cursor.Accessor} implementations if it + * wishes.

+ */ +public abstract class AbstractCursor implements Cursor { + /** + * Slot into which each accessor should write whether the + * value returned was null. + */ + protected final boolean[] wasNull = {false}; + + protected AbstractCursor() { + } + + public boolean wasNull() { + return wasNull[0]; + } + + public List createAccessors(List types, + Calendar localCalendar, ArrayImpl.Factory factory) { + List accessors = new ArrayList<>(); + for (ColumnMetaData type : types) { + accessors.add( + createAccessor(type, accessors.size(), localCalendar, factory)); + } + return accessors; + } + + protected Accessor createAccessor(ColumnMetaData columnMetaData, int ordinal, + Calendar localCalendar, ArrayImpl.Factory factory) { + // Create an accessor appropriate to the underlying type; the accessor + // can convert to any type in the same family. + Getter getter = createGetter(ordinal); + return createAccessor(columnMetaData, getter, localCalendar, factory); + } + + protected Accessor createAccessor(ColumnMetaData columnMetaData, + Getter getter, Calendar localCalendar, ArrayImpl.Factory factory) { + switch (columnMetaData.type.rep) { + case NUMBER: + switch (columnMetaData.type.id) { + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + case Types.NUMERIC: + case Types.DECIMAL: + return new NumberAccessor(getter, columnMetaData.scale); + } + } + switch (columnMetaData.type.id) { + case Types.TINYINT: + return new ByteAccessor(getter); + case Types.SMALLINT: + return new ShortAccessor(getter); + case Types.INTEGER: + return new IntAccessor(getter); + case Types.BIGINT: + return new LongAccessor(getter); + case Types.BOOLEAN: + return new BooleanAccessor(getter); + case Types.REAL: + return new FloatAccessor(getter); + case Types.FLOAT: + case Types.DOUBLE: + return new DoubleAccessor(getter); + case Types.DECIMAL: + return new BigDecimalAccessor(getter); + case Types.CHAR: + switch (columnMetaData.type.rep) { + case PRIMITIVE_CHAR: + case CHARACTER: + return new StringFromCharAccessor(getter, columnMetaData.displaySize); + default: + return new FixedStringAccessor(getter, columnMetaData.displaySize); + } + case Types.VARCHAR: + return new StringAccessor(getter); + case Types.BINARY: + case Types.VARBINARY: + switch (columnMetaData.type.rep) { + case STRING: + return new BinaryFromStringAccessor(getter); + default: + return new BinaryAccessor(getter); + } + case Types.DATE: + switch (columnMetaData.type.rep) { + case PRIMITIVE_INT: + case INTEGER: + case NUMBER: + return new DateFromNumberAccessor(getter, localCalendar); + case JAVA_SQL_DATE: + return new DateAccessor(getter); + default: + throw new AssertionError("bad " + columnMetaData.type.rep); + } + case Types.TIME: + switch (columnMetaData.type.rep) { + case PRIMITIVE_INT: + case INTEGER: + case NUMBER: + return new TimeFromNumberAccessor(getter, localCalendar); + case JAVA_SQL_TIME: + return new TimeAccessor(getter); + default: + throw new AssertionError("bad " + columnMetaData.type.rep); + } + case Types.TIMESTAMP: + switch (columnMetaData.type.rep) { + case PRIMITIVE_LONG: + case LONG: + case NUMBER: + return new TimestampFromNumberAccessor(getter, localCalendar); + case JAVA_SQL_TIMESTAMP: + return new TimestampAccessor(getter); + case JAVA_UTIL_DATE: + return new TimestampFromUtilDateAccessor(getter, localCalendar); + default: + throw new AssertionError("bad " + columnMetaData.type.rep); + } + case 2013: // TIME_WITH_TIMEZONE + switch (columnMetaData.type.rep) { + case STRING: + return new StringAccessor(getter); + default: + throw new AssertionError("bad " + columnMetaData.type.rep); + } + case 2014: // TIMESTAMP_WITH_TIMEZONE + switch (columnMetaData.type.rep) { + case STRING: + return new StringAccessor(getter); + default: + throw new AssertionError("bad " + columnMetaData.type.rep); + } + case Types.ARRAY: + final ColumnMetaData.ArrayType arrayType = + (ColumnMetaData.ArrayType) columnMetaData.type; + final SlotGetter componentGetter = new SlotGetter(); + final Accessor componentAccessor = + createAccessor(ColumnMetaData.dummy(arrayType.getComponent(), true), + componentGetter, localCalendar, factory); + return new ArrayAccessor(getter, arrayType.getComponent(), componentAccessor, + componentGetter, factory); + case Types.STRUCT: + switch (columnMetaData.type.rep) { + case OBJECT: + final ColumnMetaData.StructType structType = + (ColumnMetaData.StructType) columnMetaData.type; + List accessors = new ArrayList<>(); + for (ColumnMetaData column : structType.columns) { + final Getter fieldGetter = + structType.columns.size() == 1 + ? getter + : new StructGetter(getter, column); + accessors.add( + createAccessor(column, fieldGetter, localCalendar, factory)); + } + return new StructAccessor(getter, accessors); + default: + throw new AssertionError("bad " + columnMetaData.type.rep); + } + case Types.JAVA_OBJECT: + case Types.OTHER: // e.g. map + if (columnMetaData.type.getName().startsWith("INTERVAL_")) { + int end = columnMetaData.type.getName().indexOf("("); + if (end < 0) { + end = columnMetaData.type.getName().length(); + } + TimeUnitRange range = + TimeUnitRange.valueOf( + columnMetaData.type.getName().substring("INTERVAL_".length(), end)); + if (range.monthly()) { + return new IntervalYearMonthAccessor(getter, range); + } else { + return new IntervalDayTimeAccessor(getter, range, + columnMetaData.scale); + } + } + return new ObjectAccessor(getter); + default: + throw new RuntimeException("unknown type " + columnMetaData.type.id); + } + } + + protected abstract Getter createGetter(int ordinal); + + public abstract boolean next(); + + /** Accesses a timestamp value as a string. + * The timestamp is in SQL format (e.g. "2013-09-22 22:30:32"), + * not Java format ("2013-09-22 22:30:32.123"). */ + private static String timestampAsString(long v, Calendar calendar) { + if (calendar != null) { + v -= calendar.getTimeZone().getOffset(v); + } + return DateTimeUtils.unixTimestampToString(v); + } + + /** Accesses a date value as a string, e.g. "2013-09-22". */ + private static String dateAsString(int v, Calendar calendar) { + AvaticaUtils.discard(calendar); // time zone shift doesn't make sense + return DateTimeUtils.unixDateToString(v); + } + + /** Accesses a time value as a string, e.g. "22:30:32". */ + private static String timeAsString(int v, Calendar calendar) { + if (calendar != null) { + v -= calendar.getTimeZone().getOffset(v); + } + return DateTimeUtils.unixTimeToString(v); + } + + private static Date longToDate(long v, Calendar calendar) { + if (calendar != null) { + v -= calendar.getTimeZone().getOffset(v); + } + return new Date(v); + } + + static Time intToTime(int v, Calendar calendar) { + if (calendar != null) { + v -= calendar.getTimeZone().getOffset(v); + } + return new Time(v); + } + + static Timestamp longToTimestamp(long v, Calendar calendar) { + if (calendar != null) { + v -= calendar.getTimeZone().getOffset(v); + } + return new Timestamp(v); + } + + /** Implementation of {@link Cursor.Accessor}. */ + static class AccessorImpl implements Accessor { + protected final Getter getter; + + AccessorImpl(Getter getter) { + assert getter != null; + this.getter = getter; + } + + public boolean wasNull() throws SQLException { + return getter.wasNull(); + } + + public String getString() throws SQLException { + final Object o = getObject(); + return o == null ? null : o.toString(); + } + + public boolean getBoolean() throws SQLException { + return getLong() != 0L; + } + + public byte getByte() throws SQLException { + return (byte) getLong(); + } + + public short getShort() throws SQLException { + return (short) getLong(); + } + + public int getInt() throws SQLException { + return (int) getLong(); + } + + public long getLong() throws SQLException { + throw cannotConvert("long"); + } + + public float getFloat() throws SQLException { + return (float) getDouble(); + } + + public double getDouble() throws SQLException { + throw cannotConvert("double"); + } + + public BigDecimal getBigDecimal() throws SQLException { + throw cannotConvert("BigDecimal"); + } + + public BigDecimal getBigDecimal(int scale) throws SQLException { + throw cannotConvert("BigDecimal with scale"); + } + + public byte[] getBytes() throws SQLException { + throw cannotConvert("byte[]"); + } + + public InputStream getAsciiStream() throws SQLException { + throw cannotConvert("InputStream (ascii)"); + } + + public InputStream getUnicodeStream() throws SQLException { + throw cannotConvert("InputStream (unicode)"); + } + + public InputStream getBinaryStream() throws SQLException { + throw cannotConvert("InputStream (binary)"); + } + + public Object getObject() throws SQLException { + return getter.getObject(); + } + + public Reader getCharacterStream() throws SQLException { + throw cannotConvert("Reader"); + } + + private SQLException cannotConvert(String targetType) throws SQLException { + return new SQLDataException("cannot convert to " + targetType + " (" + + this + ")"); + } + + public Object getObject(Map> map) throws SQLException { + throw cannotConvert("Object (with map)"); + } + + public Ref getRef() throws SQLException { + throw cannotConvert("Ref"); + } + + public Blob getBlob() throws SQLException { + throw cannotConvert("Blob"); + } + + public Clob getClob() throws SQLException { + throw cannotConvert("Clob"); + } + + public Array getArray() throws SQLException { + throw cannotConvert("Array"); + } + + public Struct getStruct() throws SQLException { + throw cannotConvert("Struct"); + } + + public Date getDate(Calendar calendar) throws SQLException { + throw cannotConvert("Date"); + } + + public Time getTime(Calendar calendar) throws SQLException { + throw cannotConvert("Time"); + } + + public Timestamp getTimestamp(Calendar calendar) throws SQLException { + throw cannotConvert("Timestamp"); + } + + public URL getURL() throws SQLException { + throw cannotConvert("URL"); + } + + public NClob getNClob() throws SQLException { + throw cannotConvert("NClob"); + } + + public SQLXML getSQLXML() throws SQLException { + throw cannotConvert("SQLXML"); + } + + public String getNString() throws SQLException { + throw cannotConvert("NString"); + } + + public Reader getNCharacterStream() throws SQLException { + throw cannotConvert("NCharacterStream"); + } + + public T getObject(Class type) throws SQLException { + throw cannotConvert("Object (with type)"); + } + } + + /** + * Accessor of exact numeric values. The subclass must implement the + * {@link #getLong()} method. + */ + private abstract static class ExactNumericAccessor extends AccessorImpl { + private ExactNumericAccessor(Getter getter) { + super(getter); + } + + public BigDecimal getBigDecimal(int scale) throws SQLException { + final long v = getLong(); + if (v == 0 && getter.wasNull()) { + return null; + } + return BigDecimal.valueOf(v).setScale(scale, RoundingMode.DOWN); + } + + public BigDecimal getBigDecimal() throws SQLException { + final long val = getLong(); + if (val == 0 && getter.wasNull()) { + return null; + } + return BigDecimal.valueOf(val); + } + + public double getDouble() throws SQLException { + return getLong(); + } + + public float getFloat() throws SQLException { + return getLong(); + } + + public abstract long getLong() throws SQLException; + } + + /** + * Accessor that assumes that the underlying value is a {@link Boolean}; + * corresponds to {@link java.sql.Types#BOOLEAN}. + */ + private static class BooleanAccessor extends ExactNumericAccessor { + private BooleanAccessor(Getter getter) { + super(getter); + } + + public boolean getBoolean() throws SQLException { + Boolean o = (Boolean) getObject(); + return o != null && o; + } + + public long getLong() throws SQLException { + return getBoolean() ? 1 : 0; + } + } + + /** + * Accessor that assumes that the underlying value is a {@link Byte}; + * corresponds to {@link java.sql.Types#TINYINT}. + */ + private static class ByteAccessor extends ExactNumericAccessor { + private ByteAccessor(Getter getter) { + super(getter); + } + + public byte getByte() throws SQLException { + Object obj = getObject(); + if (null == obj) { + return 0; + } else if (obj instanceof Integer) { + return ((Integer) obj).byteValue(); + } + return (Byte) obj; + } + + public long getLong() throws SQLException { + return getByte(); + } + } + + /** + * Accessor that assumes that the underlying value is a {@link Short}; + * corresponds to {@link java.sql.Types#SMALLINT}. + */ + private static class ShortAccessor extends ExactNumericAccessor { + private ShortAccessor(Getter getter) { + super(getter); + } + + public short getShort() throws SQLException { + Object obj = getObject(); + if (null == obj) { + return 0; + } else if (obj instanceof Integer) { + return ((Integer) obj).shortValue(); + } + return (Short) obj; + } + + public long getLong() throws SQLException { + return getShort(); + } + } + + /** + * Accessor that assumes that the underlying value is an {@link Integer}; + * corresponds to {@link java.sql.Types#INTEGER}. + */ + private static class IntAccessor extends ExactNumericAccessor { + private IntAccessor(Getter getter) { + super(getter); + } + + public int getInt() throws SQLException { + Integer o = (Integer) super.getObject(); + return o == null ? 0 : o; + } + + public long getLong() throws SQLException { + return getInt(); + } + } + + /** + * Accessor that assumes that the underlying value is a {@link Long}; + * corresponds to {@link java.sql.Types#BIGINT}. + */ + private static class LongAccessor extends ExactNumericAccessor { + private LongAccessor(Getter getter) { + super(getter); + } + + public long getLong() throws SQLException { + Long o = (Long) super.getObject(); + return o == null ? 0 : o; + } + } + + /** + * Accessor of values that are {@link Double} or null. + */ + private abstract static class ApproximateNumericAccessor + extends AccessorImpl { + private ApproximateNumericAccessor(Getter getter) { + super(getter); + } + + public BigDecimal getBigDecimal(int scale) throws SQLException { + final double v = getDouble(); + if (v == 0d && getter.wasNull()) { + return null; + } + return BigDecimal.valueOf(v).setScale(scale, RoundingMode.DOWN); + } + + public BigDecimal getBigDecimal() throws SQLException { + final double v = getDouble(); + if (v == 0 && getter.wasNull()) { + return null; + } + return BigDecimal.valueOf(v); + } + + public abstract double getDouble() throws SQLException; + + public long getLong() throws SQLException { + return (long) getDouble(); + } + } + + /** + * Accessor that assumes that the underlying value is a {@link Float}; + * corresponds to {@link java.sql.Types#FLOAT}. + */ + private static class FloatAccessor extends ApproximateNumericAccessor { + private FloatAccessor(Getter getter) { + super(getter); + } + + public float getFloat() throws SQLException { + Float o = (Float) getObject(); + return o == null ? 0f : o; + } + + public double getDouble() throws SQLException { + return getFloat(); + } + } + + /** + * Accessor that assumes that the underlying value is a {@link Double}; + * corresponds to {@link java.sql.Types#DOUBLE}. + */ + private static class DoubleAccessor extends ApproximateNumericAccessor { + private DoubleAccessor(Getter getter) { + super(getter); + } + + public double getDouble() throws SQLException { + Object obj = getObject(); + if (null == obj) { + return 0d; + } else if (obj instanceof BigDecimal) { + return ((BigDecimal) obj).doubleValue(); + } + return (Double) obj; + } + } + + /** + * Accessor of exact numeric values. The subclass must implement the + * {@link #getLong()} method. + */ + private abstract static class BigNumberAccessor extends AccessorImpl { + private BigNumberAccessor(Getter getter) { + super(getter); + } + + protected abstract Number getNumber() throws SQLException; + + public double getDouble() throws SQLException { + Number number = getNumber(); + return number == null ? 0d : number.doubleValue(); + } + + public float getFloat() throws SQLException { + Number number = getNumber(); + return number == null ? 0f : number.floatValue(); + } + + public long getLong() throws SQLException { + Number number = getNumber(); + return number == null ? 0L : number.longValue(); + } + + public int getInt() throws SQLException { + Number number = getNumber(); + return number == null ? 0 : number.intValue(); + } + + public short getShort() throws SQLException { + Number number = getNumber(); + return number == null ? 0 : number.shortValue(); + } + + public byte getByte() throws SQLException { + Number number = getNumber(); + return number == null ? 0 : number.byteValue(); + } + + public boolean getBoolean() throws SQLException { + Number number = getNumber(); + return number != null && number.doubleValue() != 0; + } + } + + /** + * Accessor that assumes that the underlying value is a {@link BigDecimal}; + * corresponds to {@link java.sql.Types#DECIMAL}. + */ + private static class BigDecimalAccessor extends BigNumberAccessor { + private BigDecimalAccessor(Getter getter) { + super(getter); + } + + protected Number getNumber() throws SQLException { + return (Number) getObject(); + } + + public BigDecimal getBigDecimal(int scale) throws SQLException { + return (BigDecimal) getObject(); + } + + public BigDecimal getBigDecimal() throws SQLException { + return (BigDecimal) getObject(); + } + } + + /** + * Accessor that assumes that the underlying value is a {@link Number}; + * corresponds to {@link java.sql.Types#NUMERIC}. + * + *

This is useful when numbers have been translated over JSON. JSON + * converts a 0L (0 long) value to the string "0" and back to 0 (0 int). + * So you cannot be sure that the source and target type are the same. + */ + static class NumberAccessor extends BigNumberAccessor { + private final int scale; + + NumberAccessor(Getter getter, int scale) { + super(getter); + this.scale = scale; + } + + protected Number getNumber() throws SQLException { + return (Number) super.getObject(); + } + + public BigDecimal getBigDecimal(int scale) throws SQLException { + Number n = getNumber(); + if (n == null) { + return null; + } + BigDecimal decimal = AvaticaSite.toBigDecimal(n); + if (0 != scale) { + return decimal.setScale(scale, RoundingMode.UNNECESSARY); + } + return decimal; + } + + public BigDecimal getBigDecimal() throws SQLException { + return getBigDecimal(scale); + } + } + + /** + * Accessor that assumes that the underlying value is a {@link String}; + * corresponds to {@link java.sql.Types#CHAR} + * and {@link java.sql.Types#VARCHAR}. + */ + private static class StringAccessor extends AccessorImpl { + private StringAccessor(Getter getter) { + super(getter); + } + + public String getString() throws SQLException { + final Object obj = getObject(); + if (obj instanceof String) { + return (String) obj; + } + return null == obj ? null : obj.toString(); + } + + @Override public byte[] getBytes() throws SQLException { + return super.getBytes(); + } + } + + /** + * Accessor that assumes that the underlying value is a {@link String}; + * corresponds to {@link java.sql.Types#CHAR}. + */ + private static class FixedStringAccessor extends StringAccessor { + protected final Spacer spacer; + + private FixedStringAccessor(Getter getter, int length) { + super(getter); + this.spacer = new Spacer(length); + } + + public String getString() throws SQLException { + String s = super.getString(); + if (s == null) { + return null; + } + return spacer.padRight(s); + } + } + + /** + * Accessor that assumes that the underlying value is a {@link String}; + * corresponds to {@link java.sql.Types#CHAR}. + */ + private static class StringFromCharAccessor extends FixedStringAccessor { + private StringFromCharAccessor(Getter getter, int length) { + super(getter, length); + } + + public String getString() throws SQLException { + Character s = (Character) super.getObject(); + if (s == null) { + return null; + } + return spacer.padRight(s.toString()); + } + } + + /** + * Accessor that assumes that the underlying value is an array of + * {@link org.apache.calcite.avatica.util.ByteString} values; + * corresponds to {@link java.sql.Types#BINARY} + * and {@link java.sql.Types#VARBINARY}. + */ + private static class BinaryAccessor extends AccessorImpl { + private BinaryAccessor(Getter getter) { + super(getter); + } + + //FIXME: Protobuf gets byte[] + @Override public byte[] getBytes() throws SQLException { + Object obj = getObject(); + if (null == obj) { + return null; + } + if (obj instanceof ByteString) { + return ((ByteString) obj).getBytes(); + } else if (obj instanceof String) { + // Need to unwind the base64 for JSON + return ByteString.parseBase64((String) obj); + } else if (obj instanceof byte[]) { + // Protobuf would have a byte array + return (byte[]) obj; + } else { + throw new RuntimeException("Cannot handle " + obj.getClass() + " as bytes"); + } + } + + @Override public String getString() throws SQLException { + Object o = getObject(); + if (null == o) { + return null; + } + if (o instanceof byte[]) { + return new String((byte[]) o, StandardCharsets.UTF_8); + } else if (o instanceof ByteString) { + return ((ByteString) o).toString(); + } + throw new IllegalStateException("Unhandled value type: " + o.getClass()); + } + + + @Override public Blob getBlob() throws SQLException { + + Object o = getObject(); + if (null == o) { + return null; + } + if (o instanceof byte[]) { + byte[] byteArr = (byte[] )o; + //System.out.println(new String(byteArr, StandardCharsets.UTF_8)); + return new SerialBlob(byteArr); + } + + throw new IllegalStateException("Unhandled value type: " + o.getClass()); + + } + } + + /** + * Accessor that assumes that the underlying value is a {@link String}, + * encoding {@link java.sql.Types#BINARY} + * and {@link java.sql.Types#VARBINARY} values in Base64 format. + */ + private static class BinaryFromStringAccessor extends StringAccessor { + private BinaryFromStringAccessor(Getter getter) { + super(getter); + } + + @Override public Object getObject() throws SQLException { + return super.getObject(); + } + + @Override public byte[] getBytes() throws SQLException { + // JSON sends this as a base64-enc string, protobuf can do binary. + Object obj = getObject(); + + if (obj instanceof byte[]) { + // If we already have bytes, just send them back. + return (byte[]) obj; + } + + return getBase64Decoded(); + } + + private byte[] getBase64Decoded() throws SQLException { + final String string = super.getString(); + if (null == string) { + return null; + } + // Need to base64 decode the string. + return ByteString.parseBase64(string); + } + + @Override public String getString() throws SQLException { + final byte[] bytes = getBase64Decoded(); + if (null == bytes) { + return null; + } + // Need to base64 decode the string. + return new String(bytes, StandardCharsets.UTF_8); + } + } + + /** + * Accessor that assumes that the underlying value is a DATE, + * in its default representation {@code int}; + * corresponds to {@link java.sql.Types#DATE}. + */ + private static class DateFromNumberAccessor extends NumberAccessor { + private final Calendar localCalendar; + + private DateFromNumberAccessor(Getter getter, Calendar localCalendar) { + super(getter, 0); + this.localCalendar = localCalendar; + } + + @Override public Object getObject() throws SQLException { + return getDate(localCalendar); + } + + @Override public Date getDate(Calendar calendar) throws SQLException { + final Number v = getNumber(); + if (v == null) { + return null; + } + return longToDate(v.longValue() * DateTimeUtils.MILLIS_PER_DAY, calendar); + } + + @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { + final Number v = getNumber(); + if (v == null) { + return null; + } + return longToTimestamp(v.longValue() * DateTimeUtils.MILLIS_PER_DAY, + calendar); + } + + @Override public String getString() throws SQLException { + final Number v = getNumber(); + if (v == null) { + return null; + } + return dateAsString(v.intValue(), null); + } + } + + /** + * Accessor that assumes that the underlying value is a Time, + * in its default representation {@code int}; + * corresponds to {@link java.sql.Types#TIME}. + */ + private static class TimeFromNumberAccessor extends NumberAccessor { + private final Calendar localCalendar; + + private TimeFromNumberAccessor(Getter getter, Calendar localCalendar) { + super(getter, 0); + this.localCalendar = localCalendar; + } + + @Override public Object getObject() throws SQLException { + return getTime(localCalendar); + } + + @Override public Time getTime(Calendar calendar) throws SQLException { + final Number v = getNumber(); + if (v == null) { + return null; + } + return intToTime(v.intValue(), calendar); + } + + @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { + final Number v = getNumber(); + if (v == null) { + return null; + } + return longToTimestamp(v.longValue(), calendar); + } + + @Override public String getString() throws SQLException { + final Number v = getNumber(); + if (v == null) { + return null; + } + return timeAsString(v.intValue(), null); + } + } + + /** + * Accessor that assumes that the underlying value is a TIMESTAMP, + * in its default representation {@code long}; + * corresponds to {@link java.sql.Types#TIMESTAMP}. + */ + private static class TimestampFromNumberAccessor extends NumberAccessor { + private final Calendar localCalendar; + + private TimestampFromNumberAccessor(Getter getter, Calendar localCalendar) { + super(getter, 0); + this.localCalendar = localCalendar; + } + + @Override public Object getObject() throws SQLException { + return getTimestamp(localCalendar); + } + + @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { + final Number v = getNumber(); + if (v == null) { + return null; + } + return longToTimestamp(v.longValue(), calendar); + } + + @Override public Date getDate(Calendar calendar) throws SQLException { + final Timestamp timestamp = getTimestamp(calendar); + if (timestamp == null) { + return null; + } + return new Date(timestamp.getTime()); + } + + @Override public Time getTime(Calendar calendar) throws SQLException { + final Timestamp timestamp = getTimestamp(calendar); + if (timestamp == null) { + return null; + } + return new Time( + DateTimeUtils.floorMod(timestamp.getTime(), + DateTimeUtils.MILLIS_PER_DAY)); + } + + @Override public String getString() throws SQLException { + final Number v = getNumber(); + if (v == null) { + return null; + } + return timestampAsString(v.longValue(), null); + } + } + + /** + * Accessor that assumes that the underlying value is a DATE, + * represented as a java.sql.Date; + * corresponds to {@link java.sql.Types#DATE}. + */ + private static class DateAccessor extends ObjectAccessor { + private DateAccessor(Getter getter) { + super(getter); + } + + @Override public Date getDate(Calendar calendar) throws SQLException { + java.sql.Date date = (Date) getObject(); + if (date == null) { + return null; + } + if (calendar != null) { + long v = date.getTime(); + v -= calendar.getTimeZone().getOffset(v); + date = new Date(v); + } + return date; + } + + @Override public String getString() throws SQLException { + final int v = getInt(); + if (v == 0 && wasNull()) { + return null; + } + return dateAsString(v, null); + } + + @Override public long getLong() throws SQLException { + Date date = getDate(null); + return date == null + ? 0L + : (date.getTime() / DateTimeUtils.MILLIS_PER_DAY); + } + } + + /** + * Accessor that assumes that the underlying value is a TIME, + * represented as a java.sql.Time; + * corresponds to {@link java.sql.Types#TIME}. + */ + private static class TimeAccessor extends ObjectAccessor { + private TimeAccessor(Getter getter) { + super(getter); + } + + @Override public Time getTime(Calendar calendar) throws SQLException { + Time date = (Time) getObject(); + if (date == null) { + return null; + } + if (calendar != null) { + long v = date.getTime(); + v -= calendar.getTimeZone().getOffset(v); + date = new Time(v); + } + return date; + } + + @Override public String getString() throws SQLException { + final int v = getInt(); + if (v == 0 && wasNull()) { + return null; + } + return timeAsString(v, null); + } + + @Override public long getLong() throws SQLException { + Time time = getTime(null); + return time == null ? 0L + : (time.getTime() % DateTimeUtils.MILLIS_PER_DAY); + } + } + + /** + * Accessor that assumes that the underlying value is a TIMESTAMP, + * represented as a java.sql.Timestamp; + * corresponds to {@link java.sql.Types#TIMESTAMP}. + */ + private static class TimestampAccessor extends ObjectAccessor { + private TimestampAccessor(Getter getter) { + super(getter); + } + + @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { + Timestamp timestamp = (Timestamp) getObject(); + if (timestamp == null) { + return null; + } + if (calendar != null) { + long v = timestamp.getTime(); + v -= calendar.getTimeZone().getOffset(v); + timestamp = new Timestamp(v); + } + return timestamp; + } + + @Override public Date getDate(Calendar calendar) throws SQLException { + final Timestamp timestamp = getTimestamp(calendar); + if (timestamp == null) { + return null; + } + return new Date(timestamp.getTime()); + } + + @Override public Time getTime(Calendar calendar) throws SQLException { + final Timestamp timestamp = getTimestamp(calendar); + if (timestamp == null) { + return null; + } + return new Time( + DateTimeUtils.floorMod(timestamp.getTime(), + DateTimeUtils.MILLIS_PER_DAY)); + } + + @Override public String getString() throws SQLException { + final long v = getLong(); + if (v == 0 && wasNull()) { + return null; + } + return timestampAsString(v, null); + } + + @Override public long getLong() throws SQLException { + Timestamp timestamp = getTimestamp(null); + return timestamp == null ? 0 : timestamp.getTime(); + } + } + + /** + * Accessor that assumes that the underlying value is a TIMESTAMP, + * represented as a java.util.Date; + * corresponds to {@link java.sql.Types#TIMESTAMP}. + */ + private static class TimestampFromUtilDateAccessor extends ObjectAccessor { + private final Calendar localCalendar; + + private TimestampFromUtilDateAccessor(Getter getter, + Calendar localCalendar) { + super(getter); + this.localCalendar = localCalendar; + } + + @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { + java.util.Date date = (java.util.Date) getObject(); + if (date == null) { + return null; + } + long v = date.getTime(); + if (calendar != null) { + v -= calendar.getTimeZone().getOffset(v); + } + return new Timestamp(v); + } + + @Override public Date getDate(Calendar calendar) throws SQLException { + final Timestamp timestamp = getTimestamp(calendar); + if (timestamp == null) { + return null; + } + return new Date(timestamp.getTime()); + } + + @Override public Time getTime(Calendar calendar) throws SQLException { + final Timestamp timestamp = getTimestamp(calendar); + if (timestamp == null) { + return null; + } + return new Time( + DateTimeUtils.floorMod(timestamp.getTime(), + DateTimeUtils.MILLIS_PER_DAY)); + } + + @Override public String getString() throws SQLException { + java.util.Date date = (java.util.Date) getObject(); + if (date == null) { + return null; + } + return timestampAsString(date.getTime(), null); + } + + @Override public long getLong() throws SQLException { + Timestamp timestamp = getTimestamp(localCalendar); + return timestamp == null ? 0 : timestamp.getTime(); + } + } + + /** + * Accessor that assumes that the underlying value is a {@code int}; + * corresponds to {@link java.sql.Types#OTHER}. + */ + private static class IntervalYearMonthAccessor extends IntAccessor { + private final TimeUnitRange range; + + private IntervalYearMonthAccessor(Getter getter, TimeUnitRange range) { + super(getter); + this.range = range; + } + + @Override public String getString() throws SQLException { + final int v = getInt(); + if (v == 0 && wasNull()) { + return null; + } + return DateTimeUtils.intervalYearMonthToString(v, range); + } + } + + /** + * Accessor that assumes that the underlying value is a {@code long}; + * corresponds to {@link java.sql.Types#OTHER}. + */ + private static class IntervalDayTimeAccessor extends LongAccessor { + private final TimeUnitRange range; + private final int scale; + + private IntervalDayTimeAccessor(Getter getter, TimeUnitRange range, + int scale) { + super(getter); + this.range = range; + this.scale = scale; + } + + @Override public String getString() throws SQLException { + final long v = getLong(); + if (v == 0 && wasNull()) { + return null; + } + return DateTimeUtils.intervalDayTimeToString(v, range, scale); + } + } + + /** + * Accessor that assumes that the underlying value is an ARRAY; + * corresponds to {@link java.sql.Types#ARRAY}. + */ + public static class ArrayAccessor extends AccessorImpl { + final ColumnMetaData.AvaticaType componentType; + final Accessor componentAccessor; + final SlotGetter componentSlotGetter; + final ArrayImpl.Factory factory; + + public ArrayAccessor(Getter getter, + ColumnMetaData.AvaticaType componentType, Accessor componentAccessor, + SlotGetter componentSlotGetter, ArrayImpl.Factory factory) { + super(getter); + this.componentType = componentType; + this.componentAccessor = componentAccessor; + this.componentSlotGetter = componentSlotGetter; + this.factory = factory; + } + + @Override public Object getObject() throws SQLException { + final Object object = super.getObject(); + if (object == null || object instanceof ArrayImpl) { + return object; + } else if (object instanceof List) { + List list = (List) object; + // Run the array values through the component accessor + List convertedValues = new ArrayList<>(list.size()); + for (Object val : list) { + if (null == val) { + convertedValues.add(null); + } else { + // Set the current value in the SlotGetter so we can use the Accessor to coerce it. + componentSlotGetter.slot = val; + convertedValues.add(convertValue()); + } + } + return convertedValues; + } + // The object can be java array in case of user-provided class for row storage. + return AvaticaUtils.primitiveList(object); + } + + private Object convertValue() throws SQLException { + switch (componentType.id) { + case Types.BOOLEAN: + case Types.BIT: + return componentAccessor.getBoolean(); + case Types.TINYINT: + return componentAccessor.getByte(); + case Types.SMALLINT: + return componentAccessor.getShort(); + case Types.INTEGER: + return componentAccessor.getInt(); + case Types.BIGINT: + return componentAccessor.getLong(); + case Types.FLOAT: + return componentAccessor.getFloat(); + case Types.DOUBLE: + return componentAccessor.getDouble(); + case Types.ARRAY: + return componentAccessor.getArray(); + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.NCHAR: + case Types.LONGNVARCHAR: + return componentAccessor.getString(); + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + return componentAccessor.getBytes(); + case Types.DECIMAL: + return componentAccessor.getBigDecimal(); + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + case Types.STRUCT: + case Types.JAVA_OBJECT: + return componentAccessor.getObject(); + default: + throw new IllegalStateException("Unhandled ARRAY component type: " + componentType.rep + + ", id: " + componentType.id); + } + } + + @SuppressWarnings("unchecked") @Override public Array getArray() throws SQLException { + final Object o = getObject(); + if (o == null) { + return null; + } + if (o instanceof ArrayImpl) { + return (ArrayImpl) o; + } + // If it's not an Array already, assume it is a List. + return new ArrayImpl((List) o, this); + } + + @Override public String getString() throws SQLException { + final Array array = getArray(); + return array == null ? null : array.toString(); + } + } + + /** + * Accessor that assumes that the underlying value is a STRUCT; + * corresponds to {@link java.sql.Types#STRUCT}. + */ + private static class StructAccessor extends AccessorImpl { + private final List fieldAccessors; + + private StructAccessor(Getter getter, List fieldAccessors) { + super(getter); + this.fieldAccessors = fieldAccessors; + } + + @Override public Object getObject() throws SQLException { + return getStruct(); + } + + @SuppressWarnings("unchecked") + @Override public T getObject(Class clz) throws SQLException { + // getStruct() is not exposed on Accessor, only AccessorImpl. getObject(Class) is exposed, + // so we can make it do the right thing (call getStruct()). + if (clz.equals(Struct.class)) { + return (T) getStruct(); + } + return super.getObject(clz); + } + + @Override public Struct getStruct() throws SQLException { + final Object o = super.getObject(); + if (o == null) { + return null; + } else if (o instanceof StructImpl) { + return (StructImpl) o; + } else if (o instanceof List) { + return new StructImpl((List) o); + } else { + final List list = new ArrayList<>(); + for (Accessor fieldAccessor : fieldAccessors) { + list.add(fieldAccessor.getObject()); + } + return new StructImpl(list); + } + } + } + + /** + * Accessor that assumes that the underlying value is an OBJECT; + * corresponds to {@link java.sql.Types#JAVA_OBJECT}. + */ + private static class ObjectAccessor extends AccessorImpl { + private ObjectAccessor(Getter getter) { + super(getter); + } + } + + /** Gets a value from a particular field of the current record of this + * cursor. */ + protected interface Getter { + Object getObject() throws SQLException; + + boolean wasNull() throws SQLException; + } + + /** Abstract implementation of {@link Getter}. */ + protected abstract class AbstractGetter implements Getter { + public boolean wasNull() throws SQLException { + return wasNull[0]; + } + } + + /** Implementation of {@link Getter} that returns the current contents of + * a mutable slot. */ + public class SlotGetter implements Getter { + public Object slot; + + public Object getObject() throws SQLException { + return slot; + } + + public boolean wasNull() throws SQLException { + return slot == null; + } + } + + /** Implementation of {@link Getter} that returns the value of a given field + * of the current contents of another getter. */ + public class StructGetter implements Getter { + public final Getter getter; + private final ColumnMetaData columnMetaData; + + public StructGetter(Getter getter, ColumnMetaData columnMetaData) { + this.getter = getter; + this.columnMetaData = columnMetaData; + } + + public Object getObject() throws SQLException { + try { + final Object o = getter.getObject(); + if (o instanceof Object[]) { + Object[] objects = (Object[]) o; + return objects[columnMetaData.ordinal]; + } + final Field field = o.getClass().getField(columnMetaData.label); + return field.get(getter.getObject()); + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new SQLException(e); + } + } + + public boolean wasNull() throws SQLException { + return getObject() == null; + } + } +} + +// End AbstractCursor.java diff --git a/mdbc-server/src/main/java/org/onap/music/logging/EELFLoggerDelegate.java b/mdbc-server/src/main/java/org/onap/music/logging/EELFLoggerDelegate.java index d8b5256..2bd26ff 100755 --- a/mdbc-server/src/main/java/org/onap/music/logging/EELFLoggerDelegate.java +++ b/mdbc-server/src/main/java/org/onap/music/logging/EELFLoggerDelegate.java @@ -7,9 +7,9 @@ * Licensed 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 - * + * + * 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. @@ -19,34 +19,42 @@ */ package org.onap.music.logging; +import static com.att.eelf.configuration.Configuration.MDC_ALERT_SEVERITY; +import static com.att.eelf.configuration.Configuration.MDC_INSTANCE_UUID; +import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID; import static com.att.eelf.configuration.Configuration.MDC_SERVER_FQDN; import static com.att.eelf.configuration.Configuration.MDC_SERVER_IP_ADDRESS; import static com.att.eelf.configuration.Configuration.MDC_SERVICE_INSTANCE_ID; import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME; - +import java.io.IOException; +import java.io.InputStream; import java.net.InetAddress; import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; - import javax.servlet.http.HttpServletRequest; - import org.slf4j.MDC; - import com.att.eelf.configuration.EELFLogger; import com.att.eelf.configuration.EELFManager; import com.att.eelf.configuration.SLF4jWrapper; public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { + public static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); public static final EELFLogger applicationLogger = EELFManager.getInstance().getApplicationLogger(); public static final EELFLogger auditLogger = EELFManager.getInstance().getAuditLogger(); public static final EELFLogger metricsLogger = EELFManager.getInstance().getMetricsLogger(); public static final EELFLogger debugLogger = EELFManager.getInstance().getDebugLogger(); + // DateTime Format according to the ECOMP Application Logging Guidelines. + private static final SimpleDateFormat ecompLogDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + private String className; - private static ConcurrentMap classMap = new ConcurrentHashMap<>(); + private static ConcurrentMap classMap = new ConcurrentHashMap(); public EELFLoggerDelegate(final String className) { super(className); @@ -92,7 +100,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { */ public void trace(EELFLogger logger, String msg) { if (logger.isTraceEnabled()) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.trace(msg); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } } @@ -105,7 +115,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { */ public void trace(EELFLogger logger, String msg, Object... arguments) { if (logger.isTraceEnabled()) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.trace(msg, arguments); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } } @@ -118,7 +130,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { */ public void trace(EELFLogger logger, String msg, Throwable th) { if (logger.isTraceEnabled()) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.trace(msg, th); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } } @@ -130,7 +144,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { */ public void debug(EELFLogger logger, String msg) { if (logger.isDebugEnabled()) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.debug(msg); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } } @@ -143,7 +159,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { */ public void debug(EELFLogger logger, String msg, Object... arguments) { if (logger.isDebugEnabled()) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.debug(msg, arguments); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } } @@ -156,7 +174,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { */ public void debug(EELFLogger logger, String msg, Throwable th) { if (logger.isDebugEnabled()) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.debug(msg, th); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } } @@ -167,7 +187,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param msg */ public void info(EELFLogger logger, String msg) { - logger.info(className + " - "+msg); + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); + logger.info(msg); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } /** @@ -178,7 +200,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param arguments */ public void info(EELFLogger logger, String msg, Object... arguments) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.info(msg, arguments); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } /** @@ -189,7 +213,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param th */ public void info(EELFLogger logger, String msg, Throwable th) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.info(msg, th); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } /** @@ -199,7 +225,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param msg */ public void warn(EELFLogger logger, String msg) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.warn(msg); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } /** @@ -210,7 +238,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param arguments */ public void warn(EELFLogger logger, String msg, Object... arguments) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.warn(msg, arguments); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } /** @@ -221,7 +251,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param th */ public void warn(EELFLogger logger, String msg, Throwable th) { + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); logger.warn(msg, th); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } /** @@ -231,7 +263,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param msg */ public void error(EELFLogger logger, String msg) { - logger.error(className+ " - " + msg); + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); + logger.error(msg); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } /** @@ -242,7 +276,9 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param arguments */ public void error(EELFLogger logger, String msg, Object... arguments) { - logger.error(msg, arguments); + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); + logger.warn(msg, arguments); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } /** @@ -253,19 +289,11 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param th */ public void error(EELFLogger logger, String msg, Throwable th) { - logger.error(msg, th); + MDC.put(LoggerProperties.MDC_CLASS_NAME, className); + logger.warn(msg, th); + MDC.remove(LoggerProperties.MDC_CLASS_NAME); } - /** - * Logs a message with the associated alarm severity at error level. - * - * @param logger - * @param msg - * @param severtiy - */ - public void error(EELFLogger logger, String msg, Object /*AlarmSeverityEnum*/ severtiy) { - logger.error(msg); - } /** * Initializes the logger context. @@ -277,11 +305,30 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { info(applicationLogger, msg); error(errorLogger, msg); debug(debugLogger, msg); - info(auditLogger, msg); + // Audit and metrics logger must be told start AND stop times + final String currentDateTime = getCurrentDateTimeUTC(); + // Set the MDC with audit properties + MDC.put(LoggerProperties.AUDITLOG_BEGIN_TIMESTAMP, currentDateTime); + MDC.put(LoggerProperties.AUDITLOG_END_TIMESTAMP, currentDateTime); + info(auditLogger, msg); + MDC.remove(LoggerProperties.AUDITLOG_BEGIN_TIMESTAMP); + MDC.remove(LoggerProperties.AUDITLOG_END_TIMESTAMP); + // Set the MDC with metrics properties + MDC.put(LoggerProperties.METRICSLOG_BEGIN_TIMESTAMP, currentDateTime); + MDC.put(LoggerProperties.METRICSLOG_END_TIMESTAMP, currentDateTime); info(metricsLogger, msg); + MDC.remove(LoggerProperties.METRICSLOG_BEGIN_TIMESTAMP); + MDC.remove(LoggerProperties.METRICSLOG_END_TIMESTAMP); + } + + + public static String getCurrentDateTimeUTC() { + String currentDateTime = ecompLogDateFormat.format(new Date()); + return currentDateTime; } + /** * Builds a message using a template string and the arguments. * @@ -289,7 +336,6 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param args * @return */ - @SuppressWarnings("unused") private String formatMessage(String message, Object... args) { StringBuilder sbFormattedMessage = new StringBuilder(); if (args != null && args.length > 0 && message != null && message != "") { @@ -307,9 +353,11 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { */ private void setGlobalLoggingContext() { MDC.put(MDC_SERVICE_INSTANCE_ID, ""); + MDC.put(MDC_ALERT_SEVERITY, AlarmSeverityEnum.INFORMATIONAL.toString()); try { MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getHostName()); MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress()); + MDC.put(MDC_INSTANCE_UUID, LoggerProperties.getProperty(LoggerProperties.INSTANCE_UUID)); } catch (Exception e) { errorLogger.error("setGlobalLoggingContext failed", e); } @@ -335,23 +383,185 @@ public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { * @param req * @param appName */ - public void setRequestBasedDefaultsIntoGlobalLoggingContext(HttpServletRequest req, String appName) { + public void setRequestBasedDefaultsIntoGlobalLoggingContext(HttpServletRequest req, String appName,String reqId,String loginId) {// Load the default fields // Load the default fields - setGlobalLoggingContext(); + setGlobalLoggingContext(); + + // Load the request based fields + if (req != null) { + // Load the Request into MDC context. + + MDC.put(MDC_KEY_REQUEST_ID, reqId); + + // Load user agent into MDC context, if available. + String accessingClient = req.getHeader(LoggerProperties.USERAGENT_NAME); + if (accessingClient != null && !"".equals(accessingClient) && (accessingClient.contains("Mozilla") + || accessingClient.contains("Chrome") || accessingClient.contains("Safari"))) { + accessingClient = appName + "_FE"; + } + MDC.put(LoggerProperties.PARTNER_NAME, accessingClient); + + // Protocol, Rest URL & Rest Path + MDC.put(LoggerProperties.FULL_URL, LoggerProperties.UNKNOWN); + MDC.put(LoggerProperties.PROTOCOL, LoggerProperties.HTTP); + String restURL = getFullURL(req); + if (restURL != null && restURL != "") { + MDC.put(LoggerProperties.FULL_URL, restURL); + if (restURL.toLowerCase().contains("https")) { + MDC.put(LoggerProperties.PROTOCOL, LoggerProperties.HTTPS); + } + } + + // Rest Path + MDC.put(MDC_SERVICE_NAME, req.getServletPath()); + + // Client IPAddress i.e. IPAddress of the remote host who is making + // this request. + String clientIPAddress = req.getHeader("X-FORWARDED-FOR"); + if (clientIPAddress == null) { + clientIPAddress = req.getRemoteAddr(); + } + MDC.put(LoggerProperties.CLIENT_IP_ADDRESS, clientIPAddress); + + // Load loginId into MDC context. + MDC.put(LoggerProperties.MDC_LOGIN_ID, "Unknown"); + + + + if (loginId != null && loginId != "") { + MDC.put(LoggerProperties.MDC_LOGIN_ID, loginId); + } + } + } + + + + + public static String getFullURL(HttpServletRequest request) { + if (request != null) { + StringBuffer requestURL = request.getRequestURL(); + String queryString = request.getQueryString(); + + if (queryString == null) { + return requestURL.toString(); + } else { + return requestURL.append('?').append(queryString).toString(); + } + } + return ""; + } + + - // Load the request based fields - if (req != null) { +} +enum AlarmSeverityEnum { + CRITICAL("1"), + MAJOR("2"), + MINOR("3"), + INFORMATIONAL("4"), + NONE("0"); - // Rest Path - MDC.put(MDC_SERVICE_NAME, req.getServletPath()); + private final String severity; - // Client IPAddress i.e. IPAddress of the remote host who is making - // this request. - String clientIPAddress = req.getHeader("X-FORWARDED-FOR"); - if (clientIPAddress == null) { - clientIPAddress = req.getRemoteAddr(); - } - } - } + AlarmSeverityEnum(String severity) { + this.severity = severity; + } + + public String severity() { + return severity; + } +} + +class LoggerProperties { + + +public static final String MDC_APPNAME = "AppName"; +public static final String MDC_REST_PATH = "RestPath"; +public static final String MDC_REST_METHOD = "RestMethod"; +public static final String INSTANCE_UUID = "instance_uuid"; +public static final String MDC_CLASS_NAME = "class"; +public static final String MDC_LOGIN_ID = "LoginId"; +public static final String MDC_TIMER = "Timer"; +public static final String PARTNER_NAME = "PartnerName"; +public static final String FULL_URL = "Full-URL"; +public static final String AUDITLOG_BEGIN_TIMESTAMP = "AuditLogBeginTimestamp"; +public static final String AUDITLOG_END_TIMESTAMP = "AuditLogEndTimestamp"; +public static final String METRICSLOG_BEGIN_TIMESTAMP = "MetricsLogBeginTimestamp"; +public static final String METRICSLOG_END_TIMESTAMP = "MetricsLogEndTimestamp"; +public static final String CLIENT_IP_ADDRESS = "ClientIPAddress"; +public static final String STATUS_CODE = "StatusCode"; +public static final String RESPONSE_CODE = "ResponseCode"; + +public static final String HTTP = "HTTP"; +public static final String HTTPS = "HTTPS"; +public static final String UNKNOWN = "Unknown"; +public static final String PROTOCOL = "PROTOCOL"; +public static final String USERAGENT_NAME = "user-agent"; +public static final String USER_ATTRIBUTE_NAME = "user_attribute_name"; + + +private LoggerProperties(){} + +private static Properties properties; + +private static String propertyFileName = "logger.properties"; + +private static final Object lockObject = new Object(); + +//private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(LoggerProperties.class); + +/** + * Gets the property value for the specified key. If a value is found, leading + * and trailing space is trimmed. + * + * @param property + * Property key + * @return Value for the named property; null if the property file was not + * loaded or the key was not found. + */ +public static String getProperty(String property) { + if (properties == null) { + synchronized (lockObject) { + try { + if (!initialize()) { +// logger.error(EELFLoggerDelegate.errorLogger, "Failed to read property file " + propertyFileName); + return null; + } + } catch (IOException e) { +// logger.error(EELFLoggerDelegate.errorLogger, "Failed to read property file " + propertyFileName ,e); + return null; + } + } + } + String value = properties.getProperty(property); + if (value != null) + value = value.trim(); + return value; +} + +/** + * Reads properties from a portal.properties file on the classpath. + * + * Clients do NOT need to call this method. Clients MAY call this method to test + * whether the properties file can be loaded successfully. + * + * @return True if properties were successfully loaded, else false. + * @throws IOException + * On failure + */ +private static boolean initialize() throws IOException { + if (properties != null) + return true; + InputStream in = LoggerProperties.class.getClassLoader().getResourceAsStream(propertyFileName); + if (in == null) + return false; + properties = new Properties(); + try { + properties.load(in); + } finally { + in.close(); + } + return true; } +} \ No newline at end of file diff --git a/mdbc-server/src/main/resources/logback.xml b/mdbc-server/src/main/resources/logback.xml index af973b8..b9e9dcf 100755 --- a/mdbc-server/src/main/resources/logback.xml +++ b/mdbc-server/src/main/resources/logback.xml @@ -45,18 +45,18 @@ --> + + - - + - - - + + @@ -73,20 +73,20 @@ + value="%X{AuditLogBeginTimestamp}|%X{AuditLogEndTimestamp}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{PartnerName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{Timer}|%X{ServerFQDN}|%X{ClientIPAddress}|%X{class}|%X{Unused}|%X{ProcessKey}|%X{CustomField1}|%X{CustomField2}|%X{CustomField3}|%X{CustomField4}| %msg%n" /> + value="%X{MetricsLogBeginTimestamp}|%X{MetricsLogEndTimestamp}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{PartnerName}|%X{TargetEntity}|%X{TargetServiceName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{Timer}|%X{ServerFQDN}|%X{ClientIPAddress}|%X{class}|%X{Unused}|%X{ProcessKey}|%X{TargetVisualEntity}|%X{CustomField1}|%X{CustomField2}|%X{CustomField3}|%X{CustomField4}| %msg%n" /> + value="%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%X{RequestId}|%thread|%X{ServiceName}|%X{PartnerName}|%X{TargetEntity}|%X{TargetServiceName}|%X{class}|%X{AlertSeverity}|%X{ErrorCode}|%X{ErrorDescription}| %msg%n" /> + value="%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%X{RequestId}|%thread|%X{class}| %msg%n" /> + value="%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%X{RequestId}|%thread|%X{class}| %msg%n" /> @@ -321,7 +321,7 @@ - ${debugLogDirectory}/${debugLogName}.log + ${logDirectory}/${debugLogName}.log ${logDirectory}/${debugLogName}.%d{yyyy-MM-dd}.log.zip @@ -344,28 +344,29 @@ - + - - + + - - + + - - - + + + - - - - + + + + + - + \ No newline at end of file -- 2.16.6