/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* @(#)GoodbyeHandler.java 1.41 06/28/07
*/
package com.sun.messaging.jmq.jmsserver.data.handlers;
import com.sun.messaging.jmq.jmsserver.Globals;
import com.sun.messaging.jmq.jmsserver.data.PacketHandler;
import com.sun.messaging.jmq.io.Packet;
import java.io.IOException;
import com.sun.messaging.jmq.io.PacketType;
import com.sun.messaging.jmq.io.Status;
import com.sun.messaging.jmq.util.log.*;
import com.sun.messaging.jmq.util.timer.*;
import com.sun.messaging.jmq.util.log.*;
import com.sun.messaging.jmq.jmsserver.service.Connection;
import com.sun.messaging.jmq.jmsserver.service.ConnectionUID;
import com.sun.messaging.jmq.jmsserver.service.imq.IMQConnection;
import com.sun.messaging.jmq.jmsserver.service.imq.IMQBasicConnection;
import com.sun.messaging.jmq.jmsserver.service.ConnectionManager;
import com.sun.messaging.jmq.jmsserver.util.BrokerException;
import com.sun.messaging.jmq.jmsserver.service.imq.IMQConnection;
import com.sun.messaging.jmq.jmsserver.resources.*;
import com.sun.messaging.jmq.util.GoodbyeReason;
import java.util.*;
/**
* Handler class which deals with a "goodbye" message which is sent
* when a client quits talking to the broker
*/
public class GoodbyeHandler extends PacketHandler
{
protected static long timeout = Globals.getConfig().getLongProperty(
Globals.IMQ + ".goodbye.timeout", 0);
ConnectionManager conlist = null;
public GoodbyeHandler(ConnectionManager mgr) {
conlist = mgr;
timeout = Globals.getConfig().getLongProperty(
Globals.IMQ + ".goodbye.timeout", 0);
GoodbyeTask.initialize(timeout);
}
/**
* Method to handle goodbye messages
*/
public boolean handle(IMQConnection con, Packet msg)
throws BrokerException
{
Hashtable props = null;
try {
props = msg.getProperties();
} catch (Exception ex) {
logger.log(Logger.INFO,"Internal Error: unable to retrieve "+
" properties from goodbye message " + msg, ex);
props = new Hashtable();
}
boolean notAuthenticated = !con.isAuthenticated();
if (con.isValid() && notAuthenticated) {
logger.log(Logger.WARNING, Globals.getBrokerResources().getKString(
BrokerResources.W_RECEIVED_GOODBYE_UNAUTHENTICATED_CONN,
con.getConnectionUID().longValue()+"["+con.getRemoteConnectionString()+"]"));
}
Boolean blockprop = (props != null ?(Boolean)props.get("JMQBlock") : null);
boolean block = (blockprop != null && blockprop.booleanValue());
// send the reply (if necessary)
con.stopConnection();
if (block) {
con.cleanupConnection();
}
boolean destroy = false;
if (msg.getSendAcknowledge()) {
Packet pkt = new Packet(con.useDirectBuffers());
pkt.setPacketType(PacketType.GOODBYE_REPLY);
pkt.setConsumerID(msg.getConsumerID());
Hashtable hash = new Hashtable();
hash.put("JMQStatus", new Integer(Status.OK));
if (((IMQBasicConnection)con).getDumpPacket() ||
((IMQBasicConnection)con).getDumpOutPacket())
hash.put("JMQReqID", msg.getSysMessageID().toString());
pkt.setProperties ( hash );
con.sendControlMessage(pkt);
// increase timeout
if (con.isBlocking()) {
if (con instanceof IMQBasicConnection) {
IMQBasicConnection ipCon = (IMQBasicConnection)con;
ipCon.flushControl(timeout);
}
destroy = true;
} else {
con.setDestroyReason(
Globals.getBrokerResources().getKString(
BrokerResources.M_CLIENT_SHUTDOWN));
GoodbyeTask.addConnection(con.getConnectionUID(),
Globals.getBrokerResources().getKString(
BrokerResources.M_CLIENT_SHUTDOWN));
}
} else {
destroy = true;
}
if (destroy) {
con.destroyConnection(false /* no reply */,
GoodbyeReason.CLIENT_CLOSED,
Globals.getBrokerResources().getKString(
BrokerResources.M_CLIENT_SHUTDOWN));
}
return true;
}
}
class GoodbyeTask extends TimerTask
{
static Logger logger = Globals.getLogger();
static GoodbyeTask runner = null;
LinkedList nextSet = new LinkedList();
LinkedList reasonSet = new LinkedList();
static long timeout = 0;
boolean invalid = false;
private static synchronized GoodbyeTask get() {
if (runner == null) {
runner = new GoodbyeTask();
}
return runner;
}
public static void initialize(long ttl)
{
timeout = ttl;
}
private GoodbyeTask() {
if (timeout <= 0)
timeout = 300*1000; // 5 minutes
try {
Globals.getTimer(true).schedule(this, timeout, timeout);
} catch (IllegalStateException ex) {
logger.logStack(Logger.DEBUG,"Timer canceled ", ex);
invalid = true;
}
}
private synchronized void _addCon(ConnectionUID conuid, String reason) {
if (invalid) return;
reasonSet.add(reason);
nextSet.add(conuid);
}
public static void addConnection(ConnectionUID conuid, String reason) {
synchronized (GoodbyeTask.class) {
GoodbyeTask task = runner.get();
try {
runner.get()._addCon(conuid, reason);
} catch (IllegalStateException ex) {
logger.logStack(Logger.DEBUG,"Timer canceled ", ex);
}
}
}
public void run() {
LinkedList list = null;
LinkedList reasonlist = null;
synchronized(this.getClass()) {
synchronized(this) {
if (nextSet.isEmpty()) {
runner.cancel();
runner = null;
} else {
list = nextSet;
reasonlist = reasonSet;
nextSet = new LinkedList();
reasonSet = new LinkedList();
}
}
}
if (list == null) return;
Iterator itr = list.iterator();
while (itr.hasNext()) {
ConnectionUID uid = (ConnectionUID)itr.next();
IMQConnection con = (IMQConnection)Globals.getConnectionManager()
.getConnection(uid);
String reason = null;
try {
reason = (String)reasonlist.removeFirst();
} catch (Exception e) {
logger.log(Logger.DEBUG,"Can't get reason string for destroying connection " + uid);
}
if (reason == null) reason = "REASON NOTFOUND";
if (con != null && con.isValid()) {
try {
con.destroyConnection(false,GoodbyeReason.CLIENT_CLOSED, reason);
} catch (Exception ex) {
logger.logStack(Logger.DEBUG,"error destroying connection " + con , ex);
}
}
itr.remove();
}
}
}