Source Code Cross Referenced for JID.java in  » Net » openfire » org » xmpp » packet » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Net » openfire » org.xmpp.packet 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /**
002:         * Copyright (C) 2004-2007 Jive Software. All rights reserved.
003:         *
004:         * This software is published under the terms of the GNU Public License (GPL),
005:         * a copy of which is included in this distribution.
006:         */package org.xmpp.packet;
007:
008:        import org.jivesoftware.stringprep.IDNA;
009:        import org.jivesoftware.stringprep.Stringprep;
010:        import org.jivesoftware.stringprep.StringprepException;
011:        import org.jivesoftware.util.cache.ExternalizableUtil;
012:
013:        import java.io.Externalizable;
014:        import java.io.IOException;
015:        import java.io.ObjectInput;
016:        import java.io.ObjectOutput;
017:        import java.util.Collections;
018:        import java.util.LinkedHashMap;
019:        import java.util.Map;
020:
021:        /**
022:         * An XMPP address (JID). A JID is made up of a node (generally a username), a domain,
023:         * and a resource. The node and resource are optional; domain is required. In simple
024:         * ABNF form:
025:         *
026:         * <ul><tt>jid = [ node "@" ] domain [ "/" resource ]</tt></ul>
027:         *
028:         * Some sample JID's:
029:         * <ul>
030:         *      <li><tt>user@example.com</tt></li>
031:         *      <li><tt>user@example.com/home</tt></li>
032:         *      <li><tt>example.com</tt></li>
033:         * </ul>
034:         *
035:         * Each allowable portion of a JID (node, domain, and resource) must not be more
036:         * than 1023 bytes in length, resulting in a maximum total size (including the '@'
037:         * and '/' separators) of 3071 bytes.
038:         *
039:         * @author Matt Tucker
040:         */
041:        public class JID implements  Comparable<JID>, Externalizable {
042:
043:            // Stringprep operations are very expensive. Therefore, we cache node, domain and
044:            // resource values that have already had stringprep applied so that we can check
045:            // incoming values against the cache.
046:            private static Map<String, Object> stringprepCache = Collections
047:                    .synchronizedMap(new Cache(10000));
048:
049:            private String node;
050:            private String domain;
051:            private String resource;
052:
053:            private String cachedFullJID;
054:            private String cachedBareJID;
055:
056:            /**
057:             * Escapes the node portion of a JID according to "JID Escaping" (JEP-0106).
058:             * Escaping replaces characters prohibited by node-prep with escape sequences,
059:             * as follows:<p>
060:             *
061:             * <table border="1">
062:             * <tr><td><b>Unescaped Character</b></td><td><b>Encoded Sequence</b></td></tr>
063:             * <tr><td>&lt;space&gt;</td><td>\20</td></tr>
064:             * <tr><td>"</td><td>\22</td></tr>
065:             * <tr><td>&</td><td>\26</td></tr>
066:             * <tr><td>'</td><td>\27</td></tr>
067:             * <tr><td>/</td><td>\2f</td></tr>
068:             * <tr><td>:</td><td>\3a</td></tr>
069:             * <tr><td>&lt;</td><td>\3c</td></tr>
070:             * <tr><td>&gt;</td><td>\3e</td></tr>
071:             * <tr><td>@</td><td>\40</td></tr>
072:             * <tr><td>\</td><td>\5c</td></tr>
073:             * </table><p>
074:             *
075:             * This process is useful when the node comes from an external source that doesn't
076:             * conform to nodeprep. For example, a username in LDAP may be "Joe Smith". Because
077:             * the &lt;space&gt; character isn't a valid part of a node, the username should
078:             * be escaped to "Joe\20Smith" before being made into a JID (e.g. "joe\20smith@example.com"
079:             * after case-folding, etc. has been applied).<p>
080:             *
081:             * All node escaping and un-escaping must be performed manually at the appropriate
082:             * time; the JID class will not escape or un-escape automatically.
083:             *
084:             * @param node the node.
085:             * @return the escaped version of the node.
086:             */
087:            public static String escapeNode(String node) {
088:                if (node == null) {
089:                    return null;
090:                }
091:                StringBuilder buf = new StringBuilder(node.length() + 8);
092:                for (int i = 0, n = node.length(); i < n; i++) {
093:                    char c = node.charAt(i);
094:                    switch (c) {
095:                    case '"':
096:                        buf.append("\\22");
097:                        break;
098:                    case '&':
099:                        buf.append("\\26");
100:                        break;
101:                    case '\'':
102:                        buf.append("\\27");
103:                        break;
104:                    case '/':
105:                        buf.append("\\2f");
106:                        break;
107:                    case ':':
108:                        buf.append("\\3a");
109:                        break;
110:                    case '<':
111:                        buf.append("\\3c");
112:                        break;
113:                    case '>':
114:                        buf.append("\\3e");
115:                        break;
116:                    case '@':
117:                        buf.append("\\40");
118:                        break;
119:                    case '\\':
120:                        buf.append("\\5c");
121:                        break;
122:                    default: {
123:                        if (Character.isWhitespace(c)) {
124:                            buf.append("\\20");
125:                        } else {
126:                            buf.append(c);
127:                        }
128:                    }
129:                    }
130:                }
131:                return buf.toString();
132:            }
133:
134:            /**
135:             * Un-escapes the node portion of a JID according to "JID Escaping" (JEP-0106).<p>
136:             * Escaping replaces characters prohibited by node-prep with escape sequences,
137:             * as follows:<p>
138:             *
139:             * <table border="1">
140:             * <tr><td><b>Unescaped Character</b></td><td><b>Encoded Sequence</b></td></tr>
141:             * <tr><td>&lt;space&gt;</td><td>\20</td></tr>
142:             * <tr><td>"</td><td>\22</td></tr>
143:             * <tr><td>&</td><td>\26</td></tr>
144:             * <tr><td>'</td><td>\27</td></tr>
145:             * <tr><td>/</td><td>\2f</td></tr>
146:             * <tr><td>:</td><td>\3a</td></tr>
147:             * <tr><td>&lt;</td><td>\3c</td></tr>
148:             * <tr><td>&gt;</td><td>\3e</td></tr>
149:             * <tr><td>@</td><td>\40</td></tr>
150:             * <tr><td>\</td><td>\5c</td></tr>
151:             * </table><p>
152:             *
153:             * This process is useful when the node comes from an external source that doesn't
154:             * conform to nodeprep. For example, a username in LDAP may be "Joe Smith". Because
155:             * the &lt;space&gt; character isn't a valid part of a node, the username should
156:             * be escaped to "Joe\20Smith" before being made into a JID (e.g. "joe\20smith@example.com"
157:             * after case-folding, etc. has been applied).<p>
158:             *
159:             * All node escaping and un-escaping must be performed manually at the appropriate
160:             * time; the JID class will not escape or un-escape automatically.
161:             *
162:             * @param node the escaped version of the node.
163:             * @return the un-escaped version of the node.
164:             */
165:            public static String unescapeNode(String node) {
166:                if (node == null) {
167:                    return null;
168:                }
169:                char[] nodeChars = node.toCharArray();
170:                StringBuilder buf = new StringBuilder(nodeChars.length);
171:                for (int i = 0, n = nodeChars.length; i < n; i++) {
172:                    compare: {
173:                        char c = node.charAt(i);
174:                        if (c == '\\' && i + 2 < n) {
175:                            char c2 = nodeChars[i + 1];
176:                            char c3 = nodeChars[i + 2];
177:                            if (c2 == '2') {
178:                                switch (c3) {
179:                                case '0':
180:                                    buf.append(' ');
181:                                    i += 2;
182:                                    break compare;
183:                                case '2':
184:                                    buf.append('"');
185:                                    i += 2;
186:                                    break compare;
187:                                case '6':
188:                                    buf.append('&');
189:                                    i += 2;
190:                                    break compare;
191:                                case '7':
192:                                    buf.append('\'');
193:                                    i += 2;
194:                                    break compare;
195:                                case 'f':
196:                                    buf.append('/');
197:                                    i += 2;
198:                                    break compare;
199:                                }
200:                            } else if (c2 == '3') {
201:                                switch (c3) {
202:                                case 'a':
203:                                    buf.append(':');
204:                                    i += 2;
205:                                    break compare;
206:                                case 'c':
207:                                    buf.append('<');
208:                                    i += 2;
209:                                    break compare;
210:                                case 'e':
211:                                    buf.append('>');
212:                                    i += 2;
213:                                    break compare;
214:                                }
215:                            } else if (c2 == '4') {
216:                                if (c3 == '0') {
217:                                    buf.append("@");
218:                                    i += 2;
219:                                    break compare;
220:                                }
221:                            } else if (c2 == '5') {
222:                                if (c3 == 'c') {
223:                                    buf.append("\\");
224:                                    i += 2;
225:                                    break compare;
226:                                }
227:                            }
228:                        }
229:                        buf.append(c);
230:                    }
231:                }
232:                return buf.toString();
233:            }
234:
235:            public static String resourceprep(String resource)
236:                    throws StringprepException {
237:                String answer = resource;
238:                if (!stringprepCache.containsKey(resource)) {
239:                    answer = Stringprep.resourceprep(resource);
240:                    // Validate field is not greater than 1023 bytes. UTF-8 characters use two bytes.
241:                    if (answer != null && answer.length() * 2 > 1023) {
242:                        return answer;
243:                    }
244:                    stringprepCache.put(answer, null);
245:                }
246:                return answer;
247:            }
248:
249:            /**
250:             * Constructor added for Externalizable. Do not use this constructor.
251:             */
252:            public JID() {
253:            }
254:
255:            /**
256:             * Constructs a JID from it's String representation.
257:             *
258:             * @param jid a valid JID.
259:             * @throws IllegalArgumentException if the JID is not valid.
260:             */
261:            public JID(String jid) {
262:                if (jid == null) {
263:                    throw new NullPointerException("JID cannot be null");
264:                }
265:                String[] parts = getParts(jid);
266:
267:                init(parts[0], parts[1], parts[2]);
268:            }
269:
270:            /**
271:             * Constructs a JID given a node, domain, and resource.
272:             *
273:             * @param node the node.
274:             * @param domain the domain, which must not be <tt>null</tt>.
275:             * @param resource the resource.
276:             * @throws IllegalArgumentException if the JID is not valid.
277:             */
278:            public JID(String node, String domain, String resource) {
279:                if (domain == null) {
280:                    throw new NullPointerException("Domain cannot be null");
281:                }
282:                init(node, domain, resource);
283:            }
284:
285:            /**
286:             * Constructs a JID given a node, domain, and resource being able to specify if stringprep
287:             * should be applied or not.
288:             *
289:             * @param node the node.
290:             * @param domain the domain, which must not be <tt>null</tt>.
291:             * @param resource the resource.
292:             * @param skipStringprep true if stringprep should not be applied.
293:             * @throws IllegalArgumentException if the JID is not valid.
294:             */
295:            public JID(String node, String domain, String resource,
296:                    boolean skipStringprep) {
297:                if (domain == null) {
298:                    throw new NullPointerException("Domain cannot be null");
299:                }
300:                if (skipStringprep) {
301:                    this .node = node;
302:                    this .domain = domain;
303:                    this .resource = resource;
304:                    // Cache the bare and full JID String representation
305:                    updateCache();
306:                } else {
307:                    init(node, domain, resource);
308:                }
309:            }
310:
311:            /**
312:             * Returns a String array with the parsed node, domain and resource.
313:             * No Stringprep is performed while parsing the textual representation.
314:             *
315:             * @param jid the textual JID representation.
316:             * @return a string array with the parsed node, domain and resource.
317:             */
318:            static String[] getParts(String jid) {
319:                String[] parts = new String[3];
320:                String node = null, domain, resource;
321:                if (jid == null) {
322:                    return parts;
323:                }
324:
325:                int atIndex = jid.indexOf("@");
326:                int slashIndex = jid.indexOf("/");
327:
328:                // Node
329:                if (atIndex > 0) {
330:                    node = jid.substring(0, atIndex);
331:                }
332:
333:                // Domain
334:                if (atIndex + 1 > jid.length()) {
335:                    throw new IllegalArgumentException(
336:                            "JID with empty domain not valid");
337:                }
338:                if (atIndex < 0) {
339:                    if (slashIndex > 0) {
340:                        domain = jid.substring(0, slashIndex);
341:                    } else {
342:                        domain = jid;
343:                    }
344:                } else {
345:                    if (slashIndex > 0) {
346:                        domain = jid.substring(atIndex + 1, slashIndex);
347:                    } else {
348:                        domain = jid.substring(atIndex + 1);
349:                    }
350:                }
351:
352:                // Resource
353:                if (slashIndex + 1 > jid.length() || slashIndex < 0) {
354:                    resource = null;
355:                } else {
356:                    resource = jid.substring(slashIndex + 1);
357:                }
358:                parts[0] = node;
359:                parts[1] = domain;
360:                parts[2] = resource;
361:                return parts;
362:            }
363:
364:            /**
365:             * Transforms the JID parts using the appropriate Stringprep profiles, then
366:             * validates them. If they are fully valid, the field values are saved, otherwise
367:             * an IllegalArgumentException is thrown.
368:             *
369:             * @param node the node.
370:             * @param domain the domain.
371:             * @param resource the resource.
372:             */
373:            private void init(String node, String domain, String resource) {
374:                // Set node and resource to null if they are the empty string.
375:                if (node != null && node.equals("")) {
376:                    node = null;
377:                }
378:                if (resource != null && resource.equals("")) {
379:                    resource = null;
380:                }
381:                // Stringprep (node prep, resourceprep, etc).
382:                try {
383:                    if (!stringprepCache.containsKey(node)) {
384:                        this .node = Stringprep.nodeprep(node);
385:                        // Validate field is not greater than 1023 bytes. UTF-8 characters use two bytes.
386:                        if (this .node != null && this .node.length() * 2 > 1023) {
387:                            throw new IllegalArgumentException(
388:                                    "Node cannot be larger than 1023 bytes. "
389:                                            + "Size is "
390:                                            + (this .node.length() * 2)
391:                                            + " bytes.");
392:                        }
393:                        stringprepCache.put(this .node, null);
394:                    } else {
395:                        this .node = node;
396:                    }
397:                    // XMPP specifies that domains should be run through IDNA and
398:                    // that they should be run through nameprep before doing any
399:                    // comparisons. We always run the domain through nameprep to
400:                    // make comparisons easier later.
401:                    if (!stringprepCache.containsKey(domain)) {
402:                        this .domain = Stringprep.nameprep(IDNA.toASCII(domain),
403:                                false);
404:                        // Validate field is not greater than 1023 bytes. UTF-8 characters use two bytes.
405:                        if (this .domain.length() * 2 > 1023) {
406:                            throw new IllegalArgumentException(
407:                                    "Domain cannot be larger than 1023 bytes. "
408:                                            + "Size is "
409:                                            + (this .domain.length() * 2)
410:                                            + " bytes.");
411:                        }
412:                        stringprepCache.put(this .domain, null);
413:                    } else {
414:                        this .domain = domain;
415:                    }
416:                    this .resource = resourceprep(resource);
417:                    // Validate field is not greater than 1023 bytes. UTF-8 characters use two bytes.
418:                    if (resource != null && resource.length() * 2 > 1023) {
419:                        throw new IllegalArgumentException(
420:                                "Resource cannot be larger than 1023 bytes. "
421:                                        + "Size is " + (resource.length() * 2)
422:                                        + " bytes.");
423:                    }
424:                    // Cache the bare and full JID String representation
425:                    updateCache();
426:                } catch (Exception e) {
427:                    StringBuilder buf = new StringBuilder();
428:                    if (node != null) {
429:                        buf.append(node).append("@");
430:                    }
431:                    buf.append(domain);
432:                    if (resource != null) {
433:                        buf.append("/").append(resource);
434:                    }
435:                    throw new IllegalArgumentException("Illegal JID: "
436:                            + buf.toString(), e);
437:                }
438:            }
439:
440:            private void updateCache() {
441:                // Cache the bare JID
442:                StringBuilder buf = new StringBuilder(40);
443:                if (node != null) {
444:                    buf.append(node).append("@");
445:                }
446:                buf.append(domain);
447:                cachedBareJID = buf.toString();
448:
449:                // Cache the full JID
450:                if (resource != null) {
451:                    buf.append("/").append(resource);
452:                    cachedFullJID = buf.toString();
453:                } else {
454:                    cachedFullJID = cachedBareJID;
455:                }
456:            }
457:
458:            /**
459:             * Returns the node, or <tt>null</tt> if this JID does not contain node information.
460:             *
461:             * @return the node.
462:             */
463:            public String getNode() {
464:                return node;
465:            }
466:
467:            /**
468:             * Returns the domain.
469:             *
470:             * @return the domain.
471:             */
472:            public String getDomain() {
473:                return domain;
474:            }
475:
476:            /**
477:             * Returns the resource, or <tt>null</tt> if this JID does not contain resource information.
478:             *
479:             * @return the resource.
480:             */
481:            public String getResource() {
482:                return resource;
483:            }
484:
485:            /**
486:             * Returns the String representation of the bare JID, which is the JID with
487:             * resource information removed.
488:             *
489:             * @return the bare JID.
490:             */
491:            public String toBareJID() {
492:                return cachedBareJID;
493:            }
494:
495:            /**
496:             * Returns a String representation of the JID.
497:             *
498:             * @return a String representation of the JID.
499:             */
500:            public String toString() {
501:                return cachedFullJID;
502:            }
503:
504:            public int hashCode() {
505:                return toString().hashCode();
506:            }
507:
508:            public boolean equals(Object object) {
509:                if (!(object instanceof  JID)) {
510:                    return false;
511:                }
512:                if (this  == object) {
513:                    return true;
514:                }
515:                JID jid = (JID) object;
516:                // Node. If node isn't null, compare.
517:                if (node != null) {
518:                    if (!node.equals(jid.node)) {
519:                        return false;
520:                    }
521:                }
522:                // Otherwise, jid.node must be null.
523:                else if (jid.node != null) {
524:                    return false;
525:                }
526:                // Compare domain, which must be null.
527:                if (!domain.equals(jid.domain)) {
528:                    return false;
529:                }
530:                // Resource. If resource isn't null, compare.
531:                if (resource != null) {
532:                    if (!resource.equals(jid.resource)) {
533:                        return false;
534:                    }
535:                }
536:                // Otherwise, jid.resource must be null.
537:                else if (jid.resource != null) {
538:                    return false;
539:                }
540:                // Passed all checks, so equal.
541:                return true;
542:            }
543:
544:            public int compareTo(JID jid) {
545:                // Comparison order is domain, node, resource.
546:                int compare = domain.compareTo(jid.domain);
547:                if (compare == 0) {
548:                    String myNode = node != null ? node : "";
549:                    String hisNode = jid.node != null ? jid.node : "";
550:                    compare = myNode.compareTo(hisNode);
551:                }
552:                if (compare == 0) {
553:                    String myResource = resource != null ? resource : "";
554:                    String hisResource = jid.resource != null ? jid.resource
555:                            : "";
556:                    compare = myResource.compareTo(hisResource);
557:                }
558:                return compare;
559:            }
560:
561:            /**
562:             * Returns true if two JID's are equivalent. The JID components are compared using
563:             * the following rules:<ul>
564:             *      <li>Nodes are normalized using nodeprep (case insensitive).
565:             *      <li>Domains are normalized using IDNA and then nameprep (case insensitive).
566:             *      <li>Resources are normalized using resourceprep (case sensitive).</ul>
567:             *
568:             * These normalization rules ensure, for example, that
569:             * <tt>User@EXAMPLE.com/home</tt> is considered equal to <tt>user@example.com/home</tt>.
570:             *
571:             * @param jid1 a JID.
572:             * @param jid2 a JID.
573:             * @return true if the JIDs are equivalent; false otherwise.
574:             * @throws IllegalArgumentException if either JID is not valid.
575:             */
576:            public static boolean equals(String jid1, String jid2) {
577:                return new JID(jid1).equals(new JID(jid2));
578:            }
579:
580:            /**
581:             * A simple cache class that extends LinkedHashMap. It uses an LRU policy to
582:             * keep the cache at a maximum size.
583:             */
584:            private static class Cache extends LinkedHashMap<String, Object> {
585:
586:                private int maxSize;
587:
588:                public Cache(int maxSize) {
589:                    super (64, .75f, true);
590:                    this .maxSize = maxSize;
591:                }
592:
593:                protected boolean removeEldestEntry(Map.Entry eldest) {
594:                    return size() > maxSize;
595:                }
596:            }
597:
598:            public void writeExternal(ObjectOutput out) throws IOException {
599:                ExternalizableUtil.getInstance().writeSafeUTF(out, toString());
600:            }
601:
602:            public void readExternal(ObjectInput in) throws IOException,
603:                    ClassNotFoundException {
604:                String jid = ExternalizableUtil.getInstance().readSafeUTF(in);
605:                String[] parts = getParts(jid);
606:
607:                this .node = parts[0];
608:                this .domain = parts[1];
609:                this .resource = parts[2];
610:                // Cache the bare and full JID String representation
611:                updateCache();
612:            }
613:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.