Source Code Cross Referenced for MessageDispatcher.java in  » Web-Services » spring-ws-1.0.0 » org » springframework » ws » server » 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 » Web Services » spring ws 1.0.0 » org.springframework.ws.server 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Copyright 2005, 2006 the original author or authors.
003:         *
004:         * Licensed under the Apache License, Version 2.0 (the "License");
005:         * you may not use this file except in compliance with the License.
006:         * You may obtain a copy of the License at
007:         *
008:         *      http://www.apache.org/licenses/LICENSE-2.0
009:         *
010:         * Unless required by applicable law or agreed to in writing, software
011:         * distributed under the License is distributed on an "AS IS" BASIS,
012:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013:         * See the License for the specific language governing permissions and
014:         * limitations under the License.
015:         */
016:
017:        package org.springframework.ws.server;
018:
019:        import java.io.ByteArrayOutputStream;
020:        import java.util.ArrayList;
021:        import java.util.Collections;
022:        import java.util.Iterator;
023:        import java.util.List;
024:        import java.util.Map;
025:
026:        import org.apache.commons.logging.Log;
027:        import org.apache.commons.logging.LogFactory;
028:        import org.springframework.beans.BeansException;
029:        import org.springframework.beans.factory.BeanFactoryUtils;
030:        import org.springframework.beans.factory.BeanNameAware;
031:        import org.springframework.context.ApplicationContext;
032:        import org.springframework.context.ApplicationContextAware;
033:        import org.springframework.core.OrderComparator;
034:        import org.springframework.core.io.ClassPathResource;
035:        import org.springframework.core.io.Resource;
036:        import org.springframework.util.ClassUtils;
037:        import org.springframework.util.ObjectUtils;
038:        import org.springframework.web.servlet.DispatcherServlet;
039:        import org.springframework.ws.FaultAwareWebServiceMessage;
040:        import org.springframework.ws.NoEndpointFoundException;
041:        import org.springframework.ws.WebServiceMessage;
042:        import org.springframework.ws.context.MessageContext;
043:        import org.springframework.ws.server.endpoint.MessageEndpoint;
044:        import org.springframework.ws.server.endpoint.PayloadEndpoint;
045:        import org.springframework.ws.server.endpoint.adapter.MessageEndpointAdapter;
046:        import org.springframework.ws.server.endpoint.adapter.MessageMethodEndpointAdapter;
047:        import org.springframework.ws.server.endpoint.adapter.PayloadEndpointAdapter;
048:        import org.springframework.ws.server.endpoint.adapter.PayloadMethodEndpointAdapter;
049:        import org.springframework.ws.soap.server.SoapMessageDispatcher;
050:        import org.springframework.ws.transport.WebServiceMessageReceiver;
051:        import org.springframework.ws.transport.support.DefaultStrategiesHelper;
052:
053:        /**
054:         * Central dispatcher for use within Spring-WS, dispatching Web service messages to registered endpoints.
055:         * <p/>
056:         * This dispatcher is quite similar to Spring MVCs {@link DispatcherServlet}. Just like its counterpart, this dispatcher
057:         * is very flexible. This class is SOAP agnostic; in typical SOAP Web Services, the {@link SoapMessageDispatcher}
058:         * subclass is used. <ul> <li>It can use any {@link EndpointMapping} implementation - whether standard, or provided as
059:         * part of an application - to control the routing of request messages to endpoint objects. Endpoint mappings can be
060:         * registered using the <code>endpointMappings</code> property.</li> <li>It can use any {@link EndpointAdapter}; this
061:         * allows one to use any endpoint interface or form. Defaults to the {@link MessageEndpointAdapter} and {@link
062:         * PayloadEndpointAdapter}, for {@link MessageEndpoint} and {@link PayloadEndpoint}, respectively, and the {@link
063:         * MessageMethodEndpointAdapter} and {@link PayloadMethodEndpointAdapter}. Additional endpoint adapters can be added
064:         * through the <code>endpointAdapters</code> property.</li> <li>Its exception resolution strategy can be specified via a
065:         * {@link EndpointExceptionResolver}, for example mapping certain exceptions to SOAP Faults. Default is none. Additional
066:         * exception resolvers can be added through the <code>endpointExceptionResolvers</code> property.</li> </ul>
067:         *
068:         * @author Arjen Poutsma
069:         * @see EndpointMapping
070:         * @see EndpointAdapter
071:         * @see EndpointExceptionResolver
072:         * @see org.springframework.web.servlet.DispatcherServlet
073:         * @since 1.0.0
074:         */
075:        public class MessageDispatcher implements  WebServiceMessageReceiver,
076:                BeanNameAware, ApplicationContextAware {
077:
078:            /** Log category to use when no mapped endpoint is found for a request. */
079:            public static final String ENDPOINT_NOT_FOUND_LOG_CATEGORY = "org.springframework.ws.EndpointNotFound";
080:
081:            /** Additional logger to use when no mapped endpoint is found for a request. */
082:            protected static final Log endpointNotFoundLogger = LogFactory
083:                    .getLog(MessageDispatcher.ENDPOINT_NOT_FOUND_LOG_CATEGORY);
084:
085:            /** Logger available to subclasses. */
086:            protected final Log logger = LogFactory.getLog(getClass());
087:
088:            private final DefaultStrategiesHelper defaultStrategiesHelper;
089:
090:            /** The registered bean name for this dispatcher. */
091:            private String beanName;
092:
093:            /** List of EndpointAdapters used in this dispatcher. */
094:            private List endpointAdapters;
095:
096:            /** List of EndpointExceptionResolvers used in this dispatcher. */
097:            private List endpointExceptionResolvers;
098:
099:            /** List of EndpointMappings used in this dispatcher. */
100:            private List endpointMappings;
101:
102:            /** Initializes a new instance of the <code>MessageDispatcher</code>. */
103:            public MessageDispatcher() {
104:                Resource resource = new ClassPathResource(ClassUtils
105:                        .getShortName(getClass())
106:                        + ".properties", getClass());
107:                defaultStrategiesHelper = new DefaultStrategiesHelper(resource);
108:            }
109:
110:            /** Returns the <code>EndpointAdapter</code>s to use by this <code>MessageDispatcher</code>. */
111:            public List getEndpointAdapters() {
112:                return endpointAdapters;
113:            }
114:
115:            /** Sets the <code>EndpointAdapter</code>s to use by this <code>MessageDispatcher</code>. */
116:            public void setEndpointAdapters(List endpointAdapters) {
117:                this .endpointAdapters = endpointAdapters;
118:            }
119:
120:            /** Returns the <code>EndpointExceptionResolver</code>s to use by this <code>MessageDispatcher</code>. */
121:            public List getEndpointExceptionResolvers() {
122:                return endpointExceptionResolvers;
123:            }
124:
125:            /** Sets the <code>EndpointExceptionResolver</code>s to use by this <code>MessageDispatcher</code>. */
126:            public void setEndpointExceptionResolvers(
127:                    List endpointExceptionResolvers) {
128:                this .endpointExceptionResolvers = endpointExceptionResolvers;
129:            }
130:
131:            /** Returns the <code>EndpointMapping</code>s to use by this <code>MessageDispatcher</code>. */
132:            public List getEndpointMappings() {
133:                return endpointMappings;
134:            }
135:
136:            /** Sets the <code>EndpointMapping</code>s to use by this <code>MessageDispatcher</code>. */
137:            public void setEndpointMappings(List endpointMappings) {
138:                this .endpointMappings = endpointMappings;
139:            }
140:
141:            public final void setBeanName(String beanName) {
142:                this .beanName = beanName;
143:            }
144:
145:            public void setApplicationContext(
146:                    ApplicationContext applicationContext)
147:                    throws BeansException {
148:                initEndpointAdapters(applicationContext);
149:                initEndpointExceptionResolvers(applicationContext);
150:                initEndpointMappings(applicationContext);
151:            }
152:
153:            public void receive(MessageContext messageContext) throws Exception {
154:                if (logger.isTraceEnabled()) {
155:                    ByteArrayOutputStream os = new ByteArrayOutputStream();
156:                    messageContext.getRequest().writeTo(os);
157:                    logger.trace("MessageDispatcher with name '" + beanName
158:                            + "' received request [" + os.toString("UTF-8")
159:                            + "]");
160:                } else if (logger.isDebugEnabled()) {
161:                    logger.debug("MessageDispatcher with name '" + beanName
162:                            + "' received request ["
163:                            + messageContext.getRequest() + "]");
164:                }
165:                dispatch(messageContext);
166:                if (messageContext.hasResponse()) {
167:                    if (logger.isTraceEnabled()) {
168:                        ByteArrayOutputStream requestStream = new ByteArrayOutputStream();
169:                        messageContext.getRequest().writeTo(requestStream);
170:                        ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
171:                        messageContext.getResponse().writeTo(responseStream);
172:                        logger.trace("MessageDispatcher with name '" + beanName
173:                                + "' sends response ["
174:                                + responseStream.toString("UTF-8")
175:                                + "] for request ["
176:                                + requestStream.toString("UTF-8") + "]");
177:                    } else if (logger.isDebugEnabled()) {
178:                        logger.debug("MessageDispatcher with name '" + beanName
179:                                + "' sends response ["
180:                                + messageContext.getResponse()
181:                                + "] for request ["
182:                                + messageContext.getRequest() + "]");
183:                    }
184:                } else if (logger.isDebugEnabled()) {
185:                    logger.debug("MessageDispatcher with name '" + beanName
186:                            + "' sends no response for request ["
187:                            + messageContext.getRequest() + "]");
188:                }
189:            }
190:
191:            /**
192:             * Dispatches the request in the given MessageContext according to the configuration.
193:             *
194:             * @param messageContext the message context
195:             * @throws org.springframework.ws.NoEndpointFoundException
196:             *          thrown when an endpoint cannot be resolved for the incoming message
197:             */
198:            protected final void dispatch(MessageContext messageContext)
199:                    throws Exception {
200:                EndpointInvocationChain mappedEndpoint = null;
201:                int interceptorIndex = -1;
202:                try {
203:                    // Determine endpoint for the current context
204:                    mappedEndpoint = getEndpoint(messageContext);
205:                    if (mappedEndpoint == null
206:                            || mappedEndpoint.getEndpoint() == null) {
207:                        throw new NoEndpointFoundException(messageContext
208:                                .getRequest());
209:                    }
210:                    if (!handleRequest(mappedEndpoint, messageContext)) {
211:                        return;
212:                    }
213:                    // Apply handleRequest of registered interceptors
214:                    if (mappedEndpoint.getInterceptors() != null) {
215:                        for (int i = 0; i < mappedEndpoint.getInterceptors().length; i++) {
216:                            EndpointInterceptor interceptor = mappedEndpoint
217:                                    .getInterceptors()[i];
218:                            interceptorIndex = i;
219:                            if (!interceptor.handleRequest(messageContext,
220:                                    mappedEndpoint.getEndpoint())) {
221:                                triggerHandleResponse(mappedEndpoint,
222:                                        interceptorIndex, messageContext);
223:                                return;
224:                            }
225:                        }
226:                    }
227:                    // Acutally invoke the endpoint
228:                    EndpointAdapter endpointAdapter = getEndpointAdapter(mappedEndpoint
229:                            .getEndpoint());
230:                    endpointAdapter.invoke(messageContext, mappedEndpoint
231:                            .getEndpoint());
232:
233:                    // Apply handleResponse methods of registered interceptors
234:                    triggerHandleResponse(mappedEndpoint, interceptorIndex,
235:                            messageContext);
236:                } catch (NoEndpointFoundException ex) {
237:                    // No triggering of interceptors if no endpoint is found
238:                    if (endpointNotFoundLogger.isWarnEnabled()) {
239:                        endpointNotFoundLogger
240:                                .warn("No endpoint mapping found for ["
241:                                        + messageContext.getRequest() + "]");
242:                    }
243:                    throw ex;
244:                } catch (Exception ex) {
245:                    Object endpoint = mappedEndpoint != null ? mappedEndpoint
246:                            .getEndpoint() : null;
247:                    processEndpointException(messageContext, endpoint, ex);
248:                    triggerHandleResponse(mappedEndpoint, interceptorIndex,
249:                            messageContext);
250:                }
251:            }
252:
253:            /**
254:             * Returns the endpoint for this request. All endpoint mappings are tried, in order.
255:             *
256:             * @return the <code>EndpointInvocationChain</code>, or <code>null</code> if no endpoint could be found.
257:             */
258:            protected EndpointInvocationChain getEndpoint(
259:                    MessageContext messageContext) throws Exception {
260:                for (Iterator iterator = endpointMappings.iterator(); iterator
261:                        .hasNext();) {
262:                    EndpointMapping endpointMapping = (EndpointMapping) iterator
263:                            .next();
264:                    EndpointInvocationChain endpoint = endpointMapping
265:                            .getEndpoint(messageContext);
266:                    if (endpoint != null) {
267:                        if (logger.isDebugEnabled()) {
268:                            logger.debug("Endpoint mapping [" + endpointMapping
269:                                    + "] maps request to endpoint ["
270:                                    + endpoint.getEndpoint() + "]");
271:                        }
272:                        return endpoint;
273:                    } else if (logger.isDebugEnabled()) {
274:                        logger.debug("Endpoint mapping [" + endpointMapping
275:                                + "] has no mapping for request");
276:                    }
277:                }
278:                return null;
279:            }
280:
281:            /**
282:             * Returns the <code>EndpointAdapter</code> for the given endpoint.
283:             *
284:             * @param endpoint the endpoint to find an adapter for
285:             * @return the adapter
286:             */
287:            protected EndpointAdapter getEndpointAdapter(Object endpoint) {
288:                for (Iterator iterator = endpointAdapters.iterator(); iterator
289:                        .hasNext();) {
290:                    EndpointAdapter endpointAdapter = (EndpointAdapter) iterator
291:                            .next();
292:                    if (logger.isDebugEnabled()) {
293:                        logger.debug("Testing endpoint adapter ["
294:                                + endpointAdapter + "]");
295:                    }
296:                    if (endpointAdapter.supports(endpoint)) {
297:                        return endpointAdapter;
298:                    }
299:                }
300:                throw new IllegalStateException(
301:                        "No adapter for endpoint ["
302:                                + endpoint
303:                                + "]: Does your endpoint implement a "
304:                                + "supported interface like MessageHandler or PayloadEndpoint?");
305:            }
306:
307:            /**
308:             * Callback for pre-processing of given invocation chain and message context. Gets called before invocation of
309:             * <code>handleRequest</code> on the interceptors.
310:             * <p/>
311:             * Default implementation does nothing, and returns <code>true</code>.
312:             *
313:             * @param mappedEndpoint the mapped <code>EndpointInvocationChain</code>
314:             * @param messageContext the message context
315:             * @return <code>true</code> if processing should continue; <code>false</code> otherwise
316:             */
317:            protected boolean handleRequest(
318:                    EndpointInvocationChain mappedEndpoint,
319:                    MessageContext messageContext) {
320:                return true;
321:            }
322:
323:            /**
324:             * Determine an error <code>SOAPMessage</code> respone via the registered <code>EndpointExceptionResolvers</code>.
325:             * Most likely, the response contains a <code>SOAPFault</code>. If no suitable resolver was found, the exception is
326:             * rethrown.
327:             *
328:             * @param messageContext current SOAPMessage request
329:             * @param endpoint       the executed endpoint, or null if none chosen at the time of the exception
330:             * @param ex             the exception that got thrown during handler execution
331:             * @throws Exception if no suitable resolver is found
332:             */
333:            protected void processEndpointException(
334:                    MessageContext messageContext, Object endpoint, Exception ex)
335:                    throws Exception {
336:                for (Iterator iterator = endpointExceptionResolvers.iterator(); iterator
337:                        .hasNext();) {
338:                    EndpointExceptionResolver resolver = (EndpointExceptionResolver) iterator
339:                            .next();
340:                    if (logger.isDebugEnabled()) {
341:                        logger.debug("Testing endpoint exception resolver ["
342:                                + resolver + "]");
343:                    }
344:                    if (resolver.resolveException(messageContext, endpoint, ex)) {
345:                        logger
346:                                .warn(
347:                                        "Endpoint invocation resulted in exception - responding with SOAP Fault",
348:                                        ex);
349:                        return;
350:                    }
351:                }
352:                // exception not resolved
353:                throw ex;
354:            }
355:
356:            /**
357:             * Trigger handleResponse or handleFault on the mapped EndpointInterceptors. Will just invoke said method on all
358:             * interceptors whose handleRequest invocation returned <code>true</code>, in addition to the last interceptor who
359:             * returned <code>false</code>.
360:             *
361:             * @param mappedEndpoint   the mapped EndpointInvocationChain
362:             * @param interceptorIndex index of last interceptor that was called
363:             * @param messageContext   the message context, whose request and response are filled
364:             * @see EndpointInterceptor#handleResponse(MessageContext,Object)
365:             */
366:            private void triggerHandleResponse(
367:                    EndpointInvocationChain mappedEndpoint,
368:                    int interceptorIndex, MessageContext messageContext)
369:                    throws Exception {
370:                if (mappedEndpoint != null
371:                        && messageContext.hasResponse()
372:                        && !ObjectUtils.isEmpty(mappedEndpoint
373:                                .getInterceptors())) {
374:                    boolean hasFault = false;
375:                    WebServiceMessage response = messageContext.getResponse();
376:                    if (response instanceof  FaultAwareWebServiceMessage) {
377:                        hasFault = ((FaultAwareWebServiceMessage) response)
378:                                .hasFault();
379:                    }
380:                    boolean resume = true;
381:                    for (int i = interceptorIndex; resume && i >= 0; i--) {
382:                        EndpointInterceptor interceptor = mappedEndpoint
383:                                .getInterceptors()[i];
384:                        if (!hasFault) {
385:                            resume = interceptor.handleResponse(messageContext,
386:                                    mappedEndpoint.getEndpoint());
387:                        } else {
388:                            resume = interceptor.handleFault(messageContext,
389:                                    mappedEndpoint.getEndpoint());
390:                        }
391:                    }
392:                }
393:            }
394:
395:            /**
396:             * Initialize the <code>EndpointAdapters</code> used by this class. If no adapter beans are explictely set by using
397:             * the <code>endpointAdapters</code> property, we use the default strategies.
398:             *
399:             * @see #setEndpointAdapters(java.util.List)
400:             */
401:            private void initEndpointAdapters(
402:                    ApplicationContext applicationContext)
403:                    throws BeansException {
404:                if (endpointAdapters == null) {
405:                    Map matchingBeans = BeanFactoryUtils
406:                            .beansOfTypeIncludingAncestors(applicationContext,
407:                                    EndpointAdapter.class, true, false);
408:                    if (!matchingBeans.isEmpty()) {
409:                        endpointAdapters = new ArrayList(matchingBeans.values());
410:                        Collections.sort(endpointAdapters,
411:                                new OrderComparator());
412:                    } else {
413:                        endpointAdapters = defaultStrategiesHelper
414:                                .getDefaultStrategies(EndpointAdapter.class,
415:                                        applicationContext);
416:                        if (logger.isDebugEnabled()) {
417:                            logger
418:                                    .debug("No EndpointAdapters found, using defaults");
419:                        }
420:                    }
421:                }
422:            }
423:
424:            /**
425:             * Initialize the <code>EndpointExceptionResolver</code> used by this class. If no resolver beans are explictely set
426:             * by using the <code>endpointExceptionResolvers</code> property, we use the default strategies.
427:             *
428:             * @see #setEndpointExceptionResolvers(java.util.List)
429:             */
430:            private void initEndpointExceptionResolvers(
431:                    ApplicationContext applicationContext)
432:                    throws BeansException {
433:                if (endpointExceptionResolvers == null) {
434:                    Map matchingBeans = BeanFactoryUtils
435:                            .beansOfTypeIncludingAncestors(applicationContext,
436:                                    EndpointExceptionResolver.class, true,
437:                                    false);
438:                    if (!matchingBeans.isEmpty()) {
439:                        endpointExceptionResolvers = new ArrayList(
440:                                matchingBeans.values());
441:                        Collections.sort(endpointExceptionResolvers,
442:                                new OrderComparator());
443:                    } else {
444:                        endpointExceptionResolvers = defaultStrategiesHelper
445:                                .getDefaultStrategies(
446:                                        EndpointExceptionResolver.class,
447:                                        applicationContext);
448:                        if (logger.isDebugEnabled()) {
449:                            logger
450:                                    .debug("No EndpointExceptionResolvers found, using defaults");
451:                        }
452:                    }
453:                }
454:            }
455:
456:            /**
457:             * Initialize the <code>EndpointMappings</code> used by this class. If no mapping beans are explictely set by using
458:             * the <code>endpointMappings</code> property, we use the default strategies.
459:             *
460:             * @see #setEndpointMappings(java.util.List)
461:             */
462:            private void initEndpointMappings(
463:                    ApplicationContext applicationContext)
464:                    throws BeansException {
465:                if (endpointMappings == null) {
466:                    Map matchingBeans = BeanFactoryUtils
467:                            .beansOfTypeIncludingAncestors(applicationContext,
468:                                    EndpointMapping.class, true, false);
469:                    if (!matchingBeans.isEmpty()) {
470:                        endpointMappings = new ArrayList(matchingBeans.values());
471:                        Collections.sort(endpointMappings,
472:                                new OrderComparator());
473:                    } else {
474:                        endpointMappings = defaultStrategiesHelper
475:                                .getDefaultStrategies(EndpointMapping.class,
476:                                        applicationContext);
477:                        if (logger.isDebugEnabled()) {
478:                            logger
479:                                    .debug("No EndpointMappings found, using defaults");
480:                        }
481:                    }
482:                }
483:            }
484:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.