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


0001:        /*
0002:         * $Id: Engine.java,v 1.112 2007/09/24 12:46:37 agoubard Exp $
0003:         *
0004:         * Copyright 2003-2007 Orange Nederland Breedband B.V.
0005:         * See the COPYRIGHT file for redistribution and use restrictions.
0006:         */
0007:        package org.xins.server;
0008:
0009:        import java.io.File;
0010:        import java.io.FileNotFoundException;
0011:        import java.io.IOException;
0012:        import java.io.InputStream;
0013:        import java.io.Writer;
0014:        import java.net.MalformedURLException;
0015:        import java.net.URL;
0016:        import java.util.Arrays;
0017:        import java.util.Iterator;
0018:        import java.util.Map;
0019:        import java.util.Set;
0020:
0021:        import javax.servlet.ServletConfig;
0022:        import javax.servlet.ServletContext;
0023:        import javax.servlet.ServletException;
0024:        import javax.servlet.http.HttpServletRequest;
0025:        import javax.servlet.http.HttpServletResponse;
0026:
0027:        import org.apache.log4j.NDC;
0028:
0029:        import org.apache.oro.text.regex.MalformedPatternException;
0030:        import org.apache.oro.text.regex.Pattern;
0031:        import org.apache.oro.text.regex.Perl5Compiler;
0032:        import org.apache.oro.text.regex.Perl5Matcher;
0033:
0034:        import org.json.JSONArray;
0035:        import org.json.JSONException;
0036:        import org.json.JSONObject;
0037:        import org.xins.common.FormattedParameters;
0038:
0039:        import org.xins.common.MandatoryArgumentChecker;
0040:        import org.xins.common.Utils;
0041:        import org.xins.common.collections.InvalidPropertyValueException;
0042:        import org.xins.common.collections.MissingRequiredPropertyException;
0043:        import org.xins.common.collections.PropertyReader;
0044:        import org.xins.common.io.IOReader;
0045:        import org.xins.common.manageable.InitializationException;
0046:        import org.xins.common.spec.APISpec;
0047:        import org.xins.common.spec.EntityNotFoundException;
0048:        import org.xins.common.spec.InvalidSpecificationException;
0049:        import org.xins.common.text.DateConverter;
0050:        import org.xins.common.text.TextUtils;
0051:
0052:        import org.xins.logdoc.ExceptionUtils;
0053:        import org.xins.logdoc.LogCentral;
0054:
0055:        /**
0056:         * XINS server engine. The engine is a delegate of the {@link APIServlet} that
0057:         * is responsible for initialization and request handling.
0058:         *
0059:         * @version $Revision: 1.112 $ $Date: 2007/09/24 12:46:37 $
0060:         * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
0061:         * @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
0062:         * @author <a href="mailto:mees.witteman@orange-ftgroup.com">Mees Witteman</a>
0063:         */
0064:        final class Engine {
0065:
0066:            /**
0067:             * Class used to convert dates to String.
0068:             */
0069:            private static final DateConverter DATE_CONVERTER = new DateConverter(
0070:                    true);
0071:
0072:            /**
0073:             * Perl 5 pattern compiler.
0074:             */
0075:            private static final Perl5Compiler PATTERN_COMPILER = new Perl5Compiler();
0076:
0077:            /**
0078:             * Property used to start JMX.
0079:             */
0080:            private static final String JMX_PROPERTY = "org.xins.server.jmx";
0081:
0082:            /**
0083:             * The state machine for this engine. Never <code>null</code>.
0084:             */
0085:            private final EngineStateMachine _stateMachine = new EngineStateMachine();
0086:
0087:            /**
0088:             * The starter of this engine. Never <code>null</code>.
0089:             */
0090:            private final EngineStarter _starter;
0091:
0092:            /**
0093:             * The stored servlet configuration object. Never <code>null</code>.
0094:             */
0095:            private final ServletConfig _servletConfig;
0096:
0097:            /**
0098:             * The API that this engine forwards requests to. Never <code>null</code>.
0099:             */
0100:            private final API _api;
0101:
0102:            /**
0103:             * Diagnostic context ID generator. Never <code>null</code>.
0104:             */
0105:            private ContextIDGenerator _contextIDGenerator;
0106:
0107:            /**
0108:             * The name of the API. Never <code>null</code>.
0109:             */
0110:            private String _apiName;
0111:
0112:            /**
0113:             * The manager for the runtime configuration file. Never <code>null</code>.
0114:             */
0115:            private final ConfigManager _configManager;
0116:
0117:            /**
0118:             * The manager for the calling conventions. This field can be and initially
0119:             * is <code>null</code>. This field is initialized by
0120:             * {@link #bootstrapAPI()}.
0121:             */
0122:            private CallingConventionManager _conventionManager;
0123:
0124:            /**
0125:             * Pattern which incoming diagnostic context identifiers must match. Can be
0126:             * <code>null</code> in case no pattern has been specified. Initially this
0127:             * field is indeed <code>null</code>.
0128:             */
0129:            private Pattern _contextIDPattern;
0130:
0131:            /**
0132:             * The SMD (Simple Method Description) of this API. This value is <code>null</code>
0133:             * until the meta function <i>_SMD</i> is called.
0134:             */
0135:            private String _smd;
0136:
0137:            /**
0138:             * Constructs a new <code>Engine</code> object.
0139:             *
0140:             * @param config
0141:             *    the {@link ServletConfig} object which contains build-time properties
0142:             *    for this servlet, cannot be <code>null</code>.
0143:             *
0144:             * @throws IllegalArgumentException
0145:             *    if <code>config == null</code>.
0146:             *
0147:             * @throws ServletException
0148:             *    if the engine could not be constructed.
0149:             */
0150:            Engine(ServletConfig config) throws IllegalArgumentException,
0151:                    ServletException {
0152:
0153:                // Check preconditions
0154:                MandatoryArgumentChecker.check("config", config);
0155:
0156:                // Construct the EngineStarter
0157:                _starter = new EngineStarter(config);
0158:
0159:                // Construct a configuration manager and store the servlet configuration
0160:                _configManager = new ConfigManager(this , config);
0161:                _servletConfig = config;
0162:
0163:                // Proceed to first actual stage
0164:                _stateMachine.setState(EngineState.BOOTSTRAPPING_FRAMEWORK);
0165:
0166:                // Read configuration details
0167:                _configManager.determineConfigFile();
0168:                _configManager.readRuntimeProperties();
0169:                if (!_configManager.propertiesRead()) {
0170:                    _stateMachine
0171:                            .setState(EngineState.FRAMEWORK_BOOTSTRAP_FAILED);
0172:                    throw new ServletException();
0173:                }
0174:
0175:                // Log boot messages
0176:                _starter.logBootMessages();
0177:
0178:                // Construct and bootstrap the API
0179:                _stateMachine.setState(EngineState.CONSTRUCTING_API);
0180:                try {
0181:                    _api = _starter.constructAPI();
0182:                } catch (ServletException se) {
0183:                    _stateMachine.setState(EngineState.API_CONSTRUCTION_FAILED);
0184:                    throw se;
0185:                }
0186:                boolean bootstrapped = bootstrapAPI();
0187:                if (!bootstrapped) {
0188:                    throw new ServletException();
0189:                }
0190:
0191:                // Done bootstrapping the framework
0192:                Log.log_3225(Library.getVersion());
0193:
0194:                // Initialize the configuration manager
0195:                _configManager.init();
0196:
0197:                // Check post-conditions
0198:                if (_api == null) {
0199:                    throw Utils.logProgrammingError("_api == null");
0200:                } else if (_apiName == null) {
0201:                    throw Utils.logProgrammingError("_apiName == null");
0202:                }
0203:            }
0204:
0205:            /**
0206:             * Bootstraps the API. The following steps will be performed:
0207:             *
0208:             * <ul>
0209:             *   <li>determine the API name;
0210:             *   <li>load the Logdoc, if available;
0211:             *   <li>bootstrap the API;
0212:             *   <li>construct and bootstrap the calling conventions;
0213:             *   <li>link the engine to the API;
0214:             *   <li>construct and bootstrap a context ID generator;
0215:             *   <li>perform JMX initialization.
0216:             * </ul>
0217:             *
0218:             * @return
0219:             *    <code>true</code> if the bootstrapping of the API succeeded,
0220:             *    <code>false</code> if it failed.
0221:             */
0222:            private boolean bootstrapAPI() {
0223:
0224:                // Proceed to next stage
0225:                _stateMachine.setState(EngineState.BOOTSTRAPPING_API);
0226:
0227:                // Make the API have a link to this Engine
0228:                _api.setEngine(this );
0229:
0230:                PropertyReader bootProps;
0231:                try {
0232:
0233:                    // Determine the name of the API
0234:                    _apiName = _starter.determineAPIName();
0235:
0236:                    // Load the Logdoc if available
0237:                    _starter.loadLogdoc();
0238:
0239:                    // Actually bootstrap the API
0240:                    bootProps = _starter.bootstrap(_api);
0241:
0242:                    // Handle any failures
0243:                } catch (ServletException se) {
0244:                    _stateMachine.setState(EngineState.API_BOOTSTRAP_FAILED);
0245:                    return false;
0246:                }
0247:
0248:                // Create the calling convention manager
0249:                _conventionManager = new CallingConventionManager(_api);
0250:
0251:                // Bootstrap the calling convention manager
0252:                try {
0253:                    _conventionManager.bootstrap(bootProps);
0254:
0255:                    // Missing required property
0256:                } catch (MissingRequiredPropertyException exception) {
0257:                    Log.log_3209(exception.getPropertyName(), exception
0258:                            .getDetail());
0259:                    return false;
0260:
0261:                    // Invalid property value
0262:                } catch (InvalidPropertyValueException exception) {
0263:                    Log.log_3210(exception.getPropertyName(), exception
0264:                            .getPropertyValue(), exception.getReason());
0265:                    return false;
0266:
0267:                    // Other bootstrap error
0268:                } catch (Throwable exception) {
0269:                    Log.log_3211(exception);
0270:                    return false;
0271:                }
0272:
0273:                // Construct a generator for diagnostic context IDs
0274:                _contextIDGenerator = new ContextIDGenerator(_api.getName());
0275:                try {
0276:                    _contextIDGenerator.bootstrap(bootProps);
0277:                } catch (Exception exception) {
0278:                    return false;
0279:                }
0280:
0281:                // Perform JMX initialization if asked
0282:                String enableJmx = _configManager.getRuntimeProperties().get(
0283:                        JMX_PROPERTY);
0284:                if ("true".equals(enableJmx)) {
0285:                    _starter.registerMBean(_api);
0286:                } else if (enableJmx != null && !enableJmx.equals("false")) {
0287:                    Log.log_3251(enableJmx);
0288:                    return false;
0289:                }
0290:
0291:                // Succeeded
0292:                return true;
0293:            }
0294:
0295:            /**
0296:             * Initializes the API using the current runtime settings. This method
0297:             * should be called whenever the runtime properties changed.
0298:             *
0299:             * @return
0300:             *    <code>true</code> if the initialization succeeded, otherwise
0301:             *    <code>false</code>.
0302:             */
0303:            boolean initAPI() {
0304:
0305:                _stateMachine.setState(EngineState.INITIALIZING_API);
0306:
0307:                // Determine the locale for logging
0308:                boolean localeInitialized = _configManager.determineLogLocale();
0309:                if (!localeInitialized) {
0310:                    _stateMachine
0311:                            .setState(EngineState.API_INITIALIZATION_FAILED);
0312:                    return false;
0313:                }
0314:
0315:                // Check that the runtime properties were correct
0316:                if (!_configManager.propertiesRead()) {
0317:                    _stateMachine
0318:                            .setState(EngineState.API_INITIALIZATION_FAILED);
0319:                    return false;
0320:                }
0321:
0322:                // Determine the current runtime properties
0323:                PropertyReader properties = _configManager
0324:                        .getRuntimeProperties();
0325:
0326:                // Determine at what level should the stack traces be displayed
0327:                String stackTraceAtMessageLevel = properties
0328:                        .get(LogCentral.LOG_STACK_TRACE_AT_MESSAGE_LEVEL);
0329:                if ("true".equals(stackTraceAtMessageLevel)) {
0330:                    LogCentral.setStackTraceAtMessageLevel(true);
0331:                } else if ("false".equals(stackTraceAtMessageLevel)) {
0332:                    LogCentral.setStackTraceAtMessageLevel(false);
0333:                } else if (stackTraceAtMessageLevel != null) {
0334:                    // XXX: Report this error in some way
0335:                    _stateMachine
0336:                            .setState(EngineState.API_INITIALIZATION_FAILED);
0337:                    return false;
0338:                }
0339:
0340:                boolean succeeded = false;
0341:
0342:                try {
0343:
0344:                    // Determine filter for incoming diagnostic context IDs
0345:                    _contextIDPattern = determineContextIDPattern(properties);
0346:
0347:                    // Initialize the diagnostic context ID generator
0348:                    _contextIDGenerator.init(properties);
0349:
0350:                    // Initialize the API
0351:                    _api.init(properties);
0352:
0353:                    // Initialize the default calling convention for this API
0354:                    _conventionManager.init(properties);
0355:
0356:                    succeeded = true;
0357:
0358:                    // Missing required property
0359:                } catch (MissingRequiredPropertyException exception) {
0360:                    Log.log_3411(exception.getPropertyName(), exception
0361:                            .getDetail());
0362:
0363:                    // Invalid property value
0364:                } catch (InvalidPropertyValueException exception) {
0365:                    Log.log_3412(exception.getPropertyName(), exception
0366:                            .getPropertyValue(), exception.getReason());
0367:
0368:                    // Initialization of API failed for some other reason
0369:                } catch (InitializationException exception) {
0370:                    Log.log_3413(exception);
0371:
0372:                    // Other error
0373:                } catch (Throwable exception) {
0374:                    Log.log_3414(exception);
0375:
0376:                    // Always leave the object in a well-known state
0377:                } finally {
0378:                    if (succeeded) {
0379:                        _stateMachine.setState(EngineState.READY);
0380:                    } else {
0381:                        _stateMachine
0382:                                .setState(EngineState.API_INITIALIZATION_FAILED);
0383:                    }
0384:                }
0385:
0386:                return succeeded;
0387:            }
0388:
0389:            /**
0390:             * Determines the filter for diagnostic context identifiers.
0391:             *
0392:             * @param properties
0393:             *    the runtime properties to retrieve information from, cannot be
0394:             *    <code>null</code>.
0395:             *
0396:             * @return
0397:             *    the filter as a {@link Pattern} object, or <code>null</code> if no
0398:             *    filter is specified.
0399:             *
0400:             * @throws IllegalArgumentException
0401:             *    if <code>properties == null</code>.
0402:             *
0403:             * @throws InvalidPropertyValueException
0404:             *    if the value for the filter property is considered invalid.
0405:             */
0406:            private Pattern determineContextIDPattern(PropertyReader properties)
0407:                    throws IllegalArgumentException,
0408:                    InvalidPropertyValueException {
0409:
0410:                // Check preconditions
0411:                MandatoryArgumentChecker.check("properties", properties);
0412:
0413:                // Determine pattern string
0414:                // XXX: Store "org.xins.server.contextID.filter" in a constant
0415:                String propName = "org.xins.server.contextID.filter";
0416:                String propValue = properties.get(propName);
0417:
0418:                // If the property value is empty, then there is no pattern
0419:                Pattern pattern;
0420:                if (TextUtils.isEmpty(propValue)) {
0421:                    pattern = null;
0422:                    Log.log_3431();
0423:
0424:                    // Otherwise we must provide a Pattern instance
0425:                } else {
0426:
0427:                    // Convert the string to a Pattern
0428:                    try {
0429:                        // XXX: Why is the pattern made case-insensitive?
0430:                        int mask = Perl5Compiler.READ_ONLY_MASK
0431:                                | Perl5Compiler.CASE_INSENSITIVE_MASK;
0432:                        pattern = PATTERN_COMPILER.compile(propValue, mask);
0433:                        Log.log_3432(propValue);
0434:
0435:                        // Malformed pattern indicates an invalid value
0436:                    } catch (MalformedPatternException exception) {
0437:                        Log.log_3433(propValue);
0438:                        InvalidPropertyValueException ipve;
0439:                        ipve = new InvalidPropertyValueException(propName,
0440:                                propValue);
0441:                        ExceptionUtils.setCause(ipve, exception);
0442:                        throw ipve;
0443:                    }
0444:                }
0445:
0446:                return pattern;
0447:            }
0448:
0449:            /**
0450:             * Handles a request to this servlet (wrapper method). If any of the
0451:             * arguments is <code>null</code>, then the behaviour of this method is
0452:             * undefined.
0453:             *
0454:             * @param request
0455:             *    the servlet request, should not be <code>null</code>.
0456:             *
0457:             * @param response
0458:             *    the servlet response, should not be <code>null</code>.
0459:             *
0460:             * @throws IOException
0461:             *    in case of an I/O error.
0462:             */
0463:            void service(HttpServletRequest request,
0464:                    HttpServletResponse response) throws IOException {
0465:
0466:                // Set the correct character encoding for the request
0467:                if (request.getCharacterEncoding() == null) {
0468:                    request.setCharacterEncoding("UTF-8");
0469:                }
0470:
0471:                // Associate the current diagnostic context identifier with this thread
0472:                String contextID = determineContextID(request);
0473:                NDC.push(contextID);
0474:
0475:                // Handle the request
0476:                try {
0477:                    doService(request, response);
0478:
0479:                    // Catch and log all exceptions
0480:                } catch (Throwable exception) {
0481:                    Log.log_3003(exception);
0482:
0483:                    // Finally always disassociate the diagnostic context identifier from
0484:                    // this thread
0485:                } finally {
0486:                    NDC.pop();
0487:                    NDC.remove();
0488:                }
0489:            }
0490:
0491:            /**
0492:             * Returns an applicable diagnostic context identifier. If the request
0493:             * already specifies a diagnostic context identifier, then that will be
0494:             * used. Otherwise a new one will be generated.
0495:             *
0496:             * @param request
0497:             *    the HTTP servlet request, should not be <code>null</code>.
0498:             *
0499:             * @return
0500:             *    the diagnostic context identifier, never <code>null</code> and never
0501:             *    an empty string.
0502:             */
0503:            private String determineContextID(HttpServletRequest request) {
0504:
0505:                // See if the request already specifies a diagnostic context identifier
0506:                // XXX: Store "_context" in a constant
0507:                String contextID = request.getParameter("_context");
0508:                if (TextUtils.isEmpty(contextID)) {
0509:                    contextID = _contextIDGenerator.generate();
0510:                    Log.log_3583(contextID);
0511:
0512:                    // Indeed there is a context ID in the request, make sure it's valid
0513:                } else {
0514:
0515:                    // Valid context ID
0516:                    if (isValidContextID(contextID)) {
0517:                        Log.log_3581(contextID);
0518:
0519:                        // Invalid context ID
0520:                    } else {
0521:                        Log.log_3582(contextID);
0522:                        contextID = null;
0523:                    }
0524:                }
0525:
0526:                return contextID;
0527:            }
0528:
0529:            /**
0530:             * Determines if the specified incoming context identifier is considered
0531:             * valid.
0532:             *
0533:             * @param contextID
0534:             *    the incoming diagnostic context identifier, should not be
0535:             *    <code>null</code>.
0536:             *
0537:             * @return
0538:             *    <code>true</code> if <code>contextID</code> is considered acceptable,
0539:             *    <code>false</code> if it is considered unacceptable.
0540:             */
0541:            private boolean isValidContextID(String contextID) {
0542:
0543:                // If a filter is specified, validate that the ID matches it
0544:                if (_contextIDPattern != null) {
0545:                    Perl5Matcher matcher = new Perl5Matcher();
0546:                    return matcher.matches(contextID, _contextIDPattern);
0547:
0548:                    // No filter is specified, everything is allowed
0549:                } else {
0550:                    return true;
0551:                }
0552:            }
0553:
0554:            /**
0555:             * Handles a request to this servlet (implementation method). If any of the
0556:             * arguments is <code>null</code>, then the behaviour of this method is
0557:             * undefined.
0558:             *
0559:             * <p>This method is called from the corresponding wrapper method,
0560:             * {@link #service(HttpServletRequest,HttpServletResponse)}.
0561:             *
0562:             * @param request
0563:             *    the servlet request, should not be <code>null</code>.
0564:             *
0565:             * @param response
0566:             *    the servlet response, should not be <code>null</code>.
0567:             *
0568:             * @throws IOException
0569:             *    in case of an I/O error.
0570:             */
0571:            private void doService(HttpServletRequest request,
0572:                    HttpServletResponse response) throws IOException {
0573:
0574:                // Determine current time
0575:                long start = System.currentTimeMillis();
0576:
0577:                // Log that we have received an HTTP request
0578:                String remoteIP = request.getRemoteAddr();
0579:                String method = request.getMethod();
0580:                String path = request.getRequestURI();
0581:                String queryString = request.getQueryString();
0582:                Log.log_3521(remoteIP, method, path, queryString);
0583:
0584:                // If the current state is not usable, then return an error immediately
0585:                EngineState state = _stateMachine.getState();
0586:                if (!state.allowsInvocations()) {
0587:                    handleUnusableState(state, request, response);
0588:
0589:                    // Support the HTTP method "OPTIONS"
0590:                } else if ("OPTIONS".equals(method)) {
0591:                    if ("*".equals(path)) {
0592:                        handleOptions(null, request, response);
0593:                    } else {
0594:                        delegateToCC(start, request, response);
0595:                    }
0596:
0597:                    // The request should be handled by a calling convention
0598:                } else {
0599:                    delegateToCC(start, request, response);
0600:                }
0601:            }
0602:
0603:            /**
0604:             * Handles an unprocessable request (low-level function). The response is
0605:             * filled for the request.
0606:             *
0607:             * @param request
0608:             *    the HTTP request, cannot be <code>null</code>.
0609:             *
0610:             * @param response
0611:             *    the HTTP request, cannot be <code>null</code>.
0612:             *
0613:             * @param statusCode
0614:             *    the HTTP status code to return.
0615:             *
0616:             * @param reason
0617:             *    explanation, can be <code>null</code>.
0618:             *
0619:             * @param exception
0620:             *    the exception thrown, can be <code>null</code>.
0621:             *
0622:             * @throws IOException
0623:             *    in case of an I/O error.
0624:             */
0625:            private void handleUnprocessableRequest(HttpServletRequest request,
0626:                    HttpServletResponse response, int statusCode,
0627:                    String reason, Throwable exception) throws IOException {
0628:
0629:                // Log
0630:                Log.log_3523(exception, request.getRemoteAddr(), request
0631:                        .getMethod(), request.getRequestURI(), request
0632:                        .getQueryString(), statusCode, reason);
0633:
0634:                // Send the HTTP status code to the client
0635:                response.sendError(statusCode);
0636:            }
0637:
0638:            /**
0639:             * Handles a request that comes in while function invocations are currently
0640:             * not allowed.
0641:             *
0642:             * @param state
0643:             *    the current state, cannot be <code>null</code>.
0644:             *
0645:             * @param request
0646:             *    the HTTP request, cannot be <code>null</code>.
0647:             *
0648:             * @param response
0649:             *    the HTTP response to fill, cannot be <code>null</code>.
0650:             *
0651:             * @throws IOException
0652:             *    in case of an I/O error.
0653:             */
0654:            private void handleUnusableState(EngineState state,
0655:                    HttpServletRequest request, HttpServletResponse response)
0656:                    throws IOException {
0657:
0658:                // Log and respond
0659:                int statusCode = state.isError() ? HttpServletResponse.SC_INTERNAL_SERVER_ERROR
0660:                        : HttpServletResponse.SC_SERVICE_UNAVAILABLE;
0661:                String reason = "XINS/Java Server Framework engine state \""
0662:                        + state + "\" does not allow incoming requests.";
0663:                handleUnprocessableRequest(request, response, statusCode,
0664:                        reason, null);
0665:            }
0666:
0667:            /**
0668:             * Delegates the specified incoming request to the appropriate
0669:             * <code>CallingConvention</code>. The request may either be a function
0670:             * invocation or an <em>OPTIONS</em> request.
0671:             *
0672:             * @param start
0673:             *    timestamp indicating when the call was received by the framework, in
0674:             *    milliseconds since the
0675:             *    <a href="http://en.wikipedia.org/wiki/Unix_Epoch">UNIX Epoch</a>.
0676:             *
0677:             * @param request
0678:             *    the servlet request, should not be <code>null</code>.
0679:             *
0680:             * @param response
0681:             *    the servlet response, should not be <code>null</code>.
0682:             *
0683:             * @throws IOException
0684:             *    in case of an I/O error.
0685:             */
0686:            private void delegateToCC(long start, HttpServletRequest request,
0687:                    HttpServletResponse response) throws IOException {
0688:
0689:                // Determine the calling convention to use
0690:                CallingConvention cc = determineCC(request, response);
0691:
0692:                // If it is null, then there was an error. This error will have been
0693:                // handled completely, including logging and response output.
0694:                if (cc != null) {
0695:
0696:                    // Handle OPTIONS calls separately
0697:                    String method = request.getMethod();
0698:                    if ("OPTIONS".equals(method)) {
0699:                        handleOptions(cc, request, response);
0700:
0701:                        // Non-OPTIONS requests are function invocations
0702:                    } else {
0703:                        invokeFunction(start, cc, request, response);
0704:                    }
0705:                }
0706:            }
0707:
0708:            /**
0709:             * Determines which calling convention should be used for the specified
0710:             * request. In case of an error, an error response will be produced and
0711:             * sent to the client.
0712:             *
0713:             * @param request
0714:             *    the HTTP request for which to determine the calling convention to use
0715:             *    cannot be <code>null</code>.
0716:             *
0717:             * @param response
0718:             *    the HTTP response, cannot be <code>null</code>.
0719:             *
0720:             * @return
0721:             *    the {@link CallingConvention} to use, or <code>null</code> if the
0722:             *    calling convention to use could not be determined.
0723:             *
0724:             * @throws IOException
0725:             *    in case of an I/O error.
0726:             */
0727:            private final CallingConvention determineCC(
0728:                    HttpServletRequest request, HttpServletResponse response)
0729:                    throws IOException {
0730:
0731:                // Determine the calling convention; if an existing calling convention
0732:                // is specified in the request, then use that, otherwise use the default
0733:                // calling convention for this engine
0734:                CallingConvention cc = null;
0735:                try {
0736:                    cc = _conventionManager.getCallingConvention(request);
0737:
0738:                    // Only an InvalidRequestException is expected. If a different kind of
0739:                    // exception is received, then that is considered a programming error.
0740:                } catch (Throwable exception) {
0741:                    int statusCode;
0742:                    String reason;
0743:                    if (exception instanceof  InvalidRequestException) {
0744:
0745:                        String method = request.getMethod();
0746:                        String ccName = request
0747:                                .getParameter(CallingConventionManager.CALLING_CONVENTION_PARAMETER);
0748:                        // Check if the method is known by at least one CC (otherwise 501)
0749:                        if (!_conventionManager.getSupportedMethods().contains(
0750:                                method)) {
0751:                            statusCode = HttpServletResponse.SC_NOT_IMPLEMENTED;
0752:                            reason = "The HTTP method \""
0753:                                    + method
0754:                                    + "\" is not known by any of the usable calling conventions.";
0755:
0756:                            // Check if the method is known for the specified CC (otherwise 405)
0757:                        } else if (ccName != null
0758:                                && _conventionManager
0759:                                        .getCallingConvention2(ccName) != null
0760:                                && !Arrays.asList(
0761:                                        _conventionManager
0762:                                                .getCallingConvention2(ccName)
0763:                                                .getSupportedMethods(request))
0764:                                        .contains(method)) {
0765:                            statusCode = HttpServletResponse.SC_METHOD_NOT_ALLOWED;
0766:                            reason = "The HTTP method \""
0767:                                    + method
0768:                                    + "\" is not allowed for the calling convention \""
0769:                                    + ccName + "\".";
0770:                        } else {
0771:                            statusCode = HttpServletResponse.SC_BAD_REQUEST;
0772:                            reason = "Unable to activate appropriate calling convention";
0773:                            String exceptionMessage = exception.getMessage();
0774:                            if (TextUtils.isEmpty(exceptionMessage)) {
0775:                                reason += '.';
0776:                            } else {
0777:                                reason += ": " + exceptionMessage;
0778:                            }
0779:                        }
0780:                    } else {
0781:                        statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
0782:                        reason = "Internal error while trying to determine "
0783:                                + "appropriate calling convention.";
0784:                    }
0785:
0786:                    // Log and respond
0787:                    handleUnprocessableRequest(request, response, statusCode,
0788:                            reason, exception);
0789:                }
0790:
0791:                return cc;
0792:            }
0793:
0794:            /**
0795:             * Invokes a function, using the specified calling convention to from an
0796:             * HTTP request and to an HTTP response.
0797:             *
0798:             * @param start
0799:             *    timestamp indicating when the call was received by the framework, in
0800:             *    milliseconds since the
0801:             *    <a href="http://en.wikipedia.org/wiki/Unix_Epoch">UNIX Epoch</a>.
0802:             *
0803:             * @param cc
0804:             *    the calling convention to use, cannot be <code>null</code>.
0805:             *
0806:             * @param request
0807:             *    the HTTP request, cannot be <code>null</code>.
0808:             *
0809:             * @param response
0810:             *    the HTTP response, cannot be <code>null</code>.
0811:             *
0812:             * @throws IOException
0813:             *    in case of an I/O error.
0814:             */
0815:            private void invokeFunction(long start, CallingConvention cc,
0816:                    HttpServletRequest request, HttpServletResponse response)
0817:                    throws IOException {
0818:
0819:                // Convert the HTTP request to a XINS request
0820:                FunctionRequest xinsRequest;
0821:                try {
0822:                    xinsRequest = cc.convertRequest(request);
0823:
0824:                    // Only an InvalidRequestException or a FunctionNotSpecifiedException is
0825:                    // expected. If a different kind of exception is received, then that is
0826:                    // considered a programming error.
0827:                } catch (Throwable exception) {
0828:
0829:                    int statusCode;
0830:                    String reason;
0831:
0832:                    if (exception instanceof  InvalidRequestException) {
0833:                        statusCode = HttpServletResponse.SC_BAD_REQUEST;
0834:                        reason = "Calling convention \""
0835:                                + cc.getClass().getName()
0836:                                + "\" cannot process the request";
0837:                        String exceptionMessage = exception.getMessage();
0838:                        if (!TextUtils.isEmpty(exceptionMessage)) {
0839:                            reason += ": " + exceptionMessage;
0840:                        } else {
0841:                            reason += '.';
0842:                        }
0843:                    } else if (exception instanceof  FunctionNotSpecifiedException) {
0844:                        statusCode = HttpServletResponse.SC_NOT_FOUND;
0845:                        reason = "Cannot determine which function to invoke.";
0846:                    } else {
0847:                        statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
0848:                        reason = "Internal error.";
0849:                    }
0850:
0851:                    // Log and respond
0852:                    handleUnprocessableRequest(request, response, statusCode,
0853:                            reason, exception);
0854:
0855:                    return;
0856:                }
0857:
0858:                // Do not handle the call if the API is disabled
0859:                if (_api.isDisabled()
0860:                        && !"_EnableAPI".equals(xinsRequest.getFunctionName())) {
0861:                    response
0862:                            .sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
0863:                    return;
0864:                }
0865:
0866:                // Call the function
0867:                FunctionResult result;
0868:                try {
0869:                    result = _api.handleCall(start, xinsRequest, request
0870:                            .getRemoteAddr(), cc);
0871:
0872:                    // The only expected exceptions are NoSuchFunctionException and
0873:                    // AccessDeniedException. Other exceptions are considered to indicate
0874:                    // a programming error.
0875:                } catch (Throwable exception) {
0876:
0877:                    int statusCode;
0878:                    String reason;
0879:
0880:                    // Access denied
0881:                    if (exception instanceof  AccessDeniedException) {
0882:                        statusCode = HttpServletResponse.SC_FORBIDDEN;
0883:                        reason = "Access is denied.";
0884:
0885:                        // No such function
0886:                    } else if (exception instanceof  NoSuchFunctionException) {
0887:                        statusCode = HttpServletResponse.SC_NOT_FOUND;
0888:                        reason = "The specified function \""
0889:                                + xinsRequest.getFunctionName()
0890:                                + "\" is unknown.";
0891:
0892:                        // Internal error
0893:                    } else {
0894:                        statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
0895:                        reason = "Internal error while processing function call.";
0896:                    }
0897:
0898:                    // Log and respond
0899:                    handleUnprocessableRequest(request, response, statusCode,
0900:                            reason, exception);
0901:
0902:                    return;
0903:                }
0904:
0905:                // Shortcut for the _WSDL meta function
0906:                if (xinsRequest.getFunctionName().equals("_WSDL")) {
0907:                    handleWsdlRequest(response);
0908:                    return;
0909:                }
0910:
0911:                // Shortcut for the _SMD meta function
0912:                if (xinsRequest.getFunctionName().equals("_SMD")) {
0913:                    handleSmdRequest(request, response);
0914:                    return;
0915:                }
0916:
0917:                // Convert the XINS result to an HTTP response
0918:                try {
0919:                    cc.convertResult(result, response, request);
0920:
0921:                    // NOTE: If the convertResult method throws an exception, then it
0922:                    //       will have been logged within the CallingConvention class
0923:                    //       already.
0924:                } catch (Throwable exception) {
0925:                    response
0926:                            .sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
0927:                    return;
0928:                }
0929:            }
0930:
0931:            /**
0932:             * Handles an <em>OPTIONS</em> request for a specific calling convention
0933:             * or for the resource <code>*</code> if no calling convention is given.
0934:             *
0935:             * @param cc
0936:             *    the calling convention, can be <code>null</code>. if no calling
0937:             *    convention is specified all possible method names are returned.
0938:             *
0939:             * @param request
0940:             *    the request, never <code>null</code>.
0941:             *
0942:             * @param response
0943:             *    the response to fill, never <code>null</code>.
0944:             *
0945:             * @throws IOException
0946:             *    in case of an I/O error.
0947:             */
0948:            private void handleOptions(CallingConvention cc,
0949:                    HttpServletRequest request, HttpServletResponse response)
0950:                    throws IOException {
0951:
0952:                // Create the string with the supported HTTP methods
0953:                String[] methods;
0954:                if (cc != null) {
0955:                    methods = cc.getSupportedMethods(request);
0956:                } else {
0957:                    Set supportedMethods = _conventionManager
0958:                            .getSupportedMethods();
0959:                    methods = (String[]) supportedMethods
0960:                            .toArray(new String[supportedMethods.size()]);
0961:                }
0962:                String methodsList = "OPTIONS";
0963:                for (int i = 0; i < methods.length; i++) {
0964:
0965:                    methodsList += ", " + methods[i];
0966:                }
0967:
0968:                // Return the full response
0969:                response.setStatus(HttpServletResponse.SC_OK);
0970:                response.setHeader("Accept", methodsList);
0971:                response.setContentLength(0);
0972:            }
0973:
0974:            /**
0975:             * Destroys this servlet. A best attempt will be made to release all
0976:             * resources.
0977:             *
0978:             * <p>After this method has finished, it will set the state to
0979:             * <em>disposed</em>. In that state no more requests will be handled.
0980:             */
0981:            void destroy() {
0982:
0983:                // Log: Shutting down XINS/Java Server Framework
0984:                Log.log_3600();
0985:
0986:                // Set the state temporarily to DISPOSING
0987:                _stateMachine.setState(EngineState.DISPOSING);
0988:
0989:                // Destroy the configuration manager
0990:                if (_configManager != null) {
0991:                    try {
0992:                        _configManager.destroy();
0993:                    } catch (Throwable exception) {
0994:                        Utils.logIgnoredException(exception);
0995:                    }
0996:                }
0997:
0998:                // Destroy the API
0999:                if (_api != null) {
1000:                    try {
1001:                        _api.deinit();
1002:                    } catch (Throwable exception) {
1003:                        Utils.logIgnoredException(exception);
1004:                    }
1005:                }
1006:
1007:                // Set the state to DISPOSED
1008:                _stateMachine.setState(EngineState.DISPOSED);
1009:
1010:                // Log: Shutdown completed
1011:                Log.log_3602();
1012:            }
1013:
1014:            /**
1015:             * Re-initializes the configuration file listener if there is no file
1016:             * watcher; otherwise interrupts the file watcher.
1017:             */
1018:            void reloadPropertiesIfChanged() {
1019:                _configManager.reloadPropertiesIfChanged();
1020:            }
1021:
1022:            /**
1023:             * Returns the <code>ServletConfig</code> object which contains the
1024:             * build-time properties for this servlet. The returned
1025:             * {@link ServletConfig} object is the one that was passed to the
1026:             * constructor.
1027:             *
1028:             * @return
1029:             *    the {@link ServletConfig} object that was used to initialize this
1030:             *    servlet, never <code>null</code>.
1031:             */
1032:            ServletConfig getServletConfig() {
1033:                return _servletConfig;
1034:            }
1035:
1036:            /**
1037:             * Gets the location of a file or a directory included in the WAR file.
1038:             *
1039:             * @param path
1040:             *    the relative path in the WAR to locate the file or the directory.
1041:             *
1042:             * @return
1043:             *    the String representation of the URL of the given path or <code>null</code>
1044:             *    if the path cannot be found.
1045:             */
1046:            String getFileLocation(String path) {
1047:                String baseURL = null;
1048:                ServletConfig config = getServletConfig();
1049:                ServletContext context = config.getServletContext();
1050:                try {
1051:                    String realPath = context.getRealPath(path);
1052:                    if (realPath != null) {
1053:                        baseURL = new File(realPath).toURL().toExternalForm();
1054:                    } else {
1055:                        URL pathURL = context.getResource(path);
1056:                        if (pathURL == null) {
1057:                            pathURL = getClass().getResource(path);
1058:                        }
1059:                        if (pathURL != null) {
1060:                            baseURL = pathURL.toExternalForm();
1061:                        } else {
1062:                            Log.log_3517(path, null);
1063:                        }
1064:                    }
1065:                } catch (MalformedURLException muex) {
1066:                    // Let the base URL be null
1067:                    Log.log_3517(path, muex.getMessage());
1068:                }
1069:                return baseURL;
1070:            }
1071:
1072:            /**
1073:             * Gets the resource in the WAR file.
1074:             *
1075:             * @param path
1076:             *    the path for the resource, cannot be <code>null</code> and should start with /.
1077:             *
1078:             * @return
1079:             *    the InputStream to use to read this resource or <code>null</code> if
1080:             *    the resource cannot be found.
1081:             *
1082:             * @throws IllegalArgumentException
1083:             *    if <code>path == null</code> or if the path doesn't start with /.
1084:             *
1085:             * @since XINS 2.1.
1086:             */
1087:            InputStream getResourceAsStream(String path)
1088:                    throws IllegalArgumentException {
1089:                MandatoryArgumentChecker.check("path", path);
1090:                if (!path.startsWith("/")) {
1091:                    throw new IllegalArgumentException("The path '" + path
1092:                            + "' should start with /.");
1093:                }
1094:                String resource = getFileLocation(path);
1095:                if (resource != null) {
1096:                    try {
1097:                        return new URL(resource).openStream();
1098:                    } catch (IOException ioe) {
1099:                        // Fall through and return null
1100:                    }
1101:                }
1102:                return null;
1103:            }
1104:
1105:            /**
1106:             * Logs the specified transaction.
1107:             *
1108:             * @param request
1109:             *    the {@link FunctionRequest}, should not be <code>null</code>.
1110:             *
1111:             * @param result
1112:             *    the {@link FunctionResult}, should not be <code>null</code>.
1113:             *
1114:             * @param ip
1115:             *    the client IP address, should not be <code>null</code>.
1116:             *
1117:             * @param start
1118:             *    the start of the call, a timestamp as milliseconds since the Epoch.
1119:             *
1120:             * @param duration
1121:             *    the duration of the call, in milliseconds.
1122:             *
1123:             * @throws NullPointerException
1124:             *    if <code>request == null || result  == null</code>.
1125:             */
1126:            static void logTransaction(FunctionRequest request,
1127:                    FunctionResult result, String ip, long start, long duration)
1128:                    throws NullPointerException {
1129:
1130:                // Determine the name of the function that was requested to be invoked
1131:                String functionName = request.getFunctionName();
1132:
1133:                // Determine error code, fallback is a zero character
1134:                String code = result.getErrorCode();
1135:                if (code == null || code.length() < 1) {
1136:                    code = "0";
1137:                }
1138:
1139:                // Prepare for transaction logging
1140:                String serStart = DATE_CONVERTER.format(start);
1141:                Object inParams = new FormattedParameters(request
1142:                        .getParameters(), request.getDataElement());
1143:                Object outParams = new FormattedParameters(result
1144:                        .getParameters(), result.getDataElement());
1145:
1146:                // Log transaction before returning the result
1147:                Log.log_3540(serStart, ip, functionName, duration, code,
1148:                        inParams, outParams);
1149:                Log.log_3541(serStart, ip, functionName, duration, code);
1150:            }
1151:
1152:            /**
1153:             * Handles the request for the _WSDL meta function.
1154:             *
1155:             * @param response
1156:             *    the response to fill, never <code>null</code>.
1157:             *
1158:             * @throws IOException
1159:             *    if the WSDL cannot be found in the WAR file.
1160:             */
1161:            private void handleWsdlRequest(HttpServletResponse response)
1162:                    throws IOException {
1163:                String wsdlLocation = getFileLocation("/WEB-INF/" + _apiName
1164:                        + ".wsdl");
1165:                if (wsdlLocation == null) {
1166:                    throw new FileNotFoundException("/WEB-INF/" + _apiName
1167:                            + ".wsdl not found.");
1168:                }
1169:                InputStream inputXSLT = new URL(wsdlLocation).openStream();
1170:                String wsdlText = IOReader.readFully(inputXSLT);
1171:
1172:                // Write the text to the output
1173:                response.setContentType("text/xml");
1174:                response.setStatus(HttpServletResponse.SC_OK);
1175:                Writer outputResponse = response.getWriter();
1176:                outputResponse.write(wsdlText);
1177:                outputResponse.close();
1178:            }
1179:
1180:            /**
1181:             * Handles the request for the _SMD meta function.
1182:             *
1183:             * @param request
1184:             *    the request asking for the SMD, never <code>null</code>.
1185:             *
1186:             * @param response
1187:             *    the response to fill, never <code>null</code>.
1188:             *
1189:             * @throws IOException
1190:             *    if the SMD cannot be created or sent to the output stream.
1191:             */
1192:            private void handleSmdRequest(HttpServletRequest request,
1193:                    HttpServletResponse response) throws IOException {
1194:                if (_smd == null) {
1195:                    try {
1196:                        _smd = createSMD(request);
1197:                    } catch (Exception ex) {
1198:                        throw new IOException(ex.getMessage());
1199:                    }
1200:                }
1201:
1202:                // Write the text to the output
1203:                response
1204:                        .setContentType(JSONRPCCallingConvention.RESPONSE_CONTENT_TYPE);
1205:                response.setStatus(HttpServletResponse.SC_OK);
1206:                Writer outputResponse = response.getWriter();
1207:                outputResponse.write(_smd);
1208:                outputResponse.close();
1209:            }
1210:
1211:            /**
1212:             * Creates the SMD for this API.
1213:             * More info at http://dojo.jot.com/SMD and
1214:             * http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book9.
1215:             *
1216:             * @param request
1217:             *    the request asking for the SMD, never <code>null</code>.
1218:             *
1219:             * @return
1220:             *    the String representation of the SMD JSON Object, never <code>null</code>.
1221:             *
1222:             * @throws InvalidSpecificationException
1223:             *    if the specification of the API cannot be found.
1224:             *
1225:             * @throws EntityNotFoundException
1226:             *    if the specification of a function cannot be found.
1227:             *
1228:             * @throws JSONException
1229:             *    if the JSON object cannot be created correctly.
1230:             */
1231:            private String createSMD(HttpServletRequest request)
1232:                    throws InvalidSpecificationException,
1233:                    EntityNotFoundException, JSONException {
1234:                APISpec apiSpec = _api.getAPISpecification();
1235:                JSONObject smdObject = new JSONObject();
1236:                smdObject.put("SMDVersion", ".1");
1237:                smdObject.put("objectName", _api.getName());
1238:                smdObject.put("serviceType", "JSON-RPC");
1239:                String requestURL = request.getRequestURI();
1240:                if (requestURL.indexOf('?') != -1) {
1241:                    requestURL = requestURL.substring(0, requestURL
1242:                            .indexOf('?'));
1243:                }
1244:                smdObject.put("serviceURL", requestURL
1245:                        + "?_convention=_xins-jsonrpc");
1246:                JSONArray methods = new JSONArray();
1247:                Iterator itFunctions = _api.getFunctionList().iterator();
1248:                while (itFunctions.hasNext()) {
1249:                    String nextFunction = ((Function) itFunctions.next())
1250:                            .getName();
1251:                    JSONObject functionObject = new JSONObject();
1252:                    functionObject.put("name", nextFunction);
1253:                    JSONArray inputParameters = new JSONArray();
1254:                    Map inputParamSpecs = apiSpec.getFunction(nextFunction)
1255:                            .getInputParameters();
1256:                    Iterator itParamNames = inputParamSpecs.keySet().iterator();
1257:                    while (itParamNames.hasNext()) {
1258:                        String nextParam = (String) itParamNames.next();
1259:                        JSONObject paramObject = new JSONObject();
1260:                        paramObject.put("name", nextParam);
1261:                        inputParameters.put(paramObject);
1262:                    }
1263:                    functionObject.put("parameters", inputParameters);
1264:                    methods.put(functionObject);
1265:                }
1266:                smdObject.put("methods", methods);
1267:                return smdObject.toString();
1268:            }
1269:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.