001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mq.sm;
023:
024: import java.util.Collection;
025: import java.util.HashSet;
026: import java.util.Set;
027:
028: import javax.jms.InvalidClientIDException;
029: import javax.jms.InvalidDestinationException;
030: import javax.jms.JMSException;
031: import javax.jms.JMSSecurityException;
032:
033: import org.jboss.mq.DurableSubscriptionID;
034: import org.jboss.mq.SpyTopic;
035: import org.jboss.mq.server.JMSDestinationManager;
036: import org.jboss.mq.server.JMSTopic;
037: import org.jboss.system.ServiceMBeanSupport;
038: import org.jboss.util.JBossStringBuilder;
039:
040: /**
041: * An abstract baseclass to make it a little bit easier to implement new
042: * StateManagers.
043: *
044: * <p>Apart from one methods in StateManager subclasses implement the
045: * protected abstract callback methods to do its work.
046: *
047: * @author <a href="pra@tim.se">Peter Antman </a>
048: * @author <a href="Norbert.Lataille@m4x.org">Norbert Lataille </a>
049: * @author <a href="hiram.chirino@jboss.org">Hiram Chirino </a>
050: * @author <a href="adrian@jboss.com">Adrian Brock </a>
051: * @version $Revision: 62298 $
052: */
053:
054: public abstract class AbstractStateManager extends ServiceMBeanSupport
055: implements StateManager, AbstractStateManagerMBean {
056: /** The logged on client ids */
057: private final Set loggedOnClientIds = new HashSet();
058:
059: /**
060: * Create a new abstract state manager
061: */
062: public AbstractStateManager() {
063: }
064:
065: public void setDurableSubscription(JMSDestinationManager server,
066: DurableSubscriptionID sub, SpyTopic topic)
067: throws JMSException {
068: boolean debug = log.isDebugEnabled();
069: if (debug)
070: log.debug("Checking durable subscription: " + sub
071: + ", on topic: " + topic);
072:
073: DurableSubscription subscription = getDurableSubscription(sub);
074:
075: // A new subscription
076: if (subscription == null) {
077: if (debug)
078: log
079: .debug("The subscription was not previously registered "
080: + sub);
081: // Either this was a remove attemt, not to successfull,
082: // or it was a bogus request (should then an exception be raised?)
083: if (topic == null)
084: return;
085: // Create the real durable subscription
086: JMSTopic dest = (JMSTopic) server.getJMSDestination(topic);
087: if (dest == null)
088: throw new InvalidDestinationException(
089: "Topic does not exist: " + topic);
090: dest.createDurableSubscription(sub);
091:
092: // Save it
093: subscription = new DurableSubscription(sub.getClientID(),
094: sub.getSubscriptionName(), topic.getName(), sub
095: .getSelector());
096: // Call subclass
097: saveDurableSubscription(subscription);
098: }
099: // An existing subscription...it was previously registered...
100: // Check if it is an unsubscribe, or a change of subscription.
101: else {
102: if (debug)
103: log
104: .debug("The subscription was previously registered: "
105: + subscription);
106:
107: String newSelector = sub.getSelector();
108: String oldSelector = subscription.getSelector();
109: boolean selectorChanged = false;
110: if ((newSelector == null && oldSelector != null)
111: || (newSelector != null && newSelector
112: .equals(oldSelector) == false))
113: selectorChanged = true;
114:
115: // The client wants an unsubscribe
116: // TODO: we are not spec compliant since we neither check if
117: // the topic has an active subscriber or if there are messages
118: // destined for the client not yet acked by the session!!!
119: if (topic == null) {
120: if (debug)
121: log.debug("Removing subscription: " + subscription);
122: // we have to change the subscription...do physical work
123: SpyTopic prevTopic = new SpyTopic(subscription
124: .getTopic());
125: JMSTopic dest = (JMSTopic) server
126: .getJMSDestination(prevTopic);
127: if (dest == null)
128: throw new InvalidDestinationException(
129: "Topic does not exist: " + prevTopic);
130: // TODO here we should check if the client still has
131: // an active consumer
132:
133: dest.destroyDurableSubscription(sub);
134:
135: //straight deletion, remove subscription - call subclass
136: removeDurableSubscription(subscription);
137: }
138: // The topic previously subscribed to is not the same as the
139: // one in the subscription request.
140: else if (!subscription.getTopic().equals(topic.getName())
141: || selectorChanged) {
142: //new topic so we have to change it
143: if (debug)
144: log
145: .debug("But the topic or selector was different, changing the subscription.");
146: // remove the old sub
147: SpyTopic prevTopic = new SpyTopic(subscription
148: .getTopic());
149: JMSTopic dest = (JMSTopic) server
150: .getJMSDestination(prevTopic);
151: if (dest == null)
152: throw new InvalidDestinationException(
153: "Previous topic does not exist: "
154: + prevTopic);
155: dest.destroyDurableSubscription(sub);
156:
157: // Create the new
158: dest = (JMSTopic) server.getJMSDestination(topic);
159: if (dest == null)
160: throw new InvalidDestinationException(
161: "Topic does not exist: " + topic);
162: dest.createDurableSubscription(sub);
163:
164: //durable subscription has new topic, save.
165: subscription.setTopic(topic.getName());
166: subscription.setSelector(sub.getSelector());
167: saveDurableSubscription(subscription);
168: }
169: }
170: }
171:
172: public SpyTopic getDurableTopic(DurableSubscriptionID sub)
173: throws JMSException {
174: DurableSubscription subscription = getDurableSubscription(sub);
175: if (subscription == null)
176: throw new InvalidDestinationException(
177: "No durable subscription found for subscription: "
178: + sub.getSubscriptionName());
179:
180: return new SpyTopic(subscription.getTopic());
181: }
182:
183: public String checkUser(String login, String passwd)
184: throws JMSException {
185: String clientId = getPreconfClientId(login, passwd);
186:
187: if (clientId != null) {
188: synchronized (loggedOnClientIds) {
189: if (loggedOnClientIds.contains(clientId)) {
190: throw new JMSSecurityException(
191: "The login id has an assigned client id '"
192: + clientId
193: + "', that is already connected to the server!");
194: }
195: loggedOnClientIds.add(clientId);
196: }
197: }
198:
199: return clientId;
200: }
201:
202: public void addLoggedOnClientId(String ID) throws JMSException {
203: checkLoggedOnClientId(ID);
204:
205: synchronized (loggedOnClientIds) {
206: if (loggedOnClientIds.contains(ID))
207: throw new InvalidClientIDException("This client id '"
208: + ID + "' is already registered!");
209: loggedOnClientIds.add(ID);
210: }
211: if (log.isTraceEnabled())
212: log.trace("Client id '" + ID + "' is logged in.");
213: }
214:
215: public void removeLoggedOnClientId(String ID) {
216: synchronized (loggedOnClientIds) {
217: loggedOnClientIds.remove(ID);
218: }
219: if (log.isTraceEnabled())
220: log.trace("Client id '" + ID + "' is logged out.");
221: }
222:
223: abstract public Collection getDurableSubscriptionIdsForTopic(
224: SpyTopic topic) throws JMSException;
225:
226: /**
227: * Get preconfigured clientID for login/user, and if state manager wants
228: * do authentication. This is NOT recomended when using a SecurityManager.
229: *
230: * @param login the user name
231: * @param passwd the password
232: * @return any preconfigured client id
233: * @throws JMSException for any error
234: */
235: abstract protected String getPreconfClientId(String login,
236: String passwd) throws JMSException;
237:
238: /**
239: * Check if the clientID is allowed to logg in from the particular state
240: * managers perspective.
241: *
242: * @param clientID the client id to check
243: * @throws JMSException for any error
244: */
245: abstract protected void checkLoggedOnClientId(String clientID)
246: throws JMSException;
247:
248: /**
249: * Get a DurableSubscription.
250: *
251: * @param sub the durable subscription id
252: * @return the durable subscription or null if not found
253: * @throws JMSException for any error
254: */
255: abstract protected DurableSubscription getDurableSubscription(
256: DurableSubscriptionID sub) throws JMSException;
257:
258: /**
259: * Add to durable subs and save the subsrcription to persistent storage.<p>
260: *
261: * Called by this class so the sublclass can save. This may be both
262: * a new subscription or a changed one. It is up to the sublcass
263: * to know how to find a changed on. (Only the topic will have changed,
264: * and it is the same DurableSubscription that is saved again that this
265: * class got through getDurableSubscription.
266: *
267: * @param ds the durable subscription to save
268: * @throws JMSException for any error
269: */
270: abstract protected void saveDurableSubscription(
271: DurableSubscription ds) throws JMSException;
272:
273: /**
274: * Remove the subscription and save to persistent storage.<p>
275: *
276: * Called by this class so the sublclass can remove.
277: *
278: * @param ds the durable subscription to save
279: * @throws JMSException for any error
280: */
281: abstract protected void removeDurableSubscription(
282: DurableSubscription ds) throws JMSException;
283:
284: /**
285: * Abstracts the data between a subclass and this class.
286: *
287: * A sublcass can extends this class to ad custom behaviour.
288: */
289: protected class DurableSubscription {
290: /** The client id of the durable subscription */
291: String clientID;
292:
293: /** The subscription name of the durable subscription */
294: String name;
295:
296: /** The topic name of the durable subscription */
297: String topic;
298:
299: /** Any message selector of the durable subscription */
300: String selector;
301:
302: /**
303: * Create a new DurableSubscription.
304: */
305: public DurableSubscription() {
306: }
307:
308: /**
309: * Create a new DurableSubscription.
310: *
311: * @param clientID the client id
312: * @param name the subscription name
313: * @param topic the topic name
314: * @param selector any message selector
315: */
316: public DurableSubscription(String clientID, String name,
317: String topic, String selector) {
318: this .clientID = clientID;
319: this .name = name;
320: this .topic = topic;
321: this .selector = selector;
322: }
323:
324: /**
325: * Get the client id
326: *
327: * @return the client id
328: */
329: public String getClientID() {
330: return clientID;
331: }
332:
333: /**
334: * Get the subcription name
335: *
336: * @return the subscription name
337: */
338: public String getName() {
339: return name;
340: }
341:
342: /**
343: * Get the topic name
344: *
345: * @return the topic name
346: */
347: public String getTopic() {
348: return topic;
349: }
350:
351: /**
352: * Set the topic name
353: *
354: * @param topic the internal name of the topic
355: */
356: public void setTopic(String topic) {
357: this .topic = topic;
358: }
359:
360: /**
361: * Gets the selector.
362: *
363: * @return the selector
364: */
365: public String getSelector() {
366: return selector;
367: }
368:
369: /**
370: * Sets the selector.
371: *
372: * @param selector The selector to set
373: */
374: public void setSelector(String selector) {
375: this .selector = selector;
376: }
377:
378: public String toString() {
379: JBossStringBuilder buffer = new JBossStringBuilder();
380: buffer.append("DurableSub[clientID=").append(clientID);
381: buffer.append(" name=").append(name);
382: buffer.append(" topic=").append(topic);
383: buffer.append(" selector=").append(selector);
384: buffer.append(']');
385: return buffer.toString();
386: }
387: }
388: }
|