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.file;
023:
024: import java.io.BufferedInputStream;
025: import java.io.File;
026: import java.io.FileOutputStream;
027: import java.io.IOException;
028: import java.io.InputStream;
029: import java.io.PrintStream;
030: import java.net.URL;
031: import java.util.ArrayList;
032: import java.util.Collection;
033: import java.util.Enumeration;
034: import java.util.HashSet;
035: import java.util.Set;
036:
037: import javax.jms.InvalidClientIDException;
038: import javax.jms.JMSException;
039: import javax.jms.JMSSecurityException;
040:
041: import org.jboss.mq.DurableSubscriptionID;
042: import org.jboss.mq.SpyJMSException;
043: import org.jboss.mq.SpyTopic;
044: import org.jboss.mq.server.JMSDestinationManager;
045: import org.jboss.mq.server.JMSTopic;
046: import org.jboss.mq.sm.StateManager;
047: import org.jboss.mq.xml.XElement;
048: import org.jboss.mq.xml.XElementException;
049: import org.jboss.system.ServiceMBeanSupport;
050: import org.jboss.system.server.ServerConfigLocator;
051:
052: /**
053: * This class is a simple User Manager. It handles credential issues.
054: *
055: * <p>This is the old state manager.
056: *
057: * @jmx:mbean extends="org.jboss.system.ServiceMBean"
058: *
059: * @author Norbert Lataille (Norbert.Lataille@m4x.org)
060: * @author <a href="hiram.chirino@jboss.org">Hiram Chirino</a>
061: * @author <a href="mailto:pra@tim.se">Peter Antman</a>
062: * @version $Revision: 57198 $
063: */
064: public class OldStateManager extends ServiceMBeanSupport implements
065: StateManager, OldStateManagerMBean {
066: XElement stateConfig;
067:
068: private final Set loggedOnClientIds = new HashSet();
069: private String stateFile = "conf/default/jbossmq-state.xml";
070: private URL systemConfigURL;
071:
072: /**
073: * Constructor for the StateManager object
074: *
075: * @exception XElementException Description of Exception
076: */
077: public OldStateManager() throws XElementException {
078: //loggedOnClientIds = new HashSet();
079: }
080:
081: protected void createService() throws Exception {
082: // Get the system configuration URL
083: systemConfigURL = ServerConfigLocator.locate()
084: .getServerConfigURL();
085: }
086:
087: /**
088: * Sets the StateFile attribute of the StateManagerMBean object
089: *
090: * @jmx:managed-attribute
091: *
092: * @param newStateFile The new StateFile value
093: */
094: public void setStateFile(String newStateFile) {
095: stateFile = newStateFile.trim();
096: }
097:
098: /**
099: * @jmx:managed-attribute
100: */
101: public StateManager getInstance() {
102: return this ;
103: }
104:
105: /**
106: * Sets the DurableSubscription attribute of the StateManager object
107: *
108: * @param server The new DurableSubscription value
109: * @param sub The new DurableSubscription value
110: * @param topic The new DurableSubscription value
111: * @exception JMSException Description of Exception
112: */
113: public void setDurableSubscription(JMSDestinationManager server,
114: DurableSubscriptionID sub, SpyTopic topic)
115: throws JMSException {
116: boolean debug = log.isDebugEnabled();
117: if (debug)
118: log.debug("Checking durable subscription: " + sub
119: + ", on topic: " + topic);
120: try {
121: //Set the known Ids
122: Enumeration enumeration = stateConfig
123: .getElementsNamed("User");
124: while (enumeration.hasMoreElements()) {
125:
126: // Match the User.Name
127: XElement user = (XElement) enumeration.nextElement();
128: if (!user.containsField("Id")
129: || !user.getField("Id").equals(
130: sub.getClientID())) {
131: continue;
132: }
133:
134: if (debug)
135: log
136: .debug("Found a matching ClientID configuration section.");
137:
138: XElement subscription = null;
139:
140: // Match the User/DurableSubscription.Name
141: Enumeration enum2 = user
142: .getElementsNamed("DurableSubscription");
143: while (enum2.hasMoreElements()) {
144: XElement t = (XElement) enum2.nextElement();
145: if (t.getField("Name").equals(
146: sub.getSubscriptionName())) {
147: subscription = t;
148: break;
149: }
150: }
151:
152: // A new subscription
153: if (subscription == null) {
154: if (debug)
155: log
156: .debug("The subscription was not previously registered.");
157: // it was not previously registered...
158: if (topic == null) {
159: return;
160: }
161:
162: JMSTopic dest = (JMSTopic) server
163: .getJMSDestination(topic);
164: dest.createDurableSubscription(sub);
165:
166: subscription = new XElement("DurableSubscription");
167: subscription.addField("Name", sub
168: .getSubscriptionName());
169: subscription.addField("TopicName", topic.getName());
170: user.addElement(subscription);
171:
172: saveConfig();
173:
174: }
175: // An existing subscription...it was previously registered...
176: // Check if it is an unsubscribe, or a change of subscription.
177: else {
178: if (debug)
179: log
180: .debug("The subscription was previously registered.");
181:
182: // The client wants an unsubscribe
183: // TODO: we are not spec compliant since we neither check if
184: // the topic has an active subscriber or if there are messages
185: // destined for the client not yet acked by the session!!!
186: if (topic == null) {
187: // we have to change the subscription...
188: SpyTopic prevTopic = new SpyTopic(subscription
189: .getField("TopicName"));
190: JMSTopic dest = (JMSTopic) server
191: .getJMSDestination(prevTopic);
192: // TODO here we should check if the client still has
193: // an active consumer
194:
195: dest.destroyDurableSubscription(sub);
196:
197: //straight deletion, remove subscription
198: subscription.removeFromParent();
199: saveConfig();
200: }
201: // The topic previously subscribed to is not the same as the
202: // one in the subscription request.
203: // TODO: we do currently no check if the selector has changed.
204: else if (!subscription.getField("TopicName")
205: .equals(topic.getName())) {
206: //new topic so we have to change it
207: if (debug)
208: log
209: .debug("But the topic was different, changing the subscription.");
210: // we have to change the subscription...
211: SpyTopic prevTopic = new SpyTopic(subscription
212: .getField("TopicName"));
213: JMSTopic dest = (JMSTopic) server
214: .getJMSDestination(prevTopic);
215: dest.destroyDurableSubscription(sub);
216:
217: dest = (JMSTopic) server
218: .getJMSDestination(topic);
219: dest.createDurableSubscription(sub);
220:
221: //durable subscription has new topic
222: subscription.setField("TopicName", topic
223: .getName());
224: saveConfig();
225: }
226: }
227: return;
228: }
229:
230: // Could not find that user..
231: throw new JMSException("ClientID '" + sub.getClientID()
232: + "' cannot create durable subscriptions.");
233: } catch (IOException e) {
234: JMSException newE = new SpyJMSException(
235: "Could not setup the durable subscription");
236: newE.setLinkedException(e);
237: throw newE;
238: } catch (XElementException e) {
239: JMSException newE = new SpyJMSException(
240: "Could not setup the durable subscription");
241: newE.setLinkedException(e);
242: throw newE;
243: }
244:
245: }
246:
247: /**
248: * Get the destination a subscription is for.
249: */
250: public SpyTopic getDurableTopic(DurableSubscriptionID sub)
251: throws JMSException {
252: try {
253: XElement subscription = getSubscription(sub);
254: if (subscription == null)
255: throw new JMSException(
256: "No durable subscription found for subscription: "
257: + sub.getSubscriptionName());
258:
259: return new SpyTopic(subscription.getField("TopicName"));
260: } catch (XElementException e) {
261: JMSException newE = new SpyJMSException(
262: "Could not find durable subscription");
263: newE.setLinkedException(e);
264: throw newE;
265: }
266: }
267:
268: private XElement getSubscription(DurableSubscriptionID sub)
269: throws JMSException, XElementException {
270: boolean debug = log.isDebugEnabled();
271:
272: //Set the known Ids
273: XElement subscription = null;
274: Enumeration enumeration = stateConfig.getElementsNamed("User");
275: while (enumeration.hasMoreElements()) {
276:
277: // Match the User.Name
278: XElement user = (XElement) enumeration.nextElement();
279: if (!user.containsField("Id")
280: || !user.getField("Id").equals(sub.getClientID())) {
281: continue;
282: }
283:
284: if (debug)
285: log
286: .debug("Found a matching ClientID configuration section.");
287:
288: //XElement subscription = null;
289:
290: // Match the User/DurableSubscription.Name
291: Enumeration enum2 = user
292: .getElementsNamed("DurableSubscription");
293: while (enum2.hasMoreElements()) {
294: XElement t = (XElement) enum2.nextElement();
295: if (t.getField("Name")
296: .equals(sub.getSubscriptionName())) {
297: subscription = t;
298: //break;
299: return subscription;
300: }
301: }
302: }
303: // Nothing found will be null
304: return subscription;
305: }
306:
307: /**
308: * Gets the StateFile attribute of the StateManagerMBean object
309: *
310: * @jmx:managed-attribute
311: *
312: * @return The StateFile value
313: */
314: public String getStateFile() {
315: return stateFile;
316: }
317:
318: /**
319: * #Description of the Method
320: *
321: * @param login Description of Parameter
322: * @param passwd Description of Parameter
323: * @return Description of the Returned Value
324: * @exception JMSException Description of Exception
325: */
326: public String checkUser(String login, String passwd)
327: throws JMSException {
328: try {
329: synchronized (stateConfig) {
330:
331: Enumeration enumeration = stateConfig
332: .getElementsNamed("User");
333: while (enumeration.hasMoreElements()) {
334: XElement element = (XElement) enumeration
335: .nextElement();
336: String name = element.getField("Name");
337: if (!name.equals(login)) {
338: continue;
339: }
340:
341: String pw = element.getField("Password");
342: if (!passwd.equals(pw)) {
343: throw new JMSException("Bad password");
344: }
345:
346: String clientId = null;
347: if (element.containsField("Id")) {
348: clientId = element.getField("Id");
349: }
350:
351: if (clientId != null) {
352: synchronized (loggedOnClientIds) {
353: if (loggedOnClientIds.contains(clientId)) {
354: throw new JMSSecurityException(
355: "The login id has an assigned client id. "
356: + "That client id is already connected to the server!");
357: }
358: loggedOnClientIds.add(clientId);
359: }
360: }
361:
362: return clientId;
363: }
364: throw new JMSSecurityException(
365: "This user does not exist");
366: }
367: } catch (XElementException e) {
368: log.error(e);
369: throw new JMSException("Invalid server user configuration.");
370: }
371: }
372:
373: /**
374: * Ad a logged in clientID to the statemanager.
375: *
376: * The clientID must not be active, and it must not be password protected.
377: *
378: * @param ID The feature to be added to the LoggedOnClientId
379: * attribute
380: * @exception JMSException Description of Exception
381: */
382: public void addLoggedOnClientId(String ID) throws JMSException {
383:
384: //Check : this ID must not be registered
385: synchronized (loggedOnClientIds) {
386: if (loggedOnClientIds.contains(ID)) {
387: throw new InvalidClientIDException(
388: "This loggedOnClientIds is already registered !");
389: }
390: }
391:
392: //Check : this ID must not be password protected
393: synchronized (stateConfig) {
394: Enumeration enumeration = stateConfig
395: .getElementsNamed("User");
396: while (enumeration.hasMoreElements()) {
397: XElement element = (XElement) enumeration.nextElement();
398: try {
399: if (element.containsField("Id")
400: && element.getField("Id").equals(ID)) {
401: throw new InvalidClientIDException(
402: "This loggedOnClientIds is password protected !");
403: }
404: } catch (XElementException ignore) {
405: }
406: }
407:
408: }
409: synchronized (loggedOnClientIds) {
410: loggedOnClientIds.add(ID);
411: }
412: }
413:
414: /**
415: * #Description of the Method
416: *
417: * @param ID Description of Parameter
418: */
419: public void removeLoggedOnClientId(String ID) {
420: synchronized (loggedOnClientIds) {
421: loggedOnClientIds.remove(ID);
422: }
423: }
424:
425: /**
426: * #Description of the Method
427: *
428: * @exception Exception Description of Exception
429: */
430: public void startService() throws Exception {
431:
432: loadConfig();
433: }
434:
435: public Collection getDurableSubscriptionIdsForTopic(SpyTopic topic)
436: throws JMSException {
437: Collection durableSubs = new ArrayList();
438: try {
439: Enumeration enumeration = stateConfig
440: .getElementsNamed("User/DurableSubscription");
441: while (enumeration.hasMoreElements()) {
442: XElement element = (XElement) enumeration.nextElement();
443:
444: String clientId = element.getField("../Id");
445: String name = element.getField("Name");
446: String topicName = element.getField("TopicName");
447: if (topic.getName().equals(topicName)) {
448: durableSubs.add(new DurableSubscriptionID(clientId,
449: name, null));
450: } // end of if ()
451:
452: }
453: } catch (XElementException e) {
454: JMSException jmse = new JMSException(
455: "Error in statemanager xml");
456: jmse.setLinkedException(e);
457: throw jmse;
458: } // end of try-catch
459: return durableSubs;
460: }
461:
462: /**
463: * #Description of the Method
464: *
465: * @param server Description of Parameter
466: * @exception XElementException Description of Exception
467: */
468: /*
469: public void initDurableSubscriptions(JMSServer server) throws XElementException
470: {
471:
472: //Set the known Ids
473: Enumeration enum = stateConfig.getElementsNamed("User/DurableSubscription");
474: while (enum.hasMoreElements())
475: {
476: XElement element = (XElement)enum.nextElement();
477:
478: String clientId = element.getField("../Id");
479: String name = element.getField("Name");
480: String topicName = element.getField("TopicName");
481:
482: try
483: {
484:
485: log.debug("Restarting Durable Subscription: " + clientId + "," + name + "," + topicName);
486: SpyTopic topic = new SpyTopic(topicName);
487: JMSTopic dest = (JMSTopic)server.getJMSDestination(topic);
488: if (dest == null)
489: {
490: log.warn("Subscription topic of not found: " + topicName);
491: log.warn("Subscription cannot be initialized: " + clientId + "," + name);
492: element.removeFromParent();
493: }
494: else
495: {
496: dest.createDurableSubscription(new DurableSubscriptionID(clientId, name));
497: }
498:
499: }
500: catch (JMSException e)
501: {
502: log.error("Could not initialize a durable subscription for : Client Id=" + clientId + ", Name=" + name + ", Topic Name=" + topicName, e);
503: }
504: }
505:
506: }
507: */
508:
509: /**
510: * @jmx:managed-operation
511: */
512: public void loadConfig() throws IOException, XElementException {
513: URL configURL = new URL(systemConfigURL, stateFile);
514: if (log.isDebugEnabled()) {
515: log.debug("Loading config from: " + configURL);
516: }
517:
518: InputStream in = new BufferedInputStream(configURL.openStream());
519: try {
520: stateConfig = XElement.createFrom(in);
521: } finally {
522: in.close();
523: }
524: }
525:
526: /**
527: * @jmx:managed-operation
528: */
529: public void saveConfig() throws IOException {
530: URL configURL = new URL(systemConfigURL, stateFile);
531:
532: if (configURL.getProtocol().equals("file")) {
533: File file = new File(configURL.getFile());
534: if (log.isDebugEnabled()) {
535: log.debug("Saving config to: " + file);
536: }
537:
538: PrintStream stream = new PrintStream(new FileOutputStream(
539: file));
540: try {
541: stream.print(stateConfig.toXML(true));
542: } finally {
543: stream.close();
544: }
545: } else {
546: log.error("Can not save configuration to non-file URL: "
547: + configURL);
548: }
549: }
550:
551: /**
552: * @jmx:managed-operation
553: */
554: public String displayStateConfig() throws Exception {
555: return stateConfig.toString();
556: }
557:
558: /*
559: Does not seem to be used
560: public class Identity
561: {
562: String login;
563: String passwd;
564: String loggedOnClientIds;
565:
566: Identity(final String login,
567: final String passwd,
568: final String loggedOnClientIds)
569: {
570: this.login = login;
571: this.passwd = passwd;
572: this.loggedOnClientIds = loggedOnClientIds;
573: }
574:
575: }
576: */
577: }
|