// $Id: NetcdfServer.java,v 1.4 2002-05-29 18:31:35 steve Exp $
/*
* Copyright 1997-2000 Unidata Program Center/University Corporation for
* Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
* support@unidata.ucar.edu.
*
* This library 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 2.1 of the License, or (at
* your option) any later version.
*
* This library 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 this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package ucar.netcdf;
import ucar.util.Logger;
import ucar.util.RMILogger;
import java.io.File;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.AccessException;
import java.rmi.ServerException;
import java.rmi.RMISecurityManager;
import java.rmi.server.UnicastRemoteObject;
import java.util.Hashtable;
import java.util.Enumeration;
import java.rmi.ConnectException;
import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
/**
* A UnicastRemoteObject implementation of NetcdfService.
* @note NetcdfService is a placeholder directory service
* for a more elaborate directory service,
* hopefully to be provided later on.
* @author $Author: steve $
* @version $Revision: 1.4 $ $Date: 2002-05-29 18:31:35 $
*/
public class
NetcdfServer
extends UnicastRemoteObject implements NetcdfService {
public static void
setLog(OutputStream out)
{
if(out != null)
{
logger_.logUpTo(Logger.DEBUG);
logger_.setLog(out);
}
else
{
logger_.logUpTo(Logger.NOTICE);
logger_.setLog(System.err);
UnicastRemoteObject.setLog(out);
}
}
public
NetcdfServer(String [] exports, Registry registry)
throws RemoteException, AlreadyBoundException
{
super();
byName_ = new Hashtable();
for(int ii = 0; ii < exports.length; ii++)
export(exports[ii]);
if(byName_.size() == 0)
throw new IllegalArgumentException("No exports");
if(registry != null)
{
registry_ = registry;
registry.bind(SVC_NAME, this);
logger_.logNotice(SVC_NAME
+ " bound in registry");
}
}
public int
ping()
throws RemoteException
{
return 0;
}
public NetcdfRemoteProxy
lookup(String dataSetName)
throws RemoteException
{
final Entry entry = get(dataSetName);
if(entry == null)
throw new AccessException(dataSetName +
" not available");
AbstractNetcdf nc = null;
try {
nc = entry.getNetcdfFile();
}
catch (IOException ioe)
{
throw new ServerException("lookup", ioe);
}
return (NetcdfRemoteProxy) exportObject(
new NetcdfRemoteProxyImpl(this, dataSetName, nc));
}
public String []
list()
throws RemoteException
{
String [] ret = new String [byName_.size()];
Enumeration ee = byName_.keys();
for(int ii = 0; ee.hasMoreElements(); ii++)
ret[ii] = (String) ee.nextElement();
return ret;
}
public void
export(File ff)
{
if(!ff.isFile())
throw new IllegalArgumentException(ff.getPath()
+ " not a File");
Entry entry = new Entry(ff);
String keyval = entry.keyValue();
logger_.logDebug("Exporting " + ff + " as "
+ keyval);
put(keyval, entry);
}
public void
export(String path)
{
export(new File(path));
}
protected void
finalize()
throws Throwable
{
super.finalize();
if(registry_ != null)
{
try {
registry_.unbind(SVC_NAME);
} catch (Exception ee) {
// we tried.
logger_.logError( "unbind: " + ee.getMessage());
}
}
registry_ = null;
}
public static Registry
startRegistry()
throws RemoteException
{
logger_.logNotice("No registry, starting one");
return LocateRegistry.createRegistry(
Registry.REGISTRY_PORT);
}
public static Registry
checkRegistry(Registry regis, int tryagain)
throws RemoteException
{
if(regis == null)
regis = startRegistry();
NetcdfService existing = (NetcdfService) null;
try {
existing = (NetcdfService) regis.lookup(SVC_NAME);
}
catch (ConnectException ce) {
if(--tryagain > 0)
{
return checkRegistry(startRegistry(),
tryagain);
}
throw ce;
}
catch (NotBoundException nbe) {
return regis; // Normal return
}
// else, AlreadyBound. Is it bogus?
try {
existing.ping();
}
catch (ConnectException ce) { // ?? any RemoteException
// bogus
try {
logger_.logNotice(
"unbinding dead registry entry");
regis.unbind(SVC_NAME);
} catch (NotBoundException nbe) {
// Race condition.
// Ignore here and catch it later.
}
}
return regis;
}
public static void
main(String args[])
{
System.setSecurityManager(new RMISecurityManager());
// setLog(System.err);
Registry regis = (Registry) null;
try {
regis = checkRegistry(LocateRegistry.getRegistry(), 2);
}
catch (Exception ee) {
PrintStream ps = getLog();
if(ps == null)
ps = System.err;
ps.println(
"NetcdfServer: error getting registry: "
+ ee.getMessage());
ee.printStackTrace(ps);
System.exit(1);
}
try {
NetcdfServer svc = new NetcdfServer(args, regis);
} catch (Throwable ee) {
PrintStream ps = getLog();
if(ps == null)
ps = System.err;
ps.println("NetcdfServer err: "
+ ee.getMessage());
ee.printStackTrace(ps);
System.exit(1);
}
}
/**/
/* package */ synchronized void
_release(String keyval)
{
final Entry entry = (Entry) byName_.get(keyval);
if(entry != null)
entry.releaseNetcdfFile();
}
/**
* Gets the Entry associated with the specified name.
* @param dataSetName the name
* @return the Entry, or null if not found
*/
private Entry
get(String dataSetName)
{ return (Entry) byName_.get(dataSetName); }
/**
* Puts the specified element into the Dictionary, using its
* keyValue() as key.
* The element may be retrieved by doing a get() with the key value.
* name. The element cannot be null.
* @param entry the new entry;
* @exception NullPointerException If the value of the specified
* element is null.
*/
synchronized private void
put(String keyval, Entry entry)
{
byName_.put(keyval, entry);
}
/**
* @serial
*/
private Hashtable byName_;
/**
* @serial
*/
private Registry registry_;
static /* package */ final RMILogger logger_ = new RMILogger();
class Entry
{
final File dirent;
NetcdfFile nc;
int refcount;
Entry(File ff)
{
this.dirent = ff;
nc = (NetcdfFile) null;
refcount = 0;
}
String
keyValue()
{
// Strip leading path
final String name = dirent.getName();
// Strip extension
final int index = name.indexOf('.');
return name.substring(0, index).intern();
}
/**
* Open entry.nc.
*/
synchronized private void
open(boolean readonly)
throws IOException
{
if(nc != null)
throw new IllegalArgumentException("dataSet "
+ keyValue() + " already open");
nc = new NetcdfFile(dirent, readonly);
}
/**
* Return entry.nc, opening if necessary.
* Increments reference count.
*/
synchronized NetcdfFile
getNetcdfFile()
throws IOException
{
if(nc == null)
open(true); // all access readonly for now
refcount++;
logger_.logDebug("refcount: " + refcount);
return nc;
}
/**
* Close entry.nc and delete any references to it,
* making it available for garbage collection.
*/
synchronized private void
close()
{
if(nc != null)
{
logger_.logDebug("closing: " + nc.getFile());
try {
nc.close();
}
catch (IOException ioe) {
// TODO: what?
}
nc = (NetcdfFile) null;
refcount = 0;
}
}
/**
* Decrement the reference count and close if no
* more references.
*/
synchronized void
releaseNetcdfFile()
{
if(refcount > 0)
refcount--;
if(refcount == 0) // assert (refcount >= 0)
close();
}
}
}