aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmso-ticketmgt/src/main/resources/logback.xml377
-rw-r--r--cmso-ticketmgt/src/main/resources/logmessages.properties25
-rw-r--r--cmso-ticketmgt/src/main/script/TagVersion.groovy40
3 files changed, 442 insertions, 0 deletions
diff --git a/cmso-ticketmgt/src/main/resources/logback.xml b/cmso-ticketmgt/src/main/resources/logback.xml
new file mode 100644
index 0000000..2fd5329
--- /dev/null
+++ b/cmso-ticketmgt/src/main/resources/logback.xml
@@ -0,0 +1,377 @@
+<!--
+ Copyright © 2017-2018 AT&T Intellectual Property.
+ Modifications Copyright © 2018 IBM.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Unless otherwise specified, all documentation contained herein is licensed
+ under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ you may not use this documentation except in compliance with the License.
+ You may obtain a copy of the License at
+
+ https://creativecommons.org/licenses/by/4.0/
+
+ Unless required by applicable law or agreed to in writing, documentation
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<configuration scan="true" scanPeriod="3 seconds" debug="false">
+
+ <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
+ <property resource="application.properties" />
+ <property name="namespace" value="${kubernetes.namespace}"></property>
+ <!-- directory path for all other type logs -->
+ <property name="logDir" value="logs" />
+ <!-- directory path for debugging type logs -->
+ <property name="debugDir" value="debug-logs" />
+ <property name="logDirectory" value="${logDir}" />
+ <property name="debugLogDirectory" value="${debugDir}" />
+ <!-- log file names -->
+ <property name="generalLogName" value="application" />
+ <property name="securityLogName" value="security" />
+ <property name="performanceLogName" value="performance" />
+ <property name="serverLogName" value="server" />
+ <property name="policyLogName" value="policy" />
+ <property name="errorLogName" value="error" />
+ <property name="metricsLogName" value="metrics" />
+ <property name="auditLogName" value="audit" />
+ <property name="debugLogName" value="debug" />
+
+ <property name="defaultPattern"
+ value="%d{yyyy-MM-dd HH:mm:ss.SSS} $ version: %X{version} threadId: {PID:- } %-5level namespace:${namespace} %logger{20} [ hostname: %X{hostname} serviceName: %X{serviceName} version: %X{version} transactionId: %X{transactionId} requestTimeStamp: %X{requestTimestamp} responseTimeStamp: %X{responseTimestamp} duration: %X{duration}] %m%n" />
+
+ <property name="performanceandAuditpattern"
+ value="%date{ISO8601,UTC}|%thread|%.-5level|namespace:${namespace}|%logger{20}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{serviceName}| %msg%n" />
+
+ <property name="auditLoggerPattern"
+ value="%X{BeginTimestamp}|%X{EndTimestamp}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{PartnerName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ElapsedTime}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Unused}|%X{ProcessKey}|%X{CustomField1}|%X{CustomField2}|%X{CustomField3}|%X{CustomField4}| %msg%n" />
+
+ <property name="metricsLoggerPattern"
+ value="%X{BeginTimestamp}|%X{EndTimestamp}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{PartnerName}|%X{TargetEntity}|%X{TargetServiceName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ElapsedTime}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Unused}|%X{ProcessKey}|%X{TargetVirtualEntity}|%X{CustomField1}|%X{CustomField2}|%X{CustomField3}|%X{CustomField4}| %msg%n" />
+
+ <property name="errorLoggerPattern"
+ value="%date{ISO8601,UTC}|%X{RequestId}|%thread|%X{ServiceName}|%X{PartnerName}|%X{TargetEntity}|%X{TargetServiceName}|%.-5level|%X{ErrorCode}|%X{ErrorDescription}| %msg%n" />
+
+ <property name="debugLoggerPattern"
+ value="%date{ISO8601,UTC}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Timer}| %msg%n" />
+
+ <appender name="STDOUT"
+ class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- ============================================================================ -->
+ <!-- EELF Appenders -->
+ <!-- ============================================================================ -->
+
+ <!-- The EELFAppender is used to record events to the general application
+ log -->
+
+ <appender name="EELF"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${generalLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${generalLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELF"
+ class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELF" />
+ </appender>
+
+ <!-- EELF Security Appender. This appender is used to record security events
+ to the security log file. Security events are separate from other loggers
+ in EELF so that security log records can be captured and managed in a secure
+ way separate from the other logs. This appender is set to never discard any
+ events. -->
+ <appender name="EELFSecurity"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${securityLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${securityLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELFSecurity"
+ class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <discardingThreshold>0</discardingThreshold>
+ <appender-ref ref="EELFSecurity" />
+ </appender>
+
+ <!-- EELF Performance Appender. This appender is used to record performance
+ records. -->
+ <appender name="EELFPerformance"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${performanceLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${performanceLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+ <appender name="asyncEELFPerformance"
+ class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFPerformance" />
+ </appender>
+
+ <!-- EELF Server Appender. This appender is used to record Server related
+ logging events. The Server logger and appender are specializations of the
+ EELF application root logger and appender. This can be used to segregate
+ Server events from other components, or it can be eliminated to record these
+ events as part of the application root log. -->
+ <appender name="EELFServer"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${serverLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${serverLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+ <appender name="asyncEELFServer"
+ class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFServer" />
+ </appender>
+
+
+ <!-- EELF Policy Appender. This appender is used to record Policy engine
+ related logging events. The Policy logger and appender are specializations
+ of the EELF application root logger and appender. This can be used to segregate
+ Policy engine events from other components, or it can be eliminated to record
+ these events as part of the application root log. -->
+ <appender name="EELFPolicy"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${policyLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${policyLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+ <appender name="asyncEELFPolicy"
+ class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFPolicy" />
+ </appender>
+
+
+ <!-- EELF Audit Appender. This appender is used to record audit engine related
+ logging events. The audit logger and appender are specializations of the
+ EELF application root logger and appender. This can be used to segregate
+ Policy engine events from other components, or it can be eliminated to record
+ these events as part of the application root log. -->
+
+ <appender name="EELFAudit"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${auditLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${auditLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${auditLoggerPattern}</pattern>
+ </encoder>
+ </appender>
+ <appender name="asyncEELFAudit"
+ class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFAudit" />
+ </appender>
+
+ <appender name="EELFMetrics"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${metricsLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${metricsLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${metricsLoggerPattern}</pattern>
+ </encoder>
+ </appender>
+
+
+ <appender name="asyncEELFMetrics"
+ class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFMetrics" />
+ </appender>
+
+ <appender name="EELFError"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${errorLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${errorLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${errorLoggerPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELFError"
+ class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFError" />
+ </appender>
+
+ <appender name="EELFDebug"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${debugLogDirectory}/${debugLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${debugLogDirectory}/${debugLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${debugLoggerPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELFDebug"
+ class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFDebug" />
+ <includeCallerData>true</includeCallerData>
+ </appender>
+
+
+ <!-- ============================================================================ -->
+ <!-- EELF loggers -->
+ <!-- ============================================================================ -->
+ <logger name="com.att.eelf" level="${EELF_LEVEL:-info}" additivity="false">
+ <appender-ref ref="asyncEELF" />
+ </logger>
+ <logger name="com.att.eelf.security" level="${DECURITY_LEVEL:-info}" additivity="false">
+ <appender-ref ref="asyncEELFSecurity" />
+ </logger>
+ <logger name="com.att.eelf.perf" level="${PERF_LEVEL:-info}" additivity="false">
+ <appender-ref ref="asyncEELFPerformance" />
+ </logger>
+ <logger name="com.att.eelf.server" level="${SERVER_LEVEL:-info}" additivity="false">
+ <appender-ref ref="asyncEELFServer" />
+ </logger>
+ <logger name="com.att.eelf.policy" level="${POLICY_LEVEL:-info}" additivity="false">
+ <appender-ref ref="asyncEELFPolicy" />
+ </logger>
+ <logger name="com.att.eelf.audit" level="${AUDIT_LEVEL:-info}" additivity="false">
+ <appender-ref ref="asyncEELFAudit" />
+ </logger>
+
+ <logger name="com.att.eelf.metrics" level="${METRICS_LEVEL:-info}" additivity="false">
+ <appender-ref ref="asyncEELFMetrics" />
+ </logger>
+
+ <logger name="com.att.eelf.error" level="${ERROR_LEVEL:-error}"
+ additivity="false">
+ <appender-ref ref="asyncEELFError" />
+ </logger>
+
+ <logger name="com.att.eelf.debug" level="${DEBUG_LEVEL:-debug}"
+ additivity="false">
+ <appender-ref ref="asyncEELFDebug" />
+ </logger>
+
+ <root level="${ROOT_LEVEL:-INFO}">
+ <!-- appender-ref ref="STDOUT" / -->
+ <appender-ref ref="asyncEELF" />
+ </root>
+
+</configuration>
diff --git a/cmso-ticketmgt/src/main/resources/logmessages.properties b/cmso-ticketmgt/src/main/resources/logmessages.properties
new file mode 100644
index 0000000..de09032
--- /dev/null
+++ b/cmso-ticketmgt/src/main/resources/logmessages.properties
@@ -0,0 +1,25 @@
+# Generated from org.onap.optf.ticketmgt.common.LogMessages
+FETCH_TICKET FETCH_TICKET|Fetch ticket {0} : {1}: {2} : {3}|No resolution needed|No action is required
+CREATE_TICKET CREATE_TICKET|Create Ticket {0} : {1}: {2} : {3}|No resolution needed|No action is required
+CANCEL_TICKET CANCEL_TICKET|Cancel ticket {0} : {1}: {2} : {3}|No resolution needed|No action is required
+UPDATE_TICKET UPDATE_TICKET|Update ticket {0} : {1}: {2} : {3}|No resolution needed|No action is required
+GET_ACTIVE_TICKETS GET_ACTIVE_TICKETS|Get active tickets {0} : {1}: {2} : {3}|No resolution needed|No action is required
+SEARCH_TICKETS SEARCH_TICKETS|Search tickets {0} : {1}: {2} : {3}|No resolution needed|No action is required
+TICKET_NOT_FOUND TICKET_NOT_FOUND|Ticket not found id={0}|No resolution needed|No action is required
+INVALID_ATTRIBUTE INVALID_ATTRIBUTE|Invalid attribute {0}={1}|No resolution needed|No action is required
+MISSING_REQUIRED_ATTRIBUTE MISSING_REQUIRED_ATTRIBUTE|Missing required attribute '{0}'|No resolution needed|No action is required
+INVALID_REQUEST INVALID_REQUEST|The input data structure is incorrect|No resolution needed|No action is required
+REQUEST_TIMED_OUT REQUEST_TIMED_OUT|Request timed out.|No resolution needed|No action is required
+UNEXPECTED_EXCEPTION UNEXPECTED_EXCEPTION|Unexpected exception encountered during processing. Please contact support : {0}|No resolution needed|No action is required
+UNDEFINED_FILTER_ATTRIBUTE UNDEFINED_FILTER_ATTRIBUTE|Undefined filter attribute {0}|No resolution needed|No action is required
+INVALID_DATE_FILTER INVALID_DATE_FILTER|Invalid date filter provided {0}=(1}|No resolution needed|No action is required
+INCOMING_MESSAGE INCOMING_MESSAGE|Incoming message method={0} path={1}|No resolution needed|No action is required
+INCOMING_MESSAGE_RESPONSE INCOMING_MESSAGE_RESPONSE|Message response method={0} path={1} status={2}|No resolution needed|No action is required
+OUTGOING_MESSAGE OUTGOING_MESSAGE|Outgoing message method={0} path={1}|No resolution needed|No action is required
+OUTGOING_MESSAGE_RETURNED OUTGOING_MESSAGE_RETURNED|Outgoing message returned method={0} path={1} status={2}|No resolution needed|No action is required
+UNEXPECTED_RESPONSE UNEXPECTED_RESPONSE|Unexpected response from {0} HTTP Status={1} : {2}|No resolution needed|No action is required
+INVALID_CHANGE_WINDOW INVALID_CHANGE_WINDOW|Change window end time {1} must be after start time {0}|No resolution needed|No action is required
+EXPECTED_EXCEPTION EXPECTED_EXCEPTION|Expected exception encountered during processing. {0}|No resolution needed|No action is required
+UNABLE_TO_UPDATE_TICKET UNABLE_TO_UPDATE_TICKET|Unable to update change ticket in TM: Schedule ID: {0} : changeid: {1} : Reason: {2}|No resolution needed|No action is required
+UNAUTHORIZED UNAUTHORIZED|Authorization failed.|No resolution needed|No action is required
+UNAUTHENTICATED UNAUTHENTICATED|Authentication failed.|No resolution needed|No action is required
diff --git a/cmso-ticketmgt/src/main/script/TagVersion.groovy b/cmso-ticketmgt/src/main/script/TagVersion.groovy
new file mode 100644
index 0000000..c682f7f
--- /dev/null
+++ b/cmso-ticketmgt/src/main/script/TagVersion.groovy
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CMSO
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+package org.onap.cmso.maven.scripts
+
+println project.properties['cmso.project.version'];
+def versionArray;
+if ( project.properties['cmso.project.version'] != null ) {
+ versionArray = project.properties['cmso.project.version'].split('\\.');
+}
+
+if ( project.properties['cmso.project.version'].endsWith("-SNAPSHOT") ) {
+ project.properties['project.docker.latesttag.version']=versionArray[0] + '.' + versionArray[1] + "-SNAPSHOT-latest";
+ project.properties['project.docker.latesttagtimestamp.version']=versionArray[0] + '.' + versionArray[1] + "-SNAPSHOT-"+project.properties['cmso.build.timestamp'];
+} else {
+ project.properties['project.docker.latesttag.version']=versionArray[0] + '.' + versionArray[1] + "-STAGING-latest";
+ project.properties['project.docker.latesttagtimestamp.version']=versionArray[0] + '.' + versionArray[1] + "-STAGING-"+project.properties['cmso.build.timestamp'];
+}
+
+println 'New Tag for docker:' + project.properties['project.docker.latesttag.version']; \ No newline at end of file