/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.hawtjms.provider.stomp;
import io.hawtjms.jms.meta.JmsConsumerId;
import io.hawtjms.jms.meta.JmsConsumerInfo;
import io.hawtjms.jms.meta.JmsProducerId;
import io.hawtjms.jms.meta.JmsProducerInfo;
import io.hawtjms.jms.meta.JmsSessionId;
import io.hawtjms.jms.meta.JmsSessionInfo;
import io.hawtjms.provider.AsyncResult;
import io.hawtjms.provider.ProviderRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import javax.jms.JMSException;
/**
* Represents a logical Session instance for a STOMP connection.
*/
public class StompSession {
private final StompConnection connection;
private final JmsSessionInfo sessionInfo;
private final Map<JmsProducerId, StompProducer> producers = new HashMap<JmsProducerId, StompProducer>();
private final Map<JmsConsumerId, StompConsumer> consumers = new HashMap<JmsConsumerId, StompConsumer>();
public StompSession(StompConnection connection, JmsSessionInfo sessionInfo) {
this.connection = connection;
this.sessionInfo = sessionInfo;
}
/**
* @return the StompConnection that created this Session.
*/
public StompConnection getConnection() {
return this.connection;
}
/**
* @return the assigned JmsSessionId that identifies this Session.
*/
public JmsSessionId getSessionId() {
return this.sessionInfo.getSessionId();
}
/**
* Close this logical session ensuring that all resources associated with it are also
* closed.
*
* @param request
* the AsyncResult that awaits the close of this session.
*
* @throws IOException if an error occurs while closing a consumer.
*/
public void close(AsyncResult<Void> request) throws IOException {
if (consumers.isEmpty()) {
request.onSuccess();
}
AggregateResult pending = new AggregateResult(consumers.size(), request);
for (StompConsumer consumer : consumers.values()) {
consumer.close(pending);
}
}
/**
* Creates and returns a new StompProducer which represents a logical mapping of
* a JmsProducer to a STOMP destination. There is no information exchange between
* client and peer on producer create so the producer create is ready immediately.
*
* @param producerInfo
* the information object describing the producer and its configuration.
* @param request
* the asynchronous request that is waiting for this action to complete.
*/
public void createProducer(JmsProducerInfo producerInfo, AsyncResult<Void> request) {
StompProducer producer = new StompProducer(this, producerInfo);
producers.put(producerInfo.getProducerId(), producer);
request.onSuccess();
}
/**
* Removes the producer associated with the given JmsProducerId from the producers map.
*
* @param producerId
* the producer Id that should be removed.
*/
public void removeProducer(JmsProducerId producerId) {
producers.remove(producerId);
}
/**
* Creates and returns a new StompConsumer which maps a STOMP subscription to a
* JMS framework MessageConsumer instance.
*
* @param consumerInfo
* the information object describing the consumer and its configuration.
* @param request
* the asynchronous request that is waiting for this action to complete.
*
* @return a newly created StompConsumer instance.
*
* @throws IOException if an error occurs on sending the subscribe request.
* @throws JMSException if there is an error creating the requested type of subscription.
*/
public void createConsumer(JmsConsumerInfo consumerInfo, AsyncResult<Void> request) throws JMSException, IOException {
if (consumerInfo.getPrefetchSize() == 0) {
throw new JMSException("Cannot create a consumer with Zero Prefetch in STOMP");
}
StompConsumer consumer;
if (consumerInfo.isBrowser()) {
consumer = new StompQueueBrowser(this, consumerInfo);
} else {
consumer = new StompConsumer(this, consumerInfo);
}
consumers.put(consumerInfo.getConsumerId(), consumer);
consumer.subscribe(request);
}
/**
* Removes the producer associated with the given JmsProducerId from the producers map.
*
* @param consumerId
* the consumer Id that is to be removed from the consumers map.
*/
public void removeConsumer(JmsConsumerId consumerId) {
consumers.remove(consumerId);
}
/**
* For all consumers in this session force an acknowledge of all unacknowledged messages.
*
* @param request
* the request that awaits the completion of this operation.
*
* @throws IOException if an error occurs sending any ACK frames.
*/
public void acknowledge(AsyncResult<Void> request) throws IOException {
AggregateResult pending = new AggregateResult(consumers.size(), request);
for (StompConsumer consumer : consumers.values()) {
consumer.acknowledge(pending);
}
}
/**
*
*/
public void recover() {
// TODO Auto-generated method stub
}
/**
* @param request
*/
public void rollback(AsyncResult<Void> request) {
// TODO Auto-generated method stub
}
/**
* @param request
*/
public void commit(AsyncResult<Void> request) {
// TODO Auto-generated method stub
}
public StompProducer getProducer(JmsProducerInfo producerInfo) {
return getProducer(producerInfo.getProducerId());
}
public StompProducer getProducer(JmsProducerId producerId) {
if (producerId.getProviderHint() instanceof StompProducer) {
return (StompProducer) producerId.getProviderHint();
}
return this.producers.get(producerId);
}
public StompConsumer getConsumer(JmsConsumerInfo consumerInfo) {
return getConsumer(consumerInfo.getConsumerId());
}
public StompConsumer getConsumer(JmsConsumerId consumerId) {
if (consumerId.getProviderHint() instanceof StompConsumer) {
return (StompConsumer) consumerId.getProviderHint();
}
return this.consumers.get(consumerId);
}
//---------- Internal Utilities ------------------------------------------//
/*
* Implements an accumulating result monitor that watches for all active consumers
* at the time of the Session Acknowledge call to complete the acknowledge of all
* their delivered messages.
*/
private static class AggregateResult extends ProviderRequest<Void> {
private final AtomicInteger consumers;
public AggregateResult(int numConsumers, AsyncResult<Void> request) {
super(request);
this.consumers = new AtomicInteger(numConsumers);
}
@Override
public void onSuccess(Void result) {
if (consumers.decrementAndGet() == 0) {
super.onSuccess(result);
}
}
}
}