diff --git a/core/src/main/java/pt/webdetails/cda/cache/CacheKey.java b/core/src/main/java/pt/webdetails/cda/cache/CacheKey.java index 8f4c9b09af..3a729384f3 100644 --- a/core/src/main/java/pt/webdetails/cda/cache/CacheKey.java +++ b/core/src/main/java/pt/webdetails/cda/cache/CacheKey.java @@ -18,12 +18,13 @@ import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; +import java.util.Objects; public class CacheKey implements Serializable { private static final long serialVersionUID = -1273843592305584696L; - private ArrayList keyValuePairs = new ArrayList(); + private ArrayList keyValuePairs = new ArrayList<>(); public CacheKey() { } @@ -34,7 +35,7 @@ public CacheKey( String key, String value ) { public ArrayList getKeyValuePairs() { if ( keyValuePairs == null ) { - keyValuePairs = new ArrayList(); + keyValuePairs = new ArrayList<>(); } return keyValuePairs; } @@ -47,11 +48,7 @@ public void addKeyValuePair( String key, String value ) { public void removeByKey( String key ) { if ( !StringUtils.isEmpty( key ) ) { - for ( KeyValuePair pair : getKeyValuePairs() ) { - if ( key.equals( pair.getKey() ) ) { - getKeyValuePairs().remove( pair ); - } - } + getKeyValuePairs().removeIf( pair -> key.equals( pair.getKey() ) ); } } @@ -77,11 +74,7 @@ public boolean equals( final Object o ) { final CacheKey other = (CacheKey) o; - if ( keyValuePairs != null ? !keyValuePairs.equals( other.keyValuePairs ) : other.keyValuePairs != null ) { - return false; - } - - return true; + return Objects.equals( keyValuePairs, other.keyValuePairs ); } @Override @@ -155,15 +148,7 @@ public boolean equals( final Object o ) { final KeyValuePair other = (KeyValuePair) o; - if ( this.key == null ? other.key != null : !this.key.equals( other.key ) ) { - return false; - } - - if ( this.value == null ? other.value != null : !this.value.equals( other.value ) ) { - return false; - } - - return true; + return Objects.equals( this.key, other.key ) && Objects.equals( this.value, other.value ); } @Override @@ -180,22 +165,22 @@ public String toString() { + "]\n"; } - /* - * Serializable classes that require special handling during the serialization and deserialization process should - * implement the following methods: writeObject(java.io.ObjectOutputStream stream), - * readObject(java.io.ObjectInputStream stream) - * - * @see http://docs.oracle.com/javase/6/docs/api/java/io/ObjectInputStream.html - */ + /* + * Serializable classes that require special handling during the serialization and deserialization process should + * implement the following methods: writeObject(java.io.ObjectOutputStream stream), + * readObject(java.io.ObjectInputStream stream) + * + * @see http://docs.oracle.com/javase/6/docs/api/java/io/ObjectInputStream.html + */ private void readObject( java.io.ObjectInputStream in ) throws IOException, ClassNotFoundException { - key = (String) in.readObject(); - value = (String) in.readObject(); + key = in.readUTF(); + value = in.readUTF(); } private void writeObject( java.io.ObjectOutputStream out ) throws IOException { - out.writeObject( key ); - out.writeObject( value ); + out.writeUTF( key ); + out.writeUTF( value ); } } } diff --git a/core/src/main/java/pt/webdetails/cda/cache/EHCacheQueryCache.java b/core/src/main/java/pt/webdetails/cda/cache/EHCacheQueryCache.java index 981b4ff32f..0e919eadfa 100644 --- a/core/src/main/java/pt/webdetails/cda/cache/EHCacheQueryCache.java +++ b/core/src/main/java/pt/webdetails/cda/cache/EHCacheQueryCache.java @@ -67,7 +67,11 @@ public ExtraCacheInfo getInfo() { } private void writeObject( ObjectOutputStream out ) throws IOException { - out.writeObject( table ); + if ( table instanceof Serializable ) { + out.writeObject( table ); + } else { + throw new IOException("Cannot call writeObject on TableModel (object is not serializable)"); + } out.writeObject( info ); } @@ -112,7 +116,12 @@ protected static synchronized Cache getCacheFromManager( final boolean switchCla } } - if ( cacheManager.cacheExists( CACHE_NAME ) == false ) { + if ( cacheManager == null ) { + logger.error( "Cache manager is null" ); + return null; + } + + if ( !cacheManager.cacheExists( CACHE_NAME ) ) { cacheManager.addCache( CACHE_NAME ); } @@ -171,7 +180,7 @@ public TableModel getTableModel( TableCacheKey key ) { Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() ); final Element element = cache.get( key ); if ( element != null ) { - final TableModel cachedTableModel = (TableModel) ( (CacheElement) element.getObjectValue() ).getTable(); + final TableModel cachedTableModel = ( (CacheElement) element.getObjectValue() ).getTable(); if ( cachedTableModel != null ) { if ( logger.isDebugEnabled() ) { // we have a entry in the cache ... great! diff --git a/core/src/main/java/pt/webdetails/cda/cache/TableCacheKey.java b/core/src/main/java/pt/webdetails/cda/cache/TableCacheKey.java index e58e4909b2..ecd05f988f 100644 --- a/core/src/main/java/pt/webdetails/cda/cache/TableCacheKey.java +++ b/core/src/main/java/pt/webdetails/cda/cache/TableCacheKey.java @@ -22,14 +22,17 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputFilter; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Objects; public class TableCacheKey implements Serializable { @@ -40,7 +43,7 @@ public class TableCacheKey implements Serializable { private String queryType; private Parameter[] parameters; - private Serializable extraCacheKey; + private CacheKey extraCacheKey; /** * For serialization @@ -50,7 +53,7 @@ protected TableCacheKey() { public TableCacheKey( final Connection connection, final String query, - final List parameters, final Serializable extraCacheKey ) { + final List parameters, final CacheKey extraCacheKey ) { if ( connection == null ) { throw new NullPointerException(); } @@ -69,7 +72,7 @@ public TableCacheKey( final Connection connection, final String query, } public TableCacheKey( final Connection connection, final String query, final String queryType, - final List parameters, final Serializable extraCacheKey ) { + final List parameters, final CacheKey extraCacheKey ) { if ( connection == null ) { throw new NullPointerException(); } @@ -123,7 +126,6 @@ public Parameter[] getParameters() { return parameters; } - public void setParameterDataRow( ParameterDataRow parameterDataRow ) { //this.parameterDataRow = parameterDataRow; this.parameters = createParametersFromParameterDataRow( parameterDataRow ); @@ -135,7 +137,7 @@ public Object getExtraCacheKey() { } - public void setExtraCacheKey( Serializable extraCacheKey ) { + public void setExtraCacheKey( CacheKey extraCacheKey ) { this.extraCacheKey = extraCacheKey; } @@ -147,9 +149,9 @@ private void readObject( java.io.ObjectInputStream in ) throws IOException, Clas //connection connectionHash = in.readInt(); //query - query = (String) in.readObject(); + query = in.readUTF(); //queryTpe - queryType = (String) in.readObject(); + queryType = in.readUTF(); int len = in.readInt(); Parameter[] params = new Parameter[ len ]; @@ -160,7 +162,7 @@ private void readObject( java.io.ObjectInputStream in ) throws IOException, Clas params[ i ] = param; } parameters = params; - extraCacheKey = (Serializable) in.readObject(); + extraCacheKey = (CacheKey) in.readObject(); } //Hazelcast will use serialized version to perform comparisons and hashcodes @@ -169,8 +171,8 @@ private void writeObject( java.io.ObjectOutputStream out ) throws IOException { //binary comparison/hash will be used out.writeInt( connectionHash ); - out.writeObject( query ); - out.writeObject( queryType ); + out.writeUTF( query ); + out.writeUTF( queryType ); out.writeInt( parameters.length ); for ( Parameter param : parameters ) { @@ -184,12 +186,11 @@ private void writeObject( java.io.ObjectOutputStream out ) throws IOException { * Serialize as printable String. */ public static String getTableCacheKeyAsString( TableCacheKey cacheKey ) - throws IOException, UnsupportedEncodingException { + throws IOException { ByteArrayOutputStream keyStream = new ByteArrayOutputStream(); ObjectOutputStream objStream = new ObjectOutputStream( keyStream ); cacheKey.writeObject( objStream ); - String identifier = new String( Base64.encodeBase64( keyStream.toByteArray() ), "UTF-8" ); - return identifier; + return new String( Base64.encodeBase64( keyStream.toByteArray() ), StandardCharsets.UTF_8 ); } /** @@ -198,12 +199,34 @@ public static String getTableCacheKeyAsString( TableCacheKey cacheKey ) public static TableCacheKey getTableCacheKeyFromString( String encodedCacheKey ) throws IOException, ClassNotFoundException { ByteArrayInputStream keyStream = new ByteArrayInputStream( Base64.decodeBase64( encodedCacheKey.getBytes() ) ); - ObjectInputStream objStream = new ObjectInputStream( keyStream ); + + // We add this filter to allow only the specific classes that will be read further ahead, + // and block everything else from readObject. This is important because readObject can allow injection attacks. + ObjectInputFilter paramFilter = ObjectInputFilter.Config.createFilter( + "pt.webdetails.cda.dataaccess.Parameter;" + + "pt.webdetails.cda.dataaccess.Parameter$Type;" + + "pt.webdetails.cda.cache.CacheKey;" + + "javax.swing.table.TableModel;" + + "pt.webdetails.cda.cache.monitor.ExtraCacheInfo;" + + "pt.webdetails.cda.dataaccess.MdxDataAccess$BANDED_MODE;" + + "!*" + ); + ObjectInputStream objStream = new FilteredObjectInputStream( keyStream, paramFilter ); TableCacheKey cacheKey = new TableCacheKey(); cacheKey.readObject( objStream ); return cacheKey; } + // We set the filter in this custom class, because it cannot be applied after read operations have already happened, + // which was the case when using the normal constructor and applying the filter after class instantiation. + private static class FilteredObjectInputStream extends ObjectInputStream { + FilteredObjectInputStream( InputStream in, ObjectInputFilter filter ) throws IOException { + super( in ); + setObjectInputFilter( filter ); + } + } + + public boolean equals( final Object o ) { if ( this == o ) { return true; @@ -220,14 +243,10 @@ public boolean equals( final Object o ) { if ( parameters != null ? !Arrays.equals( parameters, that.parameters ) : that.parameters != null ) { return false; } - if ( query != null ? !query.equals( that.query ) : that.query != null ) { - return false; - } - if ( extraCacheKey != null ? !extraCacheKey.equals( that.extraCacheKey ) : that.extraCacheKey != null ) { + if ( !Objects.equals( query, that.query ) ) { return false; } - - return true; + return Objects.equals( extraCacheKey, that.extraCacheKey ); } @@ -264,11 +283,11 @@ public int compare( Parameter o1, Parameter o2 ) { * for serialization */ private static Parameter[] createParametersFromParameterDataRow( final ParameterDataRow row ) { - ArrayList parameters = new ArrayList(); + ArrayList parameters = new ArrayList<>(); if ( row != null ) { for ( String name : row.getColumnNames() ) { Object value = row.get( name ); - Parameter param = new Parameter( name, value != null ? value : null ); + Parameter param = new Parameter( name, value ); Parameter.Type type = Parameter.Type.inferTypeFromObject( value ); param.setType( type ); parameters.add( param ); diff --git a/core/src/main/java/pt/webdetails/cda/cache/monitor/ExtraCacheInfo.java b/core/src/main/java/pt/webdetails/cda/cache/monitor/ExtraCacheInfo.java index 5a37643233..36f65ec5c4 100644 --- a/core/src/main/java/pt/webdetails/cda/cache/monitor/ExtraCacheInfo.java +++ b/core/src/main/java/pt/webdetails/cda/cache/monitor/ExtraCacheInfo.java @@ -116,25 +116,25 @@ public void setTimeToLive( int timeToLive ) { } private void writeObject( ObjectOutputStream out ) throws IOException { - out.writeObject( cdaSettingsId ); - out.writeObject( dataAccessId ); + out.writeUTF( cdaSettingsId ); + out.writeUTF( dataAccessId ); out.writeLong( queryDurationMs ); out.writeInt( nbrRows ); out.writeLong( entryTime ); out.writeInt( timeToLive ); - out.writeObject( tableSnapshot != null ? tableSnapshot.toString() : null ); + out.writeUTF( tableSnapshot != null ? tableSnapshot.toString() : null ); } private void readObject( ObjectInputStream in ) throws IOException, ClassNotFoundException { - cdaSettingsId = (String) in.readObject(); - dataAccessId = (String) in.readObject(); + cdaSettingsId = in.readUTF(); + dataAccessId = in.readUTF(); queryDurationMs = in.readLong(); nbrRows = in.readInt(); entryTime = in.readLong(); timeToLive = in.readInt(); try { - tableSnapshot = new JSONObject( (String) in.readObject() ); + tableSnapshot = new JSONObject( in.readUTF() ); } catch ( Exception e ) { tableSnapshot = null; } diff --git a/core/src/main/java/pt/webdetails/cda/dataaccess/AbstractDataAccess.java b/core/src/main/java/pt/webdetails/cda/dataaccess/AbstractDataAccess.java index 13a8bdc298..8eae6b9841 100644 --- a/core/src/main/java/pt/webdetails/cda/dataaccess/AbstractDataAccess.java +++ b/core/src/main/java/pt/webdetails/cda/dataaccess/AbstractDataAccess.java @@ -36,7 +36,6 @@ import pt.webdetails.cda.utils.kettle.SortException; import javax.swing.table.TableModel; -import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -70,6 +69,9 @@ public abstract class AbstractDataAccess implements DataAccess { private static final String PARAM_ITERATOR_END = ")"; private static final String PARAM_ITERATOR_ARG_SEPARATOR = ","; private static final String EXTRA_CACHE_KEYS_PROPERTY = "pt.webdetails.cda.cache.extraCacheKeys"; + private static final String CACHE_DURATION_ATTRIBUTE = "cacheDuration"; + private static final String CACHE_ATTRIBUTE = "cache"; + private static final String ACCESS_ATTRIBUTE = "access"; protected AbstractDataAccess() { } @@ -77,12 +79,12 @@ protected AbstractDataAccess() { protected AbstractDataAccess( final Element element ) { name = ""; - columnDefinitionIndexMap = new HashMap(); - columnDefinitions = new ArrayList(); - parameters = new ArrayList(); - outputs = new HashMap>(); - outputs.put( 1, new ArrayList() ); - outputMode = new HashMap(); + columnDefinitionIndexMap = new HashMap<>(); + columnDefinitions = new ArrayList<>(); + parameters = new ArrayList<>(); + outputs = new HashMap<>(); + outputs.put( 1, new ArrayList<>() ); + outputMode = new HashMap<>(); outputMode.put( 1, OutputMode.INCLUDE ); parseOptions( element ); @@ -98,12 +100,12 @@ protected AbstractDataAccess( String id, String name ) { this.name = name; this.id = id; - columnDefinitionIndexMap = new HashMap(); - columnDefinitions = new ArrayList(); - parameters = new ArrayList(); - outputs = new HashMap>(); - outputs.put( 1, new ArrayList() ); - outputMode = new HashMap(); + columnDefinitionIndexMap = new HashMap<>(); + columnDefinitions = new ArrayList<>(); + parameters = new ArrayList<>(); + outputs = new HashMap<>(); + outputs.put( 1, new ArrayList<>() ); + outputMode = new HashMap<>(); outputMode.put( 1, OutputMode.INCLUDE ); } @@ -126,41 +128,48 @@ private void parseOptions( final Element element ) { name = nameElement.getTextTrim(); } - if ( element.attributeValue( "access" ) != null && element.attributeValue( "access" ).equals( "private" ) ) { + if ( element.attributeValue( ACCESS_ATTRIBUTE ) != null && element.attributeValue( ACCESS_ATTRIBUTE ) + .equals( "private" ) ) { access = DataAccessEnums.ACCESS_TYPE.PRIVATE; } - if ( element.attributeValue( "cache" ) != null && element.attributeValue( "cache" ).equals( "true" ) ) { + if ( element.attributeValue( CACHE_ATTRIBUTE ) != null && element.attributeValue( CACHE_ATTRIBUTE ) + .equals( "true" ) ) { cacheEnabled = true; } - if ( element.attribute( "cacheDuration" ) != null && !element.attribute( "cacheDuration" ).toString() - .equals( "" ) ) { - cacheDuration = Integer.parseInt( element.attributeValue( "cacheDuration" ) ); + if ( element.attribute( CACHE_DURATION_ATTRIBUTE ) != null && !element.attribute( CACHE_DURATION_ATTRIBUTE ) + .toString().isEmpty() ) { + cacheDuration = Integer.parseInt( element.attributeValue( CACHE_DURATION_ATTRIBUTE ) ); } + parseParameters( element, parameters ); + parseOutputs( element ); + parseColumns( element ); + buildColumnDefinitionIndexMap(); + parseCdaCacheKey( element ); + } - // Parse parameters + private void parseParameters( final Element element, ArrayList parameters ) { final List parameterNodes = Util.selectElements( element, "Parameters/Parameter" ); for ( final Element p : parameterNodes ) { parameters.add( new Parameter( p ) ); } + } - // Parse outputs + private void parseOutputs( final Element element ) { final List outputNodes = Util.selectElements( element, "Output" ); for ( final Element outputNode : outputNodes ) { - ArrayList myOutputs = new ArrayList(); + ArrayList myOutputs = new ArrayList<>(); if ( outputNode != null ) { int localId = 1; - if ( outputNode.attribute( "id" ) != null && !outputNode.attribute( "id" ).toString().equals( "" ) ) { + if ( outputNode.attribute( "id" ) != null && !outputNode.attribute( "id" ).toString().isEmpty() ) { // if parseInt fails an exception will be thrown and the cda file will not be accepted localId = Integer.parseInt( outputNode.attributeValue( "id" ) ); - } else { - // if an output has not a defined or empty id then it will have key = 1 - localId = 1; } + // if an output has not a defined or empty id then it will have key = 1 try { outputMode.put( localId, OutputMode.valueOf( outputNode.attributeValue( "mode" ).toUpperCase() ) ); @@ -176,21 +185,25 @@ private void parseOptions( final Element element ) { outputs.put( localId, myOutputs ); } } + } - // Parse Columns + private void parseColumns( Element element ) { final List columnNodes = Util.selectElements( element, "Columns/*" ); for ( final Element p : columnNodes ) { columnDefinitions.add( new ColumnDefinition( p ) ); } + } + private void buildColumnDefinitionIndexMap() { // Build the columnDefinitionIndexMap final ArrayList cols = getColumns(); for ( final ColumnDefinition columnDefinition : cols ) { columnDefinitionIndexMap.put( columnDefinition.getIndex(), columnDefinition ); } + } - // parse cda cache key + private void parseCdaCacheKey( Element element ) { final Element cdaCache = (Element) element.selectSingleNode( "Cache" ); if ( cdaCache != null ) { cdaCacheParser = new DataAccessCacheElementParser( cdaCache ); @@ -232,7 +245,9 @@ public static synchronized void shutdownCache() { public static synchronized void clearCache() { IQueryCache cache = getCdaCache(); - cache.clearCache(); + if ( cache != null ) { + cache.clearCache(); + } } @@ -249,7 +264,7 @@ public TableModel doQuery( final QueryOptions queryOptions ) throws QueryExcepti * 1. Sort * 2. Show only the output columns * 3. Paginate - * + * * */ @@ -281,7 +296,7 @@ public TableModel listParameters() { public ArrayList getColumns() { - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); for ( final ColumnDefinition definition : columnDefinitions ) { if ( definition.getType() == ColumnDefinition.TYPE.COLUMN ) { @@ -295,7 +310,7 @@ public ArrayList getColumns() { public ColumnDefinition getColumnDefinition( final int idx ) { - return columnDefinitionIndexMap.get( new Integer( idx ) ); + return columnDefinitionIndexMap.get( idx ); } @@ -409,7 +424,7 @@ public List getDataAccessConnectionDescriptors() public List getInterface() { - ArrayList properties = new ArrayList(); + ArrayList properties = new ArrayList<>(); properties .add( new PropertyDescriptor( "id", PropertyDescriptor.Type.STRING, PropertyDescriptor.Placement.ATTRIB ) ); properties @@ -566,10 +581,10 @@ private TableModel doQueryOnIterableParameters( QueryOptions queryOptions, Map> iterableParameters ) throws QueryException { //all iterators need to have at least one value.. - List names = new ArrayList(); - List> iterators = new ArrayList>(); - List> iterables = new ArrayList>(); - List values = new ArrayList(); + List names = new ArrayList<>(); + List> iterators = new ArrayList<>(); + List> iterables = new ArrayList<>(); + List values = new ArrayList<>(); TableModel result = null; try { @@ -666,21 +681,21 @@ public ArrayList getColumnDefinitions() { return columnDefinitions; } - public Serializable getCacheKey() { + public CacheKey getCacheKey() { String cacheKeyInfo = "Getting Cache Key for file: " + this.getCdaSettings().getId() + ", DataAccessID: " + this.getId() + "\n"; CacheKey systemWideCacheKey = getSystemCacheKeys(); if ( cdaCacheParser != null ) { if ( cdaCacheParser.parseKeys() ) { CacheKey mergedCacheKey = mergeCacheKeys( cdaCacheParser.getCacheKey(), systemWideCacheKey ); - if ( mergedCacheKey.getKeyValuePairs().size() > 0 ) { + if ( !mergedCacheKey.getKeyValuePairs().isEmpty() ) { cacheKeyInfo += mergedCacheKey.toString(); } logger.info( cacheKeyInfo ); return mergedCacheKey; } } - if ( systemWideCacheKey.getKeyValuePairs().size() > 0 ) { + if ( !systemWideCacheKey.getKeyValuePairs().isEmpty() ) { cacheKeyInfo += systemWideCacheKey.toString(); } logger.info( cacheKeyInfo ); @@ -720,7 +735,7 @@ private CacheKey mergeCacheKeys( CacheKey cacheKey1, CacheKey cacheKey2 ) { * * @return */ - protected Serializable getExtraCacheKey() { + protected CacheKey getExtraCacheKey() { return getCacheKey(); } } diff --git a/core/src/main/java/pt/webdetails/cda/dataaccess/DenormalizedMdxDataAccess.java b/core/src/main/java/pt/webdetails/cda/dataaccess/DenormalizedMdxDataAccess.java index d245be76e9..15c17316b7 100644 --- a/core/src/main/java/pt/webdetails/cda/dataaccess/DenormalizedMdxDataAccess.java +++ b/core/src/main/java/pt/webdetails/cda/dataaccess/DenormalizedMdxDataAccess.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.Serializable; +import java.util.Objects; /** * Implementation of a DataAccess that will get data from a SQL database @@ -39,6 +40,7 @@ public DenormalizedMdxDataAccess( final Element element ) { public DenormalizedMdxDataAccess() { } + @Override protected AbstractNamedMDXDataFactory createDataFactory() { return new ExtDenormalizedMDXDataFactory(); } @@ -48,7 +50,7 @@ public String getType() { } @Override - protected Serializable getExtraCacheKey() { //TODO: is this necessary after role assembly in EvaluableConnection + protected CacheKey getExtraCacheKey() { // .evaluate()? MondrianConnectionInfo mci; try { @@ -58,10 +60,13 @@ protected Serializable getExtraCacheKey() { //TODO: is this necessary after role mci = null; } - CacheKey cacheKey = getCacheKey() != null ? ( (CacheKey) getCacheKey() ).clone() : new CacheKey(); - - cacheKey.addKeyValuePair( "roles", mci.getMondrianRole() ); + CacheKey cacheKey = getCacheKey() != null ? ( getCacheKey() ).clone() : new CacheKey(); + if ( mci != null) { + cacheKey.addKeyValuePair( "roles", mci.getMondrianRole() ); + } else { + logger.warn( "No Mondrian connection info was found for cache key, roles were not included." ); + } return cacheKey; } @@ -85,20 +90,17 @@ public boolean equals( Object obj ) { } final ExtraCacheKey other = (ExtraCacheKey) obj; - if ( this.roles == null ? other.roles != null : !this.roles.equals( other.roles ) ) { - return false; - } - return true; + return Objects.equals( this.roles, other.roles ); } private void readObject( java.io.ObjectInputStream in ) throws IOException, ClassNotFoundException { - this.roles = (String) in.readObject(); + this.roles = in.readUTF(); } private void writeObject( java.io.ObjectOutputStream out ) throws IOException { - out.writeObject( this.roles ); + out.writeUTF( this.roles ); } diff --git a/core/src/main/java/pt/webdetails/cda/dataaccess/KettleDataAccess.java b/core/src/main/java/pt/webdetails/cda/dataaccess/KettleDataAccess.java index 166f31127f..3e84ac3c80 100644 --- a/core/src/main/java/pt/webdetails/cda/dataaccess/KettleDataAccess.java +++ b/core/src/main/java/pt/webdetails/cda/dataaccess/KettleDataAccess.java @@ -32,7 +32,6 @@ import pt.webdetails.cda.settings.UnknownConnectionException; import pt.webdetails.cda.utils.ParameterArrayToStringEncoder; -import java.io.Serializable; import java.util.List; public class KettleDataAccess extends PREDataAccess { @@ -88,9 +87,9 @@ public void setCdaSettings( CdaSettings cdaSettings ) { * paths, only the path needs to be stored. */ @Override - public Serializable getExtraCacheKey() { + public CacheKey getExtraCacheKey() { - CacheKey cacheKey = getCacheKey() != null ? ( (CacheKey) getCacheKey() ).clone() : new CacheKey(); + CacheKey cacheKey = getCacheKey() != null ? ( getCacheKey() ).clone() : new CacheKey(); cacheKey.addKeyValuePair( "path", path ); @@ -116,7 +115,7 @@ private String getQuoteCharacter() { @Override protected IDataSourceQuery performRawQuery( ParameterDataRow parameterDataRow ) throws QueryException { - if ( getParameters().size() == 0 ) { + if ( getParameters().isEmpty() ) { return super.performRawQuery( parameterDataRow ); } diff --git a/core/src/main/java/pt/webdetails/cda/dataaccess/MdxDataAccess.java b/core/src/main/java/pt/webdetails/cda/dataaccess/MdxDataAccess.java index c518c80f2f..a6c001322c 100644 --- a/core/src/main/java/pt/webdetails/cda/dataaccess/MdxDataAccess.java +++ b/core/src/main/java/pt/webdetails/cda/dataaccess/MdxDataAccess.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.Serializable; import java.util.List; +import java.util.Objects; /** * Implementation of a DataAccess that will get data from a SQL database @@ -116,7 +117,7 @@ public List getInterface() { @Override - protected Serializable getExtraCacheKey() { //TODO: is this necessary after role assembly in EvaluableConnection + protected CacheKey getExtraCacheKey() { // .evaluate()? MondrianConnectionInfo mci; try { @@ -126,7 +127,7 @@ protected Serializable getExtraCacheKey() { //TODO: is this necessary after role mci = null; } - CacheKey cacheKey = getCacheKey() != null ? ( (CacheKey) getCacheKey() ).clone() : new CacheKey(); + CacheKey cacheKey = getCacheKey() != null ? ( getCacheKey() ).clone() : new CacheKey(); cacheKey.addKeyValuePair( "bandedMode", bandedMode.toString() ); if ( mci != null ) { @@ -159,25 +160,19 @@ public boolean equals( Object obj ) { return false; } final ExtraCacheKey other = (ExtraCacheKey) obj; - if ( this.bandedMode != other.bandedMode && ( this.bandedMode == null || !this.bandedMode - .equals( other.bandedMode ) ) ) { - return false; - } else if ( this.roles == null ? other.roles != null : !this.roles.equals( other.roles ) ) { - return false; - } - return true; + return Objects.equals( this.bandedMode, other.bandedMode ) && Objects.equals( this.roles, other.roles ); } private void readObject( java.io.ObjectInputStream in ) throws IOException, ClassNotFoundException { this.bandedMode = (BANDED_MODE) in.readObject(); - this.roles = (String) in.readObject(); + this.roles = in.readUTF(); } private void writeObject( java.io.ObjectOutputStream out ) throws IOException { out.writeObject( this.bandedMode ); - out.writeObject( this.roles ); + out.writeUTF( this.roles ); } diff --git a/core/src/main/java/pt/webdetails/cda/dataaccess/Parameter.java b/core/src/main/java/pt/webdetails/cda/dataaccess/Parameter.java index 1b71efad91..f700f87603 100644 --- a/core/src/main/java/pt/webdetails/cda/dataaccess/Parameter.java +++ b/core/src/main/java/pt/webdetails/cda/dataaccess/Parameter.java @@ -111,8 +111,8 @@ public static ParameterDataRow createParameterDataRowFromParameters( final List< public static ParameterDataRow createParameterDataRowFromParameters( final Parameter[] parameters ) throws InvalidParameterException { - final ArrayList names = new ArrayList(); - final ArrayList values = new ArrayList(); + final ArrayList names = new ArrayList<>(); + final ArrayList values = new ArrayList<>(); if ( parameters != null ) { for ( final Parameter parameter : parameters ) { @@ -121,10 +121,7 @@ public static ParameterDataRow createParameterDataRowFromParameters( final Param } } - final ParameterDataRow parameterDataRow = - new ParameterDataRow( names.toArray( new String[] {} ), values.toArray() ); - - return parameterDataRow; + return new ParameterDataRow( names.toArray( new String[] {} ), values.toArray() ); } public void inheritDefaults( Parameter defaultParameter ) { @@ -161,33 +158,36 @@ public Object getValue() throws InvalidParameterException { */ } - if ( objValue instanceof String ) { //may be a string or a parsed value - final String strValue = (String) objValue; - //check if it is a formula - if ( strValue != null && strValue.trim().startsWith( FORMULA_BEGIN ) ) { - String formula = Util.getContentsBetween( strValue, FORMULA_BEGIN, FORMULA_END ); - if ( formula == null ) { - throw new InvalidParameterException( "Malformed formula expression", null ); - } - Object value = FormulaEvaluator.processFormula( formula ); - if ( getType() == Type.STRING && !( value instanceof String ) ) { - return getValueAsString( value ); - } else { - return value; - } - } - - Type valueType = getType(); - if ( valueType == null ) { - throw new InvalidParameterException( "Parameter type " + getType() + " unknown, can't continue", null ); - } - value = getValueFromString( strValue, valueType ); - return value; + if ( objValue instanceof String ) { + return handleStringValue( (String) objValue ); //may be a string or a parsed value } else { return objValue; } } + private Object handleStringValue( String strValue ) throws InvalidParameterException { + //check if it is a formula + if ( strValue.trim().startsWith( FORMULA_BEGIN ) ) { + String formula = Util.getContentsBetween( strValue, FORMULA_BEGIN, FORMULA_END ); + if ( formula == null ) { + throw new InvalidParameterException( "Malformed formula expression", null ); + } + Object evaluatedValue = FormulaEvaluator.processFormula( formula ); + if ( getType() == Type.STRING && !( evaluatedValue instanceof String ) ) { + return getValueAsString( evaluatedValue ); + } else { + return evaluatedValue; + } + } + + Type valueType = getType(); + if ( valueType == null ) { + throw new InvalidParameterException( "Parameter type " + getType() + " unknown, can't continue", null ); + } + value = getValueFromString( strValue, valueType ); + return value; + } + public void setValue( final Object value ) { this.value = value; } @@ -200,7 +200,7 @@ public void setValue( final Object value ) { */ private Object getValueFromString( final String localValue, Type valueType ) throws InvalidParameterException { - switch( valueType ) { + switch ( valueType ) { case STRING: return localValue; case INTEGER: @@ -236,7 +236,7 @@ private Object getValueFromString( final String localValue, Type valueType ) thr private T[] parseToArray( String arrayAsString, Type elementType, T[] array ) throws InvalidParameterException { CSVTokenizer tokenizer = new CSVTokenizer( arrayAsString, getSeparator() ); - ArrayList result = new ArrayList(); + ArrayList result = new ArrayList<>(); while ( tokenizer.hasMoreTokens() ) { result.add( (T) getValueFromString( tokenizer.nextToken(), elementType ) ); } @@ -284,9 +284,7 @@ public void setPattern( final String pattern ) { } private String getValueAsString( Object value ) { - String separator = getSeparator(); - - ParameterArrayToStringEncoder encoder = new ParameterArrayToStringEncoder( separator, getQuoteCharacter() ); + ParameterArrayToStringEncoder encoder = new ParameterArrayToStringEncoder( getSeparator(), getQuoteCharacter() ); if ( value == null ) { if ( getDefaultValue() != null ) { return getDefaultValue().toString(); @@ -296,7 +294,7 @@ private String getValueAsString( Object value ) { } else if ( value instanceof String ) { return (String) value; } else if ( type != null ) { - switch( type ) { + switch ( type ) { case STRING_ARRAY://csvTokenizer compatible return encoder.encodeParameterArray( value, type ); case DATE: @@ -404,12 +402,13 @@ public int hashCode() { public void readObject( ObjectInputStream in ) throws IOException { try { - this.setName( (String) in.readObject() ); + this.setName( in.readUTF() ); this.setType( (Type) in.readObject() ); - //if(isDateType()) this.setPattern((String) in.readObject()); - this.setStringValue( (String) in.readObject(), this.getType() ); - this.setSeparator( (String) in.readObject() ); - this.setQuoteCharacter( (String) in.readObject() ); + + this.setStringValue( in.readUTF(), this.getType() ); + + this.setSeparator( in.readUTF() ); + this.setQuoteCharacter( in.readUTF() ); } catch ( ClassNotFoundException e ) { throw new IOException( "Error casting read object.", e ); } @@ -419,12 +418,11 @@ public void readObject( ObjectInputStream in ) throws IOException { * Should only be called on evaluated parameters */ public void writeObject( ObjectOutputStream out ) throws IOException { - out.writeObject( this.getName() ); + out.writeUTF( this.getName() ); out.writeObject( this.getType() ); - //if(isDateType()) out.writeObject(this.pattern); - out.writeObject( this.getStringValue() ); - out.writeObject( this.getSeparator() ); - out.writeObject( this.getQuoteCharacter() ); + out.writeUTF( this.getStringValue() ); + out.writeUTF( this.getSeparator() ); + out.writeUTF( this.getQuoteCharacter() ); } @@ -451,6 +449,7 @@ public static Access parse( String text ) { return PUBLIC; //default } + @Override public String toString() { return this.name; } @@ -516,7 +515,7 @@ public String toString() { } public boolean isArrayType() { - switch( this ) { + switch ( this ) { case STRING_ARRAY: case INTEGER_ARRAY: case NUMERIC_ARRAY: diff --git a/core/src/main/java/pt/webdetails/cda/dataaccess/SimpleDataAccess.java b/core/src/main/java/pt/webdetails/cda/dataaccess/SimpleDataAccess.java index d98d8331f8..eca2d85f0c 100644 --- a/core/src/main/java/pt/webdetails/cda/dataaccess/SimpleDataAccess.java +++ b/core/src/main/java/pt/webdetails/cda/dataaccess/SimpleDataAccess.java @@ -190,7 +190,7 @@ public List getFilledParameters( final QueryOptions queryOptions ) th // Get parameters from definition and apply their values //TODO: use queryOptions' parameters instead of copying? - final List parameters = new ArrayList( getParameters().size() ); + final List parameters = new ArrayList<>( getParameters().size() ); for ( Parameter param : getParameters() ) { parameters.add( new Parameter( param ) ); } @@ -253,7 +253,7 @@ private long logIfDurationAboveThreshold( final long beginTime, final String que //log query and duration String logMsg = "Query " + queryId + " took " + duration + "s.\n"; logMsg += "\t Query contents: << " + query.trim() + " >>\n"; - if ( parameters.size() > 0 ) { + if ( !parameters.isEmpty() ) { logMsg += "\t Parameters: \n"; for ( Parameter parameter : parameters ) { logMsg += "\t\t" + parameter.toString() + "\n"; @@ -272,7 +272,7 @@ protected TableModel postProcessTableModel( TableModel tm ) { /** * Logs that the query is starting. - * + *

* Used for correlating query and parameters with other MDC logging information. * * @param queryOptions The query options. @@ -282,20 +282,20 @@ public void logQueryStart( final QueryOptions queryOptions, final List 0 ) { + .map( String::trim ) + .collect( Collectors.joining( "%n\t\t" ) ); + if ( !queryText.isEmpty() ) { logMsgBuilder.append( String.format( "%n\t Query:%n\t\t===%n\t\t%s%n\t\t===", queryText ) ); } } @@ -332,7 +332,6 @@ public static interface IDataSourceQuery { public void closeDataSource() throws QueryException; } - protected abstract IDataSourceQuery performRawQuery( ParameterDataRow parameterDataRow ) throws QueryException; @@ -382,7 +381,7 @@ private static int getQueryTimeThresholdFromConfig( int defaultValue ) { public void accept( DomVisitor xmlVisitor, Element root ) { - xmlVisitor.visit( (SimpleDataAccess) this, root ); + xmlVisitor.visit( this, root ); } @@ -396,18 +395,19 @@ public void setQueryType( String queryType ) { /** * Parses the text content of an xml node defensively. - * @param element base node - * @param nodePath path from {@code element} - * @param parse convert string to value + * + * @param element base node + * @param nodePath path from {@code element} + * @param parse convert string to value * @param defaultValue default value in case of not found, empty or error * @return */ protected T parseNode( Element element, String nodePath, Function parse, T defaultValue ) { try { return Optional.ofNullable( element.selectSingleNode( nodePath ) ) - .map( Node::getText ) - .map( parse ) - .orElse( defaultValue ); + .map( Node::getText ) + .map( parse ) + .orElse( defaultValue ); } catch ( Exception e ) { logger.error( " parse error in " + this.getName() + ": " + e.getMessage() ); return defaultValue;