| /******************************************************************************* |
| * Copyright (c) 1998, 2013 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 v1.0 and Eclipse Distribution License v. 1.0 |
| * which accompanies this distribution. |
| * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * Contributors: |
| * Mike Norman - from Proof-of-concept, become production code |
| ******************************************************************************/ |
| package dbws.testing.shadowddlgeneration.oldjpub; |
| |
| //javase imports |
| import java.io.Externalizable; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.ObjectOutput; |
| import java.sql.CallableStatement; |
| import java.sql.Connection; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| //EclipseLink imports |
| import dbws.testing.shadowddlgeneration.oldjpub.MethodFilter; |
| import dbws.testing.shadowddlgeneration.oldjpub.Util; |
| import dbws.testing.shadowddlgeneration.oldjpub.OracleTypes; |
| import dbws.testing.shadowddlgeneration.oldjpub.SqlName; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.ALL_ARGUMENTS; |
| |
| @SuppressWarnings({"unchecked","rawtypes"}) |
| public class ViewCache implements Externalizable { |
| |
| static final String VIEW_CACHE_PREFIX = "viewcachefor"; |
| public static final String PARAMETER_USER = "USER"; |
| public static final String PARAMETER_ALL = "ALL"; |
| |
| protected Connection m_conn; |
| protected String m_user; |
| protected Map m_rowsCacheIndex; |
| protected ArrayList m_rowsCache; |
| protected int m_hits; |
| protected int m_visits; |
| protected boolean m_viewCacheDebug = false; |
| |
| @SuppressWarnings("unused") |
| public Iterator<ViewRow> getRows(String view, String[] columns, String[] keys, Object[] values, |
| String[] orderby) throws java.sql.SQLException { |
| m_visits++; |
| String cKey = makeKey(view, columns, keys, values, orderby); |
| ArrayList rowsV = (ArrayList)m_rowsCacheIndex.get(cKey); |
| ArrayList derivedRowsV = rowsV; |
| |
| // NARROW DOWN QUERIES |
| if (derivedRowsV == null) { |
| for (int i = 0; i < m_rowsCache.size(); i++) { |
| RowsCacheEntry incoming = new RowsCacheEntry(view, columns, keys, values, null); |
| RowsCacheEntry cached = (RowsCacheEntry)m_rowsCache.get(i); |
| RowsCacheEntry diff = cached.compare(incoming); |
| if (diff != null) { |
| if (m_viewCacheDebug) { |
| System.out.println("viewcache.hit.query.relaxed: " + i); |
| } |
| derivedRowsV = new ArrayList(); |
| for (int j = 0; j < diff.getRows().size(); j++) { |
| ViewRow row = diff.getRows().get(j); |
| boolean match = true; |
| for (int k = 0; k < diff.getKeys().length; k++) { |
| if ((k + 1) < diff.getKeys().length |
| && diff.getKeys()[k].equals(diff.getKeys()[k + 1])) { |
| if (!row.equals(diff.getKeys()[k], diff.getValues()[k]) |
| && !row.equals(diff.getKeys()[k], diff.getValues()[k])) { |
| match = false; |
| k++; |
| } |
| } |
| else if (!row.equals(diff.getKeys()[k], diff.getValues()[k])) { |
| match = false; |
| } |
| } |
| if (match) { |
| derivedRowsV.add(row); |
| } |
| } |
| break; |
| } |
| else if (m_viewCacheDebug) { |
| System.out.println("viewcache.no.match.query: " + i + ", diff=" + diff); |
| } |
| } |
| } |
| // ORDER BY SEQUENCE |
| if (rowsV == null && orderby.length == 1 && orderby[0].equalsIgnoreCase(Util.SEQUENCE) |
| && ViewRowFactory.hasSequence(view)) { |
| if (derivedRowsV == null) { |
| String unorderedCKey = makeKey(view, columns, keys, values, new String[0]); |
| derivedRowsV = (ArrayList)m_rowsCacheIndex.get(unorderedCKey); |
| } |
| if (derivedRowsV != null) { |
| rowsV = (ArrayList)derivedRowsV.clone(); |
| UserArguments.orderBySequence(rowsV); |
| if (m_viewCacheDebug) { |
| System.out.println("viewcache.hit.query.unordered.sequence"); |
| } |
| } |
| } |
| else |
| // ORDER BY POSITION |
| if (rowsV == null && orderby.length == 1 && orderby[0].equalsIgnoreCase("POSITION") |
| && ViewRowFactory.hasPosition(view)) { |
| if (derivedRowsV == null) { |
| String unorderedCKey = makeKey(view, columns, keys, values, new String[0]); |
| derivedRowsV = (ArrayList)m_rowsCacheIndex.get(unorderedCKey); |
| } |
| if (derivedRowsV != null) { |
| rowsV = (ArrayList)derivedRowsV.clone(); |
| UserArguments.orderByPosition(rowsV); |
| if (m_viewCacheDebug) { |
| System.out.println("viewcache.hit.query.unordered.position"); |
| } |
| } |
| } |
| if (rowsV == null) { |
| rowsV = derivedRowsV; |
| } |
| if (rowsV == null) { |
| String stmtText = makeQuery(view, columns, keys, values, orderby); |
| ResultSet rset = null; |
| PreparedStatement stmt = null; |
| if (m_conn == null) { |
| throw new SQLException("ERROR: null JDBC connection."); |
| } |
| try { |
| stmt = m_conn.prepareStatement(stmtText); |
| long millis = 0; |
| if (m_viewCacheDebug) { |
| millis = System.currentTimeMillis(); |
| System.out.println("viewcache.execute.query: " + stmtText); |
| } |
| rset = stmt.executeQuery(); |
| rowsV = new ArrayList<ViewRow>(); |
| while (rset.next()) { |
| rowsV.add(ViewRowFactory.createViewRow(view, columns, rset)); |
| if (m_viewCacheDebug) { |
| System.out.print("."); |
| } |
| } |
| if (m_viewCacheDebug) { |
| System.out.println("\nviewcache.execute.query.result.set.next.elapse: " |
| + (System.currentTimeMillis() - millis)); |
| } |
| } |
| finally { |
| if (rset != null) { |
| rset.close(); |
| } |
| if (stmt != null) { |
| stmt.close(); |
| } |
| } |
| |
| m_rowsCacheIndex.put(cKey, rowsV); |
| m_rowsCache.add(new RowsCacheEntry(view, columns, keys, values, rowsV)); |
| |
| } |
| else { |
| m_hits++; |
| if (m_viewCacheDebug) { |
| String stmtText = makeQuery(view, columns, keys, values, orderby); |
| System.out.println("viewcache.hit.query: " + stmtText); |
| } |
| } |
| |
| if (rowsV == null) { |
| // not found in view cache and no database connection |
| rowsV = new ArrayList(); |
| } |
| if (m_viewCacheDebug) { |
| System.out.println("viewcache.execute.query.result.size: " + rowsV.size()); |
| System.out.println("viewcache.hit.rate: " + cKey + ", " + m_hits + "/" + m_visits); |
| } |
| return rowsV.iterator(); |
| } |
| |
| public Object[] getOutParameters(String stmtText, Object[] inParams, int[] types) |
| throws SQLException { |
| m_visits++; |
| Object[] objTypes = toObject(types); |
| String cKey = makeKey(stmtText, new String[0], new String[0], inParams, new String[0]); |
| ArrayList outParamList = (ArrayList)m_rowsCacheIndex.get(cKey); |
| if (outParamList == null && m_conn != null) { |
| // initialize parameter list |
| outParamList = new ArrayList(); |
| CallableStatement stmt = m_conn.prepareCall(stmtText); |
| int i = 1; |
| for (; i < inParams.length + 1; i++) { |
| if (inParams[i - 1] instanceof byte[]) { |
| stmt.setBytes(i, (byte[])inParams[i - 1]); |
| } |
| else { |
| throw new SQLException("input type not supported: " |
| + inParams[i - 1].getClass().getName()); |
| } |
| } |
| for (; i < (inParams.length + types.length + 1); i++) { |
| stmt.registerOutParameter(i, types[i - inParams.length - 1]); |
| } |
| stmt.executeUpdate(); |
| for (i = inParams.length; i < inParams.length + types.length; i++) { |
| int index = i - inParams.length; |
| if (types[index] == OracleTypes.INTEGER) { |
| outParamList.add(Integer.valueOf(stmt.getInt(i + 1))); |
| } |
| else if (types[index] == OracleTypes.VARCHAR) { |
| outParamList.add(stmt.getString(i + 1)); |
| } |
| } |
| stmt.close(); |
| m_rowsCacheIndex.put(cKey, outParamList); |
| m_rowsCache.add(new RowsCacheEntry(stmtText, new String[0], new String[0], objTypes, |
| outParamList)); |
| } |
| |
| Object[] outParams = null; |
| if (outParamList != null) { |
| outParams = outParamList.toArray(new Object[0]); |
| } |
| return outParams; |
| } |
| |
| private Object[] toObject(int[] types) { |
| Object[] obj = new Object[types.length]; |
| for (int i = 0; i < types.length; i++) { |
| obj[i] = Integer.valueOf(types[i]); |
| } |
| return obj; |
| } |
| |
| private String makeKey(String view, String[] columns, String[] keys, Object[] values, |
| String[] orderby) { |
| String key = view; |
| if (columns.length != 0) { |
| key = key + "["; |
| for (int i = 0; i < columns.length; i++) { |
| key += columns[i] + (i > 0 ? ", " : ""); |
| } |
| key += "]"; |
| } |
| key = key + "("; |
| for (int i = 0; i < keys.length; i++) { |
| key += keys[i] + ", "; |
| } |
| key += ": "; |
| for (int i = 0; i < values.length; i++) { |
| if (values[i] instanceof byte[]) { |
| byte[] bytes = (byte[])values[i]; |
| for (int j = 0; j < bytes.length; j++) { |
| key += bytes[j]; |
| } |
| key += ", "; |
| } |
| else { |
| key += values[i] + ", "; |
| } |
| } |
| key += ")"; |
| for (int i = 0; i < orderby.length; i++) { |
| key += orderby[i] + "(o)"; |
| } |
| return key.toUpperCase(); // On Windows, all keys are capitalized during deserialization |
| } |
| |
| /* |
| * makeQuery( "ALL_OBJECT", new String[]{"OWNER", "OBJEC_TYPE", "OBJECT_TYPE"}, new |
| * String[]{"SCOTT", "PROCEDURE", "FUN%CTION"}) => WEHERE OWNER='SCOTT' AND |
| * (OBJECT_TYPE='PROCEDURE OR OBJECT_TYPE like 'FUN%CTION') |
| */ |
| private String makeQuery(String view, String[] columns, String[] keys, Object[] values, |
| String[] orderby) { |
| String stmt = "SELECT " + ViewRowFactory.getProject(view, columns) + " FROM " + view; |
| if (keys.length > 0) { |
| stmt += " WHERE "; |
| } |
| boolean inOR = false; |
| for (int i = 0; i < keys.length; i++) { |
| // OR |
| if (!inOR && i < (keys.length - 1) && keys[i].equals(keys[i + 1])) { |
| stmt += "("; |
| inOR = true; |
| } |
| stmt += keys[i]; |
| if (values[i] == null || "".equals(values[i])) { |
| stmt += " IS NULL"; |
| } |
| else if ("NOT NULL".equals(values[i])) { |
| stmt += " IS NOT NULL"; |
| } |
| else if (values[i] instanceof java.lang.String) { |
| stmt += "='" + ((java.lang.String)values[i]) + "'"; |
| } |
| else if ((values[i] instanceof java.lang.String) |
| && ((String)values[i]).indexOf("%") > -1) { |
| stmt += " like '" + ((java.lang.String)values[i]) + "'"; |
| } |
| else { |
| stmt += "=" + values[i]; |
| } |
| if (inOR && i < keys.length - 1 && keys[i].equals(keys[i + 1])) { |
| stmt += " OR "; |
| } |
| else if (inOR && i < keys.length - 1) { |
| stmt += ") AND "; |
| inOR = false; |
| } |
| else if (inOR) { |
| stmt += ")"; |
| inOR = false; |
| } |
| else if (i < keys.length - 1) { |
| stmt += " AND "; |
| } |
| } |
| for (int i = 0; i < orderby.length; i++) { |
| if (i == 0) { |
| stmt += " ORDER BY "; |
| } |
| stmt += orderby[i]; |
| if (i != (orderby.length - 1)) { |
| stmt += ", "; |
| } |
| } |
| return stmt; |
| } |
| |
| public ViewCache() { |
| } |
| |
| public ViewCache(Connection conn, String user) { |
| m_conn = conn; |
| m_user = user; |
| m_rowsCacheIndex = new HashMap(); |
| m_rowsCache = new ArrayList(); |
| m_hits = 0; |
| m_visits = 0; |
| } |
| |
| /* |
| * initialize the cache using queries of the viewcache passed in |
| */ |
| public void init(ViewCache viewCache) { |
| int len = m_rowsCache.size(); |
| for (int i = 0; i < len; i++) { |
| RowsCacheEntry entry = (RowsCacheEntry)m_rowsCache.get(i); |
| try { |
| getRows(entry); |
| } |
| catch (Exception e) { |
| System.err.println("WARNING: error in refreshing view cache for view " |
| + entry.getView()); |
| } |
| } |
| } |
| |
| /* |
| * refresh the cache |
| */ |
| public void refresh() { |
| int len = m_rowsCache.size(); |
| for (int i = 0; i < len; i++) { |
| RowsCacheEntry entry = (RowsCacheEntry)m_rowsCache.get(i); |
| try { |
| getRows(entry); |
| } |
| catch (Exception e) { |
| System.err.println("WARNING: error in refreshing view cache for view " |
| + entry.getView()); |
| } |
| } |
| } |
| |
| private Iterator getRows(RowsCacheEntry entry) throws SQLException { |
| return getRows(entry.getView(), entry.getSelects(), entry.getKeys(), entry.getValues(), |
| new String[0]); |
| } |
| |
| public void fetch(String packageName, MethodFilter sigf) throws SQLException { |
| if (m_viewCacheDebug) { |
| System.out.println("viewcache.fetch: " + packageName); |
| } |
| |
| packageName = dbifyName(packageName); |
| if (PARAMETER_ALL.equalsIgnoreCase(packageName) |
| || PARAMETER_USER.equalsIgnoreCase(packageName)) { |
| getRows(ALL_ARGUMENTS, new String[0], new String[]{}, new Object[]{}, new String[0]); |
| } |
| else { |
| String[] keys = null; |
| Object[] values = null; |
| if (sigf != null && sigf.isSingleMethod()) { |
| keys = new String[]{Util.PACKAGE_NAME, Util.OBJECT_NAME}; |
| values = new Object[]{packageName, sigf.getSingleMethodName()}; |
| } |
| else { |
| keys = new String[]{Util.PACKAGE_NAME}; |
| values = new Object[]{packageName}; |
| } |
| getRows(ALL_ARGUMENTS, new String[0], keys, values, new String[0]); |
| } |
| } |
| |
| public int getHits() { |
| return m_hits; |
| } |
| |
| public int getVisits() { |
| return m_visits; |
| } |
| |
| public void reset(Connection conn) { |
| m_conn = conn; |
| } |
| |
| public void close() { |
| try { |
| if (m_conn != null) { |
| m_conn.close(); |
| } |
| } |
| catch (Exception e) { |
| // Closing resources. OK to ignore exception. |
| } |
| } |
| |
| public String getUser() { |
| return m_user; |
| } |
| |
| public String getFileName(String dir) { |
| return getFileName(dir, m_user); |
| } |
| |
| public static String getFileName(String dir, String user) { |
| return (dir == null ? "" : dir + File.separator) + VIEW_CACHE_PREFIX + user.toLowerCase(); |
| } |
| |
| public void readExternal(java.io.ObjectInput in) throws IOException, ClassNotFoundException { |
| if (m_viewCacheDebug) { |
| System.out.println("viewcache.read.external"); |
| } |
| // summary |
| m_user = (String)in.readObject(); |
| m_hits = ((Integer)in.readObject()).intValue(); |
| m_visits = ((Integer)in.readObject()).intValue(); |
| |
| // m_rowsCache |
| int rowsCacheSize = ((Integer)in.readObject()).intValue(); |
| m_rowsCache = new ArrayList(rowsCacheSize); |
| for (int i = 0; i < rowsCacheSize; i++) { |
| RowsCacheEntry rce = (RowsCacheEntry)in.readObject(); |
| m_rowsCache.add(rce); |
| } |
| |
| // m_rowsCacheIndex (String, ArrayList<ViewRow>) |
| int rowsCacheIndexSize = ((Integer)in.readObject()).intValue(); |
| m_rowsCacheIndex = new HashMap(rowsCacheIndexSize); |
| for (int i = 0; i < rowsCacheIndexSize; i++) { |
| String key = (String)in.readObject(); |
| int rowsSize = ((Integer)in.readObject()).intValue(); |
| ArrayList rows = new ArrayList(rowsSize); |
| for (int j = 0; j < rowsSize; j++) { |
| ViewRow row = (ViewRow)in.readObject(); |
| rows.add(row); |
| } |
| m_rowsCacheIndex.put(key, rows); |
| } |
| } |
| |
| public void writeExternal(ObjectOutput out) throws IOException { |
| if (m_viewCacheDebug) { |
| System.out.println("viewcache.write.external"); |
| } |
| // summary |
| out.writeObject(m_user); |
| out.writeObject(Integer.valueOf(m_hits)); |
| out.writeObject(Integer.valueOf(m_visits)); |
| |
| // m_rowsCache |
| out.writeObject(Integer.valueOf(m_rowsCache.size())); |
| for (int i = 0; i < m_rowsCache.size(); i++) { |
| RowsCacheEntry rce = (RowsCacheEntry)m_rowsCache.get(i); |
| out.writeObject(rce); |
| } |
| |
| // m_rowsCacheIndex (String, ArrayList<ViewRow>) |
| out.writeObject(Integer.valueOf(m_rowsCacheIndex.size())); |
| Iterator keys = m_rowsCacheIndex.keySet().iterator(); |
| Iterator values = m_rowsCacheIndex.values().iterator(); |
| while (keys.hasNext()) { |
| out.writeObject(keys.next()); |
| ArrayList rows = (ArrayList)values.next(); |
| out.writeObject(Integer.valueOf(rows.size())); |
| for (int i = 0; i < rows.size(); i++) { |
| out.writeObject(rows.get(i)); |
| } |
| } |
| } |
| |
| public String printSummary() { |
| // summary |
| String text = ""; |
| text += "****" + getFileName(null) + "****" + "\n"; |
| text += "schema: " + m_user + "\n"; |
| text += "hits/visits: " + m_hits + "/" + m_visits + "\n"; |
| for (int i = 0; i < m_rowsCache.size(); i++) { |
| RowsCacheEntry rce = (RowsCacheEntry)m_rowsCache.get(i); |
| text += rce.printSummary(); |
| } |
| return text; |
| } |
| |
| /* |
| * if the name is quoted, the quotes are removed. |
| * |
| * public static String dbifyName(String s, SqlReflector reflector) { if (s == null || |
| * s.equals("") || reflector == null || reflector.getConnection() == null) { return s; } return |
| * dbifyName(s, reflector.getViewCache()); } |
| */ |
| |
| public String dbifyName(String s) { |
| if (s == null) { |
| return ""; |
| } |
| else if (s.equals("")) { |
| return ""; |
| } |
| else if (SqlName.isQuoted(s)) { |
| return s.substring(1, s.length() - 1); |
| } |
| String upper_s = s.toUpperCase(); |
| String dbName = ""; |
| try { |
| Iterator rowIter = getRows(Util.DUAL, new String[]{"UPPER('" + s + "') AS UPPER_NAME"}, |
| new String[0], new Object[0], new String[0]); |
| if (rowIter.hasNext()) { |
| SingleColumnViewRow row = (SingleColumnViewRow)rowIter.next(); |
| dbName = row.getValue(); |
| } |
| else { |
| // not found in viewCache and no database connection |
| dbName = upper_s; |
| } |
| } |
| catch (Exception ex) { |
| System.err.println(ex.getMessage()); |
| dbName = upper_s; |
| } |
| return dbName; |
| } |
| |
| } |