/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.cloud.network;
import java.util.*;
import java.util.logging.*;
import com.caucho.cloud.topology.CloudServer;
import com.caucho.config.ConfigException;
import com.caucho.env.service.*;
import com.caucho.network.listen.*;
import com.caucho.util.*;
import com.caucho.vfs.QServerSocket;
public class NetworkListenSystem extends AbstractResinSubSystem
implements AlarmListener
{
public static final int START_PRIORITY_AT_BEGIN = 50;
public static final int START_PRIORITY_AT_END = 90;
private static final L10N L = new L10N(NetworkListenSystem.class);
private static final Logger log
= Logger.getLogger(NetworkListenSystem.class.getName());
private static final long ALARM_TIMEOUT = 120 * 1000L;
private final CloudServer _cloudServer;
private TcpSocketLinkListener _clusterListener;
private final ArrayList<TcpSocketLinkListener> _listeners
= new ArrayList<TcpSocketLinkListener>();
private boolean _isBindPortsAtEnd = true;
private Alarm _alarm;
private NetworkListenSystem(CloudServer cloudServer)
{
_cloudServer = cloudServer;
NetworkClusterSystem clusterService = NetworkClusterSystem.getCurrent();
if (clusterService != null)
_clusterListener = clusterService.getClusterListener();
if (_clusterListener != null)
_listeners.add(_clusterListener);
NetworkServerConfig config = new NetworkServerConfig(this);
configure(_cloudServer, config);
}
public static NetworkListenSystem
createAndAddService(CloudServer cloudServer)
{
ResinSystem system = preCreate(NetworkListenSystem.class);
NetworkListenSystem service = new NetworkListenSystem(cloudServer);
system.addService(NetworkListenSystem.class, service);
return service;
}
public static NetworkListenSystem getCurrent()
{
return ResinSystem.getCurrentService(NetworkListenSystem.class);
}
/**
* Returns the cluster listener, if in a clustered environment.
*/
public TcpSocketLinkListener getClusterListener()
{
return _clusterListener;
}
public void addListener(TcpSocketLinkListener listener)
{
try {
if (! _listeners.contains(listener))
_listeners.add(listener);
/*
if (_lifecycle.isAfterStarting()) {
// server/1e00
port.bind();
port.start();
}
*/
} catch (Exception e) {
throw ConfigException.create(e);
}
}
/**
* If true, ports are bound at end.
*/
public void setBindPortsAfterStart(boolean bindAtEnd)
{
_isBindPortsAtEnd = bindAtEnd;
}
/**
* If true, ports are bound at end.
*/
public boolean isBindPortsAfterStart()
{
return _isBindPortsAtEnd;
}
/**
* Returns the {@link TcpSocketLinkListener}s for this server.
*/
public Collection<TcpSocketLinkListener> getListeners()
{
return Collections.unmodifiableList(_listeners);
}
public void bind(String address, int port, QServerSocket ss)
throws Exception
{
if ("null".equals(address))
address = null;
for (int i = 0; i < _listeners.size(); i++) {
TcpSocketLinkListener serverPort = _listeners.get(i);
if (port != serverPort.getPort())
continue;
if ((address == null) != (serverPort.getAddress() == null))
continue;
else if (address == null || address.equals(serverPort.getAddress())) {
serverPort.bind(ss);
return;
}
}
throw new IllegalStateException(L.l("No matching port for {0}:{1}",
address, port));
}
/**
* Finds the TcpConnection given the threadId
*/
public TcpSocketLink findConnectionByThreadId(long threadId)
{
for (TcpSocketLinkListener listener : getListeners()) {
TcpSocketLink conn = listener.findConnectionByThreadId(threadId);
if (conn != null)
return conn;
}
return null;
}
//
// lifecycle
//
@Override
public int getStartPriority()
{
if (_isBindPortsAtEnd)
return START_PRIORITY_AT_END;
else
return START_PRIORITY_AT_BEGIN;
}
/**
* Bind the ports.
*/
@Override
public void start()
throws Exception
{
boolean isFirst = true;
for (TcpSocketLinkListener listener : _listeners) {
if (listener == _clusterListener)
continue;
if (isFirst)
log.info("");
isFirst = false;
listener.bind();
listener.start();
}
if (! isFirst)
log.info("");
_alarm = new Alarm(this);
_alarm.queue(ALARM_TIMEOUT);
}
private void configure(CloudServer server, Object config)
{
ClusterServerProgram program;
program = server.getCluster().getData(ClusterServerProgram.class);
if (program != null)
program.getProgram().configure(config);
program = server.getPod().getData(ClusterServerProgram.class);
if (program != null)
program.getProgram().configure(config);
program = server.getData(ClusterServerProgram.class);
if (program != null)
program.getProgram().configure(config);
}
/**
* Handles the alarm.
*/
@Override
public void handleAlarm(Alarm alarm)
{
try {
for (TcpSocketLinkListener listener : _listeners) {
if (listener.isClosed()) {
log.severe("Resin restarting due to closed listener: " + listener);
// destroy();
//_controller.restart();
}
}
} catch (Throwable e) {
log.log(Level.WARNING, e.toString(), e);
// destroy();
//_controller.restart();
} finally {
alarm = _alarm;
if (alarm != null)
alarm.queue(ALARM_TIMEOUT);
}
}
/**
* Closes the server.
*/
@Override
public void stop()
{
Alarm alarm = _alarm;
_alarm = null;
if (alarm != null)
alarm.dequeue();
for (TcpSocketLinkListener listener : _listeners) {
try {
if (listener != _clusterListener) {
listener.close();
}
} catch (Throwable e) {
log.log(Level.WARNING, e.toString(), e);
}
}
}
}