| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.testing.tests.dbchangenotification; |
| |
| import java.util.*; |
| |
| import org.eclipse.persistence.sessions.Session; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.sessions.DatabaseRecord; |
| |
| /** |
| * This class translates a database change notification message |
| * into invalidation of a corresponding object in TopLink cache. |
| * |
| * The class expects the invalidation notification to be found in the following |
| * JMSMessage properties: |
| * String property "TABLE" should contain table name; |
| * Properties of appropriate type should contain value(s) of PK field(s). |
| * |
| * Examples based on Employee demo: |
| * 1. EMPLOYEE table: |
| * getStringProperty("TABLE") == "EMPLOYEE"; |
| * getObjectProperty("EMP_ID") == value of EMP_ID column for the modified row |
| * |
| * 2. SALARY table: |
| * getStringProperty("TABLE") == "SALARY"; |
| * getObjectProperty("EMP_ID") == value of EMP_ID column for the modified row |
| * |
| * 3. PHONE table: |
| * getStringProperty("TABLE") == "PHONE"; |
| * getObjectProperty("EMP_ID") == value of EMP_ID column for the modified row |
| * getObjectProperty("TYPE") == value of TYPE column for the modified row |
| */ |
| public class |
| |
| CacheInvalidator { |
| // maps a table name to Class mapped to this table |
| Hashtable tableNameToClass; |
| // maps table name to a vector of primary key fields' names |
| Hashtable tableNameToPkFieldNames; |
| |
| // Create a CacheInvalidator object that invalidates cache if the changed table |
| // is mapped by one of the descriptors of the passed session |
| |
| public CacheInvalidator(Session session) { |
| // HashSet is used to avoid duplications |
| HashSet tableNames = new HashSet(); |
| |
| // fill out tableNames collection with all tables' names mapped by all descriptors |
| Iterator<ClassDescriptor> descriptors = session.getDescriptors().values().iterator(); |
| while (descriptors.hasNext()) { |
| ClassDescriptor desc = descriptors.next(); |
| |
| // Create a Vector containing names of all tables mapped to the descriptor |
| Vector descTableNames = desc.getTableNames(); |
| // Remove schema names (if any) converting "SCHEMA_NAME.TABLE_NAME" to "TABLE_NAME" |
| removePrefixFromDatabaseObjectNames(descTableNames); |
| // add descTableNames to the collection |
| tableNames.addAll(descTableNames); |
| } |
| // initialize |
| initializeWithTableNames(session, tableNames); |
| } |
| |
| // Create a CacheInvalidator object that invalidates cache if the changed table's name |
| // is in tableNames collection |
| // and the table is mapped by one of the descriptors of the passed session. |
| // Note that the Collection tableNames will be altered - only names |
| // of tables not found in descriptors of the passed session will remain. |
| |
| public CacheInvalidator(Session session, Collection tableNames) { |
| // initialize |
| initializeWithTableNames(session, tableNames); |
| } |
| |
| protected void initializeWithTableNames(Session session, Collection tableNames) { |
| tableNameToClass = new Hashtable(tableNames.size()); |
| tableNameToPkFieldNames = new Hashtable(tableNames.size()); |
| |
| // pkFieldVectors cached here to avoid calculating it more than once per class |
| Hashtable classToPkFieldNames = new Hashtable(); |
| // loop through the descriptors to fill out tableNameToClass and tableNameToPkFieldNames |
| Iterator<ClassDescriptor> descriptors = session.getDescriptors().values().iterator(); |
| while (descriptors.hasNext() && !tableNames.isEmpty()) { |
| ClassDescriptor desc = descriptors.next(); |
| |
| // Create a Vector containing names of all tables mapped to the descriptor |
| Vector descTableNames = desc.getTableNames(); |
| |
| // bypass descriptors with no tables |
| if (descTableNames.isEmpty()) { |
| continue; |
| } |
| |
| // Remove schema names (if any) converting "SCHEMA_NAME.TABLE_NAME" to "TABLE_NAME" |
| removePrefixFromDatabaseObjectNames(descTableNames); |
| |
| // handle inheritance: table name should be mapped to the base mapped class |
| Class baseClass = desc.getJavaClass(); |
| while (desc.isChildDescriptor()) { |
| desc = session.getDescriptor(desc.getInheritancePolicy().getParentClass()); |
| baseClass = desc.getJavaClass(); |
| } |
| |
| Iterator it = tableNames.iterator(); |
| while (it.hasNext()) { |
| // for each tableName specified by the user |
| String tableName = (String)it.next(); |
| // verify whether the descriptor maps a table with the same name |
| if (descTableNames.contains(tableName)) { |
| // map the table name to the baseClass corresponding to the descriptor |
| tableNameToClass.put(tableName, baseClass); |
| |
| // try to obtain cached pkFieldNames Vector corresponding to baseClass |
| Vector pkFieldNames = (Vector)classToPkFieldNames.get(baseClass); |
| if (pkFieldNames == null) { |
| // Create a Vector containing names of all primary key fields |
| pkFieldNames = desc.getPrimaryKeyFieldNames(); |
| // Remove table name converting from "TABLE_NAME.FIELD_NAME" to "FIELD_NAME" |
| removePrefixFromDatabaseObjectNames(pkFieldNames); |
| // cache pkFieldNames Vector corresponding to baseClass |
| classToPkFieldNames.put(baseClass, pkFieldNames); |
| } |
| // map the table name to the Vector of names of primary key fields. |
| tableNameToPkFieldNames.put(tableName, pkFieldNames); |
| |
| // the table name is mapped - remove it from the list of table names to be mapped. |
| it.remove(); |
| } |
| } |
| } |
| } |
| |
| // invalidates in tjhe cache the object corresponding to the massage |
| |
| public void invalidateObject(Session session, javax.jms.Message msg) throws javax.jms.JMSException { |
| String tableName = msg.getStringProperty("TABLE"); |
| if (tableName == null) { |
| return; |
| } |
| Class baseClass = (Class)tableNameToClass.get(tableName); |
| if (baseClass == null) { |
| return; |
| } |
| Vector pkFieldNames = (Vector)tableNameToPkFieldNames.get(tableName); |
| if (pkFieldNames == null) { |
| return; |
| } |
| |
| // create DatabaseRecord corresponding to the message |
| DatabaseRecord row = new DatabaseRecord(pkFieldNames.size()); |
| for (int i = 0; i < pkFieldNames.size(); i++) { |
| String fieldName = (String)pkFieldNames.elementAt(i); |
| Object value = msg.getObjectProperty(fieldName); |
| row.put(fieldName, value); |
| } |
| |
| // invalidate in TopLink cache the object corresponding to the row and the baseClass |
| session.getIdentityMapAccessor().invalidateObject(row, baseClass); |
| } |
| |
| // converts "SCHEMA_NAME.TABLE_NAME" to "TABLE_NAME" |
| |
| protected void removePrefixFromDatabaseObjectNames(Vector names) { |
| for (int i = 0; i < names.size(); i++) { |
| String qualifiedName = (String)names.elementAt(i); |
| String name = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); |
| names.setElementAt(name, i); |
| } |
| } |
| } |