All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aoindustries.aoserv.client.FilesystemCachedTable Maven / Gradle / Ivy

There is a newer version: 1.92.0
Show newest version
/*
 * aoserv-client - Java client for the AOServ platform.
 * Copyright (C) 2003-2013, 2016  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-client.
 *
 * aoserv-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aoserv-client 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with aoserv-client.  If not, see .
 */
package com.aoindustries.aoserv.client;

import com.aoindustries.io.FileList;
import com.aoindustries.io.FileListObjectFactory;
import com.aoindustries.util.sort.ComparisonSortAlgorithm;
import com.aoindustries.util.sort.FastQSort;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * A FilesystemCachedTable stores all of the
 * available FilesystemCachedObjects in a
 * temporary file and performs all subsequent data access
 * locally.  The server notifies the client when a table
 * is updated, and the caches are then invalidated.  Once
 * invalidated, the data is reloaded upon next use.
 * 

* The file format is a simple fixed record length format. * * TODO: It is possible to use the same column sorting technique * to implement the getIndexedRows method from AOServTable. * * @author AO Industries, Inc. */ public abstract class FilesystemCachedTable> extends AOServTable implements FileListObjectFactory { /** * The last time that the data was loaded, or * -1 if not yet loaded. */ private long lastLoaded=-1; /** * One file list may exist per column. Only the unique columns will have a non-null value. * Once a file is sorted on the specific column, its FileList is wrapped in an * unmodifiable list. This allows the list to be returned to any number of callers without * any additional copying. If the data is reloaded, a new FileList will be created, leaving * the old copy intact for those still using the previous copy. */ private List> columnLists; /** * The raw list of objects as downloaded from the master. */ private FileList tableList; /** * This is an unmodifiable list and may be returned to any number of callers without copying. */ private List unmodifiableTableList; protected FilesystemCachedTable(AOServConnector connector, Class clazz) { super(connector, clazz); } abstract int getRecordLength(); /** * Clears the cache, freeing up memory. The data will be reloaded upon * next use. */ @Override public void clearCache() { super.clearCache(); synchronized(this) { lastLoaded=-1; tableList=null; unmodifiableTableList=null; if(columnLists!=null) columnLists.clear(); } } /** * Reloads the cache if the cache has expired. All accesses are already synchronized. */ private void validateCache() throws IOException, SQLException { long currentTime=System.currentTimeMillis(); if( // If cache never loaded lastLoaded==-1 // If the system time was reset to previous time || currentTime newTableList=new FileList<>( schemaTable.getName(), "table", getRecordLength(), this ); getObjects(true, newTableList, AOServProtocol.CommandID.GET_TABLE, getTableID()); tableList=newTableList; unmodifiableTableList=Collections.unmodifiableList(tableList); lastLoaded=currentTime; if(columnLists!=null) columnLists.clear(); } } /** * Gets the complete list of objects in the table. This list is unmodifiable and will not ever be changed. * Newer data will be contained in new lists so that any calling code sees a snapshot of the code and may * safely assume the data is constant as long as the code uses the same reference to List returned * here. */ @Override public final List getRows() throws IOException, SQLException { synchronized(this) { validateCache(); return unmodifiableTableList; } } /** * FastQSort accesses the disk file less than other algorithms, and does * not load all the objects into memory at once like the default Java * merge sort. */ @Override protected ComparisonSortAlgorithm getSortAlgorithm() { return FastQSort.getInstance(); } @Override final protected V getUniqueRowImpl(int col, Object value) throws IOException, SQLException { SchemaTable schemaTable=getTableSchema(); SchemaColumn schemaColumn=schemaTable.getSchemaColumn(connector, col); SQLComparator Vcomparator=new SQLComparator<>( connector, new SQLExpression[] { new SQLColumnValue(connector, schemaColumn) }, new boolean[] {ASCENDING} ); SQLComparator Ocomparator=new SQLComparator<>( connector, new SQLExpression[] { new SQLColumnValue(connector, schemaColumn) }, new boolean[] {ASCENDING} ); synchronized(this) { validateCache(); // Create any needed objects int minLength=col+1; if(columnLists==null) columnLists=new ArrayList<>(minLength); else while(columnLists.size() unmodifiableSortedList = columnLists.get(col); if(unmodifiableSortedList==null) { FileList sortedFileList=new FileList<>( schemaTable.getName()+'.'+schemaColumn.getColumnName(), "unique", getRecordLength(), tableList.getObjectFactory() ); sortedFileList.addAll(tableList); getSortAlgorithm().sort(sortedFileList, Vcomparator); unmodifiableSortedList=Collections.unmodifiableList(sortedFileList); columnLists.set(col, unmodifiableSortedList); } int index=Collections.binarySearch(unmodifiableSortedList, value, Ocomparator); return index<0?null:unmodifiableSortedList.get(index); } } /** * Determines if the contents are currently sorted for quick unique lookups. */ boolean isSorted(int uniqueColumn) { return columnLists!=null && columnLists.size()>uniqueColumn && columnLists.get(uniqueColumn)!=null ; } @Override final public boolean isLoaded() { return lastLoaded!=-1; } @Override public V createInstance() throws IOException { V obj = getNewObject(); if(obj instanceof SingleTableObject) ((SingleTableObject)obj).setTable(this); return obj; } }