Source Code Cross Referenced for JBossTimer.java in  » EJB-Server-JBoss-4.2.1 » jmx » org » jboss » mx » timer » 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 » EJB Server JBoss 4.2.1 » jmx » org.jboss.mx.timer 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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.mx.timer;
023:
024:        import java.util.Date;
025:        import java.util.HashMap;
026:        import java.util.Iterator;
027:        import java.util.Vector;
028:
029:        import javax.management.InstanceNotFoundException;
030:        import javax.management.MBeanRegistration;
031:        import javax.management.MBeanServer;
032:        import javax.management.NotificationBroadcasterSupport;
033:        import javax.management.ObjectName;
034:        import javax.management.timer.TimerNotification;
035:
036:        import org.jboss.logging.Logger;
037:        import org.jboss.mx.util.RunnableScheduler;
038:        import org.jboss.mx.util.SchedulableRunnable;
039:
040:        /**
041:         * A clone of the JBossMX javax.management.timer.Timer service.
042:         * 
043:         * There are indications that the jdk5 javax.management.timer.Timer 
044:         * uses internally a single-threaded implementation for executing
045:         * scheduled tasks, so scheduling of multiple tasks is affected
046:         * when moving from jdk1.4 and the jboss implementation of Timer,
047:         * to a jdk5 runtime.
048:         * 
049:         * The JBossMX Timer implementation in contrast uses a dynamically
050:         * extensible thread pool to execute scheduled tasks. Since we don't
051:         * control the jdk5 implementation, we've cloned the jboss timer
052:         * so it can be used as a drop-in replacement of the jdk JMX Timer.
053:         * 
054:         * The two classes *should* be kept in sync, or instead change our
055:         * javax.management.timer.Timer to delegate to this class.
056:         * 
057:         * @see javax.management.timer.Timer
058:         *
059:         * @author <a href="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>
060:         * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
061:         * @version $Revision: 57200 $
062:         */
063:        public class JBossTimer extends NotificationBroadcasterSupport
064:                implements  JBossTimerMBean, MBeanRegistration {
065:            // logging support
066:            private static Logger log = Logger.getLogger(JBossTimer.class);
067:
068:            // Constants -----------------------------------------------------
069:
070:            /** The number of milliseconds in one second. */
071:            public static final long ONE_SECOND = 1000;
072:
073:            /** The number of milliseconds in one minute. */
074:            public static final long ONE_MINUTE = ONE_SECOND * 60;
075:
076:            /** The number of milliseconds in one hour. */
077:            public static final long ONE_HOUR = ONE_MINUTE * 60;
078:
079:            /** The number of milliseconds in one day. */
080:            public static final long ONE_DAY = ONE_HOUR * 24;
081:
082:            /** The number of milliseconds in one week. */
083:            public static final long ONE_WEEK = ONE_DAY * 7;
084:
085:            /** Don't send notifications at initial start up. */
086:            private static final int SEND_NO = 0;
087:
088:            /** Send all past notifications at initial start up. */
089:            private static final int SEND_START = 1;
090:
091:            /** Normal operation sending */
092:            private static final int SEND_NORMAL = 2;
093:
094:            // Attributes ----------------------------------------------------
095:
096:            /** The next notification id. */
097:            int nextId = 0;
098:
099:            /** The next notification sequence number. */
100:            long sequenceNumber = 0;
101:
102:            /** The send past events attribute. */
103:            boolean sendPastNotifications = false;
104:
105:            /** Whether the service is active. */
106:            boolean active = false;
107:
108:            /** Our object name. */
109:            ObjectName objectName;
110:
111:            /** The registered notifications. */
112:            HashMap notifications = new HashMap();
113:
114:            /** The scheduler */
115:            private RunnableScheduler scheduler = new RunnableScheduler();
116:
117:            // Static --------------------------------------------------------
118:
119:            // Constructors --------------------------------------------------
120:
121:            // Public --------------------------------------------------------
122:
123:            // TimerMBean implementation -------------------------------------
124:
125:            public Integer addNotification(String type, String message,
126:                    Object userData, Date date) throws IllegalArgumentException {
127:                return addNotification(type, message, userData, date, 0);
128:            }
129:
130:            public Integer addNotification(String type, String message,
131:                    Object userData, Date date, long period)
132:                    throws IllegalArgumentException {
133:                return addNotification(type, message, userData, date, period, 0);
134:            }
135:
136:            public Integer addNotification(String type, String message,
137:                    Object userData, Date date, long period, long occurences)
138:                    throws IllegalArgumentException {
139:                return addNotification(type, message, userData, date, period,
140:                        occurences, false);
141:            }
142:
143:            /**
144:             * Creates a new timer notification with the specified type, message and userData and inserts it into the list of notifications with a given date, period and number of occurences.
145:             * <p/>
146:             * If the timer notification to be inserted has a date that is before the current date, the method behaves as if the specified date were the current date.
147:             * For once-off notifications, the notification is delivered immediately.
148:             * For periodic notifications, the first notification is delivered immediately and the subsequent ones are spaced as specified by the period parameter.
149:             * <p/>
150:             * Note that once the timer notification has been added into the list of notifications, its associated date, period and number of occurences cannot be updated.
151:             * <p/>
152:             * In the case of a periodic notification, the value of parameter fixedRate is used to specify the execution scheme, as specified in Timer.
153:             *
154:             * @param type         The timer notification type.
155:             * @param message      The timer notification detailed message.
156:             * @param userData     The timer notification user data object.
157:             * @param date         The date when the notification occurs.
158:             * @param period       The period of the timer notification (in milliseconds).
159:             * @param nbOccurences The total number the timer notification will be emitted.
160:             * @param fixedRate    If true and if the notification is periodic, the notification is scheduled with a fixed-rate execution scheme. If false and if the notification is periodic, the notification is scheduled with a fixed-delay execution scheme. Ignored if the notification is not periodic.
161:             * @return The identifier of the new created timer notification.
162:             * @throws IllegalArgumentException The period or the number of occurences is negative
163:             */
164:            public Integer addNotification(String type, String message,
165:                    Object userData, Date date, long period, long nbOccurences,
166:                    boolean fixedRate) throws IllegalArgumentException {
167:                // Generate the next id.
168:                int newId = 0;
169:                newId = ++nextId;
170:                Integer id = new Integer(newId);
171:
172:                // Validate and create the registration.
173:                RegisteredNotification rn = new RegisteredNotification(id,
174:                        type, message, userData, date, period, nbOccurences,
175:                        fixedRate);
176:
177:                // Add the registration.
178:                synchronized (notifications) {
179:                    notifications.put(id, rn);
180:                    rn.setNextRun(rn.nextDate);
181:                    rn.setScheduler(scheduler);
182:                }
183:
184:                return id;
185:            }
186:
187:            public Vector getAllNotificationIDs() {
188:                synchronized (notifications) {
189:                    return new Vector(notifications.keySet());
190:                }
191:            }
192:
193:            public Date getDate(Integer id) {
194:                // Make sure there is a registration
195:                RegisteredNotification rn = (RegisteredNotification) notifications
196:                        .get(id);
197:                if (rn == null)
198:                    return null;
199:
200:                // Return a copy of the date.
201:                return new Date(rn.startDate);
202:            }
203:
204:            public int getNbNotifications() {
205:                return notifications.size();
206:            }
207:
208:            public Long getNbOccurences(Integer id) {
209:                // Make sure there is a registration
210:                RegisteredNotification rn = (RegisteredNotification) notifications
211:                        .get(id);
212:                if (rn == null)
213:                    return null;
214:
215:                // Return a copy of the occurences.
216:                return new Long(rn.occurences);
217:            }
218:
219:            /**
220:             * Gets a copy of the flag indicating whether a peridic notification is executed at fixed-delay or at fixed-rate.
221:             *
222:             * @param id The timer notification identifier.
223:             * @return A copy of the flag indicating whether a peridic notification is executed at fixed-delay or at fixed-rate.
224:             */
225:            public Boolean getFixedRate(Integer id) {
226:                // Make sure there is a registration
227:                RegisteredNotification rn = (RegisteredNotification) notifications
228:                        .get(id);
229:                if (rn == null)
230:                    return null;
231:
232:                // Return a copy of the fixedRate
233:                return new Boolean(rn.fixedRate);
234:            }
235:
236:            public Vector getNotificationIDs(String type) {
237:                Vector result = new Vector();
238:
239:                // Loop through the notifications looking for the passed type.
240:                synchronized (notifications) {
241:                    Iterator iterator = notifications.values().iterator();
242:                    while (iterator.hasNext()) {
243:                        RegisteredNotification rn = (RegisteredNotification) iterator
244:                                .next();
245:                        if (rn.type.equals(type))
246:                            result.add(rn.id);
247:                    }
248:                }
249:
250:                return result;
251:            }
252:
253:            public String getNotificationMessage(Integer id) {
254:                // Make sure there is a registration
255:                RegisteredNotification rn = (RegisteredNotification) notifications
256:                        .get(id);
257:                if (rn == null)
258:                    return null;
259:
260:                // Return the message
261:                return rn.message;
262:            }
263:
264:            public String getNotificationType(Integer id) {
265:                // Make sure there is a registration
266:                RegisteredNotification rn = (RegisteredNotification) notifications
267:                        .get(id);
268:                if (rn == null)
269:                    return null;
270:
271:                // Return the type.
272:                return rn.type;
273:            }
274:
275:            public Object getNotificationUserData(Integer id) {
276:                // Make sure there is a registration
277:                RegisteredNotification rn = (RegisteredNotification) notifications
278:                        .get(id);
279:                if (rn == null)
280:                    return null;
281:
282:                // Return the user data.
283:                return rn.userData;
284:            }
285:
286:            public Long getPeriod(Integer id) {
287:                // Make sure there is a registration
288:                RegisteredNotification rn = (RegisteredNotification) notifications
289:                        .get(id);
290:                if (rn == null)
291:                    return null;
292:
293:                // Return a copy of the period
294:                return new Long(rn.period);
295:            }
296:
297:            public boolean getSendPastNotifications() {
298:                return sendPastNotifications;
299:            }
300:
301:            public boolean isActive() {
302:                return active;
303:            }
304:
305:            public boolean isEmpty() {
306:                return notifications.isEmpty();
307:            }
308:
309:            public void removeAllNotifications() {
310:                // Remove the notifications
311:                synchronized (notifications) {
312:                    Iterator iterator = notifications.values().iterator();
313:                    while (iterator.hasNext()) {
314:                        RegisteredNotification rn = (RegisteredNotification) iterator
315:                                .next();
316:                        rn.setScheduler(null);
317:                        iterator.remove();
318:                    }
319:                }
320:
321:                // The spec says to reset the identifiers, seems like a bad idea to me
322:                synchronized (this ) {
323:                    nextId = 0;
324:                }
325:            }
326:
327:            public void removeNotification(Integer id)
328:                    throws InstanceNotFoundException {
329:
330:                log.debug("removeNotification: " + objectName + ",id=" + id);
331:
332:                // Check if there is a notification.
333:                synchronized (notifications) {
334:                    RegisteredNotification rn = (RegisteredNotification) notifications
335:                            .get(id);
336:                    if (rn == null)
337:                        throw new InstanceNotFoundException(
338:                                "No notification id : " + id.toString());
339:
340:                    // Remove the notification
341:                    rn.setScheduler(null);
342:                    notifications.remove(id);
343:                }
344:            }
345:
346:            public void removeNotifications(String type)
347:                    throws InstanceNotFoundException {
348:                boolean found = false;
349:
350:                log.debug("removeNotifications: " + objectName + ",type="
351:                        + type);
352:
353:                // Loop through the notifications removing the passed type.
354:                synchronized (notifications) {
355:                    Iterator iterator = notifications.values().iterator();
356:                    while (iterator.hasNext()) {
357:                        RegisteredNotification rn = (RegisteredNotification) iterator
358:                                .next();
359:                        if (rn.type.equals(type)) {
360:                            rn.setScheduler(null);
361:                            iterator.remove();
362:                            found = true;
363:                        }
364:                    }
365:                }
366:
367:                // The spec says to through an exception when nothing removed.
368:                if (found == false)
369:                    throw new InstanceNotFoundException(
370:                            "Nothing registered for type: " + type);
371:            }
372:
373:            public void setSendPastNotifications(boolean value) {
374:                log.debug("setSendPastNotifications: " + objectName + ",value="
375:                        + value);
376:                sendPastNotifications = value;
377:            }
378:
379:            public synchronized void start() {
380:                // Ignore if already active
381:                if (active == true)
382:                    return;
383:                active = true;
384:
385:                log.debug("start: " + objectName + " at " + new Date());
386:
387:                // Perform the initial sends, for past notifications send missed events
388:                // otherwise ignore them
389:                synchronized (notifications) {
390:                    Iterator iterator = notifications.values().iterator();
391:                    while (iterator.hasNext()) {
392:                        RegisteredNotification rn = (RegisteredNotification) iterator
393:                                .next();
394:                        if (sendPastNotifications)
395:                            rn.sendType = SEND_START;
396:                        else
397:                            rn.sendType = SEND_NO;
398:                        sendNotifications(rn);
399:                        rn.sendType = SEND_NORMAL;
400:                    }
401:                }
402:
403:                // Start 'em up
404:                scheduler.start();
405:            }
406:
407:            public synchronized void stop() {
408:                // Ignore if not active
409:                if (active == false)
410:                    return;
411:
412:                log.debug("stop: " + objectName + ",now=" + new Date());
413:
414:                // Stop the threads
415:                active = false;
416:                scheduler.stop();
417:            }
418:
419:            // MBeanRegistrationImplementation overrides ---------------------
420:
421:            public ObjectName preRegister(MBeanServer server,
422:                    ObjectName objectName) throws Exception {
423:                // Save the object name
424:                this .objectName = objectName;
425:
426:                // Use the passed object name.
427:                return objectName;
428:            }
429:
430:            public void postRegister(Boolean registrationDone) {
431:            }
432:
433:            public void preDeregister() throws Exception {
434:                // Stop the timer before deregistration.
435:                stop();
436:            }
437:
438:            public void postDeregister() {
439:            }
440:
441:            // Package protected ---------------------------------------------
442:
443:            // Protected -----------------------------------------------------
444:
445:            // Private -------------------------------------------------------
446:
447:            /**
448:             * Send any outstanding notifications.
449:             *
450:             * @param rn the registered notification to send.
451:             */
452:            private void sendNotifications(RegisteredNotification rn) {
453:                // Keep going until we have done all outstanding notifications.
454:                // The loop ends when not active, or there are no outstanding
455:                // notifications.
456:                // REVIEW: In practice for normal operation it never loops. We
457:                // ignore sends that we have missed. This avoids problems where
458:                // the notification takes longer than the period. Correct???
459:                while (isActive() && rn.nextDate != 0
460:                        && rn.nextDate <= System.currentTimeMillis()) {
461:                    // Do we actually send it?
462:                    // Yes, unless start and not sending past notifications.
463:                    if (rn.sendType != SEND_NO) {
464:                        long seq = 0;
465:                        synchronized (this ) {
466:                            seq = ++sequenceNumber;
467:                        }
468:
469:                        log.debug("sendNotification: " + rn);
470:                        TimerNotification tn = new TimerNotification(rn.type,
471:                                objectName, seq, rn.nextDate, rn.message, rn.id);
472:                        tn.setUserData(rn.userData);
473:                        sendNotification(tn);
474:                    }
475:                    // Calculate the next date.
476:                    // Except for when we are sending past notifications at start up,
477:                    // it cannot be in the future.
478:                    do {
479:                        // If no next run, remove it sets the next date to zero.
480:                        if (rn.calcNextDate() == false) {
481:                            synchronized (notifications) {
482:                                log.debug("remove: " + rn);
483:                                notifications.remove(rn.id);
484:                            }
485:                        }
486:                    } while (isActive() && rn.sendType != SEND_START
487:                            && rn.nextDate != 0 && rn.occurences == 0
488:                            && rn.nextDate < System.currentTimeMillis());
489:                }
490:
491:                if (rn.nextDate != 0)
492:                    rn.setNextRun(rn.nextDate);
493:            }
494:
495:            // Inner classes -------------------------------------------------
496:
497:            /**
498:             * A registered notification. These run as separate threads.
499:             */
500:            private class RegisteredNotification extends SchedulableRunnable {
501:                // Attributes ----------------------------------------------------
502:
503:                /** The notification id. */
504:                public Integer id;
505:
506:                /** The notification type. */
507:                public String type;
508:
509:                /** The message. */
510:                public String message;
511:
512:                /** The user data. */
513:                public Object userData;
514:
515:                /** The start date. */
516:                public long startDate;
517:
518:                /** The period. */
519:                public long period;
520:
521:                /** The maximum number of occurences. */
522:                public long occurences;
523:
524:                /** The flag to indicate fixedRate notifications, or fixedDelay (default) */
525:                public boolean fixedRate;
526:
527:                /** The send type, no send, past notifications or normal */
528:                public int sendType = SEND_NORMAL;
529:
530:                /** The next run date */
531:                public long nextDate = 0;
532:
533:                // Constructors --------------------------------------------------
534:
535:                /**
536:                 * The default constructor.
537:                 *
538:                 * @param id the notification id.
539:                 * @param type the notification type.
540:                 * @param message the notification's message string.
541:                 * @param userData the notification's user data.
542:                 * @param startDate the date/time the notification will occur.
543:                 * @param period the repeat period in milli-seconds. Passing zero means
544:                 *        no repeat.
545:                 * @param occurences the maximum number of repeats. When the period is not
546:                 *        zero and this parameter is zero, it will repeat indefinitely.
547:                 * @param fixedRate If true and if the notification is periodic, the notification
548:                 *        is scheduled with a fixed-rate execution scheme. If false and if the notification
549:                 *        is periodic, the notification is scheduled with a fixed-delay execution scheme.
550:                 *        Ignored if the notification is not periodic.
551:                 *
552:                 * @exception IllegalArgumentException when the date is before the current
553:                 *        date, the period is negative or the number of repeats is
554:                 *        negative.
555:                 */
556:                public RegisteredNotification(Integer id, String type,
557:                        String message, Object userData, Date startDate,
558:                        long period, long occurences, boolean fixedRate)
559:                        throws IllegalArgumentException {
560:                    // Basic validation
561:                    if (startDate == null)
562:                        throw new IllegalArgumentException("Null Date");
563:                    if (period < 0)
564:                        throw new IllegalArgumentException("Negative Period");
565:                    if (occurences < 0)
566:                        throw new IllegalArgumentException(
567:                                "Negative Occurences");
568:
569:                    this .startDate = startDate.getTime();
570:                    if (startDate.getTime() < System.currentTimeMillis()) {
571:                        log.debug("startDate [" + startDate
572:                                + "] in the past, set to now");
573:                        this .startDate = System.currentTimeMillis();
574:                    }
575:
576:                    // Remember the values
577:                    this .id = id;
578:                    this .type = type;
579:                    this .message = message;
580:                    this .userData = userData;
581:                    this .period = period;
582:                    this .occurences = occurences;
583:                    this .fixedRate = fixedRate;
584:
585:                    this .nextDate = this .startDate;
586:
587:                    String msgStr = "new " + this .toString();
588:                    log.debug(msgStr);
589:                }
590:
591:                // Public --------------------------------------------------------
592:
593:                /**
594:                 * Calculate the next notification date. Add on the period until
595:                 * the number of occurences is exhausted.
596:                 *
597:                 * @return false when there are no more occurences, true otherwise.
598:                 */
599:                boolean calcNextDate() {
600:                    // No period, we've finished
601:                    if (period == 0) {
602:                        nextDate = 0;
603:                        return false;
604:                    }
605:
606:                    // Limited number of repeats have we finished?
607:                    if (occurences != 0 && --occurences == 0) {
608:                        nextDate = 0;
609:                        return false;
610:                    }
611:
612:                    // Calculate the next occurence
613:                    if (fixedRate)
614:                        nextDate += period;
615:                    else
616:                        // fixed delay
617:                        nextDate = System.currentTimeMillis() + period;
618:
619:                    return true;
620:                }
621:
622:                // SchedulableRunnable overrides ---------------------------------
623:
624:                /**
625:                 * Send the notifications.
626:                 */
627:                public void doRun() {
628:                    // Send any notifications
629:                    sendNotifications(this );
630:                }
631:
632:                public String toString() {
633:                    return " RegisteredNotification: [timer=" + objectName
634:                            + ",id=" + id + ",startDate=" + new Date(startDate)
635:                            + ",period=" + period + ",occurences=" + occurences
636:                            + ",fixedRate=" + fixedRate + ",nextDate="
637:                            + new Date(nextDate) + "]";
638:                }
639:            }
640:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.