/* Copyright 2014 MITRE Corporation * * 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 * * 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.mitre.provenance.plusobject; import java.sql.SQLException; import java.util.ArrayList; import java.util.Map; import java.util.logging.Logger; import org.mitre.provenance.PLUSException; import org.mitre.provenance.PropertySet; /** * A PLUSObject that is attached to a relational database. * @author moxious */ public class PLUSRelational extends PLUSDataObject { protected static Logger log = Logger.getLogger(PLUSRelational.class.getName()); /** Database connect string */ protected String dbconnector; /** Table name identifier */ protected String tableName; /** Name of a key field */ protected String keyFieldName; /** The value of the key field which is indicated */ protected String keyFieldValue; /** SQL statement, which when evaluated, results in the data this object refers to */ protected String resultOfSQL; protected ArrayList <String> columnNames; protected ArrayList <String> redacted; public static final String PLUS_SUBTYPE_RELATIONAL = "relational"; public PLUSRelational() { super(); setKeyFieldName("N/A"); setKeyFieldValue("N/A"); setDBConnector("unknown"); setSQL("none"); setObjectSubtype("relational"); redacted = new ArrayList <String> (); } public ArrayList <String> getColumnNames() { return columnNames; } public ArrayList <String> getRedactedList() { return redacted; } /** * Relational objects have the ability to mark particular attributes as "redacted". Subclasses get to decide * what to do with this "redacted" information, but it should be used to remove fields from output reported * to users. So for example by setting the column "foo" to redacted, when calling methods to access the * object's underlying data, foo data will not be returned. * @param attribute the name of the attribute whose redaction status you want to change. * @param redactedStatus true if the attribute should be redacted, false if you want it removed from the redaction list. */ public void setRedacted(String attribute, boolean redactedStatus) { if(redacted == null) redacted = new ArrayList <String> (); if(redactedStatus) redacted.add(attribute.toLowerCase()); else { int idx = redacted.indexOf(attribute.toLowerCase()); if(idx == -1) return; else redacted.remove(idx); } } // End setRedacted /** * Determine whether a particular attribute is redacted or not. * @param attribute the name of the attribute * @return true if it should be redacted, false otherwise. */ public boolean isRedacted(String attribute) { if(redacted == null) redacted = new ArrayList <String> (); return redacted.contains(attribute.toLowerCase()); } /** * Relational objects tend to require a lot of data following them around, and have high latency. * Call this method when you need to populate the object. Subclasses of PLUSRelational should override * this method, since all this one does is throw an exception. * * @throws SQLException */ public void populate() throws SQLException { throw new SQLException("MUST OVERRIDE populate()!"); } public String getDBConnector() { return dbconnector; } public String getTableName() { if(tableName == null && getSQL() != null) { String sql = getSQL(); log.info("PLUSRelational: trying to figure out table name."); try { String marker = " from "; int idx = sql.toLowerCase().lastIndexOf(marker); int end = sql.indexOf(" ", (idx + marker.length() + 1)); tableName = sql.substring(idx, end); return tableName; } catch(Exception e) { log.severe("Failed to figure out table name from SQL"); return null; } } // End if return tableName; } // End getTableName public String getKeyFieldName() { return keyFieldName; } public String getKeyFieldValue() { return keyFieldValue; } public String getSQL() { return resultOfSQL; } public void setDBConnector(String dbconnector) { this.dbconnector = dbconnector; } public void setTableName(String tableName) { this.tableName = tableName; } public void setKeyFieldName(String keyFieldName) { this.keyFieldName = keyFieldName; } public void setKeyFieldValue(String keyFieldValue) { this.keyFieldValue = keyFieldValue; } public void setSQL(String resultOfSQL) { this.resultOfSQL = resultOfSQL; } protected void copy(PLUSRelational other) { super.copy(other); setDBConnector(other.getDBConnector()); setTableName(other.getTableName()); setKeyFieldName(other.getKeyFieldName()); setKeyFieldValue(other.getKeyFieldValue()); setSQL(other.getSQL()); columnNames = other.columnNames; redacted = other.getRedactedList(); setObjectSubtype(PLUS_SUBTYPE_RELATIONAL); setObjectType(PLUS_TYPE_DATA); } // End copy() public PLUSObject clone() { PLUSRelational r = new PLUSRelational(); r.copy(this); return r; } public Map<String,Object> getStorableProperties() { Map<String,Object> m = super.getStorableProperties(); m.put("keyFieldName", getKeyFieldName()); m.put("keyFieldValue", getKeyFieldValue()); m.put("tableName", getTableName()); m.put("SQL", getSQL()); return m; } public PLUSObject setProperties(PropertySet props, ProvenanceCollection contextCollection) throws PLUSException { super.setProperties(props, contextCollection); setKeyFieldName(""+props.getProperty("keyFieldName", "")); setKeyFieldValue(""+props.getProperty("keyFieldValue", "")); setTableName("" + props.getProperty("tableName", "")); setSQL(""+ props.getProperty("SQL")); return this; } } // End PLUSRelational