/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.persistence; import java.io.IOException; import java.io.Serializable; import java.rmi.RemoteException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.Utils; /** * Class for client side cache to prevent recurring network calls * * @author jblok */ public class ServerProxy implements IServer, Serializable { protected final IServer server; //local cache private String serverName; private String databaseProductName; // handle serialization of ConcurrentHashMap in writeObject/readObject (tables transient) because in a terracotta environment serialization of // ConcurrentHashMap is broken, See http://jira.terracotta.org/jira/browse/CDV-1377 private volatile transient Map<String, ITable> tables = new ConcurrentHashMap<String, ITable>(); public ServerProxy(IServer a_server) { server = a_server; // we cannot load all tables with columns from database, it to slow on big databases (1000+ tables) } private void writeObject(java.io.ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeObject(new HashMap<String, ITable>(tables)); } @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); tables = new ConcurrentHashMap<String, ITable>(); tables.putAll((Map< ? extends String, ? extends ITable>)s.readObject()); } @SuppressWarnings("nls") public ITable getTable(String tableName) throws RepositoryException, RemoteException { if (tableName == null) return null; String lcname = Utils.toEnglishLocaleLowerCase(tableName); ITable table = tables.get(lcname); if (table == null) { // table is not found, now just first load all initialized tables from the server // so that it doesn't go one by one. The first client will not find anything, but it will slowly be filled. tables.putAll(getInitializedTables()); table = tables.get(lcname); if (table == null) { // table still not found, now try to get it and initialize it (so that it will be found the next time in getInitializedTables()) table = server.getTable(tableName); if (table != null) { tables.put(lcname, table); } else { Debug.trace("Table " + tableName + " name not found on server: " + getName()); } } } return table; } public ITable getTableBySqlname(String tableSQLName) throws RepositoryException, RemoteException { if (tableSQLName == null) return null; for (ITable table : tables.values()) { if (tableSQLName.equals(table.getSQLName())) { return table; } } tables.putAll(getInitializedTables()); // try again for (ITable table : tables.values()) { if (tableSQLName.equals(table.getSQLName())) { return table; } } // still not loaded, do remote call ITable table = server.getTableBySqlname(tableSQLName); if (table != null) { tables.put(Utils.toEnglishLocaleLowerCase(table.getName()), table); } return table; } /* * (non-Javadoc) * * @see com.servoy.j2db.persistence.IServer#getTables(boolean) */ public Map<String, ITable> getInitializedTables() throws RepositoryException, RemoteException { return server.getInitializedTables(); } public List<String> getTableAndViewNames(boolean hideTemporary) throws RepositoryException, RemoteException { return server.getTableAndViewNames(hideTemporary); } public List<String> getViewNames(boolean hideTempViews) throws RepositoryException, RemoteException { return server.getViewNames(hideTempViews); } public List<String> getTableNames(boolean hideTempTables) throws RepositoryException, RemoteException { return server.getTableNames(hideTempTables); } public String getName() throws RemoteException { if (serverName == null) { serverName = server.getName(); } return serverName; } private boolean valid = false; public boolean isValid() throws RemoteException { // cache the valid state when the server was valid on the servoy server. // If this toggles for a client then we have other problems anyway. if (!valid) { valid = server.isValid(); } return valid; } public void combineTables(ServerProxy sp) { if (sp == null || sp == this) return; for (java.util.Map.Entry<String, ITable> entry : sp.tables.entrySet()) { if (entry.getValue() != null && !tables.containsKey(entry.getKey())) { tables.put(entry.getKey(), entry.getValue()); } } } @Override public String toString() { try { return getName(); } catch (RemoteException e) { Debug.error(e); return super.toString(); } } public String getDatabaseProductName() throws RepositoryException, RemoteException { if (databaseProductName == null) { databaseProductName = server.getDatabaseProductName(); } return databaseProductName; } public String getQuotedIdentifier(String tableSqlName, String columnSqlName) throws RemoteException, RepositoryException { return server.getQuotedIdentifier(tableSqlName, columnSqlName); } public int getTableType(String tableName) throws RepositoryException, RemoteException { ITable t = getTable(tableName); if (t != null) { return t.getTableType(); } return ITable.UNKNOWN; } @Override public String[] getDataModelClonesFrom() throws RemoteException { return server.getDataModelClonesFrom(); } }