[ofbiz-framework] branch trunk updated (2da6de1 -> d0f522a)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

[ofbiz-framework] branch trunk updated (2da6de1 -> d0f522a)

mthl
This is an automated email from the ASF dual-hosted git repository.

mthl pushed a change to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git.


    from 2da6de1  Fixed: https://github.com/apache/ofbiz-framework/pull/3
     new 815fb52  Improved: Remove some explicit transitive dependencies
     new 81f82b1  Improved: Upgrade some dependencies
     new 22f1da7  Improved: Remove unnecessary ‘ofbizDebug’ task type (OFBIZ-11302)
     new c764a90  Improved: Lint ‘SEOContextFilter#doFilter’ (OFBIZ-11278)
     new d0f522a  Fixed: Remove obsolete references to WorkflowActivity (OFBIZ-11303)

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.adoc                                        | 13 ++---
 .../order/groovyScripts/order/OrderView.groovy     | 10 ----
 applications/order/template/order/Transitions.ftl  | 41 ----------------
 .../ofbiz/product/category/SeoContextFilter.java   | 55 +++++++++++++---------
 build.gradle                                       | 22 ++-------
 5 files changed, 41 insertions(+), 100 deletions(-)

Reply | Threaded
Open this post in threaded view
|

[ofbiz-framework] 01/05: Improved: Remove some explicit transitive dependencies

mthl
This is an automated email from the ASF dual-hosted git repository.

mthl pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit 815fb528352b4156f0a5c7b4372ae45ac6f43fb4
Author: Mathieu Lirzin <[hidden email]>
AuthorDate: Sat Nov 30 01:50:57 2019 +0100

    Improved: Remove some explicit transitive dependencies
---
 build.gradle | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/build.gradle b/build.gradle
index 91faf7d..ae9e523 100644
--- a/build.gradle
+++ b/build.gradle
@@ -25,7 +25,6 @@ import org.asciidoctor.gradle.AsciidoctorTask
  * ======================================================== */
 plugins {
     id 'application'
-    id 'java'
     id 'groovy'
     id 'eclipse'
     id 'checkstyle'
@@ -165,7 +164,6 @@ dependencies {
     implementation 'com.sun.mail:javax.mail:1.6.2'
     implementation 'com.sun.syndication:com.springsource.com.sun.syndication:0.9.0'
     implementation 'com.thoughtworks.xstream:xstream:1.4.11.1'
-    implementation 'commons-cli:commons-cli:1.4'
     implementation 'commons-fileupload:commons-fileupload:1.4'
     implementation 'commons-net:commons-net:3.6'
     implementation 'commons-validator:commons-validator:1.6'
@@ -173,7 +171,6 @@ dependencies {
     implementation 'net.fortuna.ical4j:ical4j:1.0-rc3-atlassian-11'
     implementation 'org.apache.ant:ant-junit:1.10.7'
     implementation 'org.apache.commons:commons-collections4:4.4'
-    implementation 'org.apache.commons:commons-csv:1.7'
     implementation 'org.apache.commons:commons-dbcp2:2.7.0'
     implementation 'org.apache.commons:commons-text:1.8'
     implementation 'org.apache.geronimo.components:geronimo-transaction:3.1.4'
@@ -183,7 +180,6 @@ dependencies {
     implementation 'org.apache.poi:poi:4.1.0'
     implementation 'org.apache.shiro:shiro-core:1.4.1'
     implementation 'org.apache.sshd:sshd-core:1.7.0'
-    implementation 'org.apache.tika:tika-core:1.22'
     implementation 'org.apache.tika:tika-parsers:1.22'
     implementation 'org.apache.tomcat:tomcat-catalina-ha:9.0.29' // Remember to change the version number in javadoc.options block
     implementation 'org.apache.tomcat:tomcat-jasper:9.0.29'
@@ -198,12 +194,9 @@ dependencies {
     implementation 'org.zapodot:jackson-databind-java-optional:2.6.1'
     implementation 'oro:oro:2.0.8'
     implementation 'wsdl4j:wsdl4j:1.6.3'
-    implementation 'org.jsoup:jsoup:1.12.1'
     implementation 'com.auth0:java-jwt:3.8.3'
-    testImplementation 'org.hamcrest:hamcrest:2.1'
     testImplementation 'org.hamcrest:hamcrest-library:2.1' // Enable junit4 to not depend on hamcrest-1.3
     testImplementation 'org.mockito:mockito-core:3.1.0'
-    testImplementation 'com.pholser:junit-quickcheck-core:0.9'
     testImplementation 'com.pholser:junit-quickcheck-generators:0.9'
     runtimeOnly 'javax.xml.soap:javax.xml.soap-api:1.4.0'
     runtimeOnly 'de.odysseus.juel:juel-spi:2.2.7'

Reply | Threaded
Open this post in threaded view
|

[ofbiz-framework] 02/05: Improved: Upgrade some dependencies

mthl
In reply to this post by mthl
This is an automated email from the ASF dual-hosted git repository.

mthl pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit 81f82b12034b7e02827b8ebedf5e5fa544cd0b05
Author: Mathieu Lirzin <[hidden email]>
AuthorDate: Sat Nov 30 01:59:19 2019 +0100

    Improved: Upgrade some dependencies
---
 build.gradle | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/build.gradle b/build.gradle
index ae9e523..98c7d93 100644
--- a/build.gradle
+++ b/build.gradle
@@ -33,7 +33,7 @@ plugins {
     id 'org.asciidoctor.convert' version '2.0.0'
     id 'org.owasp.dependencycheck' version '5.2.2' apply false
     id 'se.patrikerdes.use-latest-versions' version '0.2.12' apply false
-    id 'com.github.ben-manes.versions' version '0.25.0' apply false
+    id 'com.github.ben-manes.versions' version '0.27.0' apply false
     id "com.github.ManifestClasspath" version "0.1.0-RELEASE"
 }
 
@@ -195,8 +195,8 @@ dependencies {
     implementation 'oro:oro:2.0.8'
     implementation 'wsdl4j:wsdl4j:1.6.3'
     implementation 'com.auth0:java-jwt:3.8.3'
-    testImplementation 'org.hamcrest:hamcrest-library:2.1' // Enable junit4 to not depend on hamcrest-1.3
-    testImplementation 'org.mockito:mockito-core:3.1.0'
+    testImplementation 'org.hamcrest:hamcrest-library:2.2' // Enable junit4 to not depend on hamcrest-1.3
+    testImplementation 'org.mockito:mockito-core:3.2.0'
     testImplementation 'com.pholser:junit-quickcheck-generators:0.9'
     runtimeOnly 'javax.xml.soap:javax.xml.soap-api:1.4.0'
     runtimeOnly 'de.odysseus.juel:juel-spi:2.2.7'

Reply | Threaded
Open this post in threaded view
|

[ofbiz-framework] 03/05: Improved: Remove unnecessary ‘ofbizDebug’ task type (OFBIZ-11302)

mthl
In reply to this post by mthl
This is an automated email from the ASF dual-hosted git repository.

mthl pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit 22f1da78590d7907a64f7b543670c56ddd4c1769
Author: Mathieu Lirzin <[hidden email]>
AuthorDate: Sat Nov 30 02:04:55 2019 +0100

    Improved: Remove unnecessary ‘ofbizDebug’ task type (OFBIZ-11302)
   
    the standard ‘--debug-jvm’ Gradle option already provides the same
    functionality.
---
 README.adoc  | 13 ++++---------
 build.gradle |  7 -------
 2 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/README.adoc b/README.adoc
index 7019362..b4dd654 100644
--- a/README.adoc
+++ b/README.adoc
@@ -149,7 +149,6 @@ There are two types of tasks designed for OFBiz in Gradle:
 * *OFBiz server tasks*: To execute OFBiz startup commands. These tasks start
 with one of the following words:
 * *ofbiz* : standard server commands
-* *ofbizDebug* : server commands running in remote debug mode
 * *ofbizBackground* ; server commands running in a background forked process
 
 Tips:
@@ -175,7 +174,7 @@ tasks. Example: `gradlew loadAdminUserLogin -PuserLoginId=myadmin` =
 
 `gradlew "ofbiz --help"`
 
-`gradlew "ofbizDebug --test"`
+`gradlew "ofbiz --test" --debug-jvm`
 
 `gradlew "ofbizBackground --start --portoffset 10000"`
 
@@ -273,11 +272,7 @@ inconsistent state / data
 Starts OFBiz in remote debug mode and waits for debugger or IDEs to connect on
 port *5005*
 
-`gradlew "ofbizDebug --start"`
-
-OR
-
-`gradlew ofbizDebug`
+`gradlew ofbiz --debug-jvm`
 
 [[start-ofbiz-on-a-different-port]]
 ==== Start OFBiz on a different port
@@ -514,7 +509,7 @@ run a test case, in this example the component is "entity" and the case name is
 
 listens on port *5005*
 
-`gradlew "ofbizDebug --test component=entity --test loglevel=verbose"`
+`gradlew "ofbiz --test component=entity --test loglevel=verbose" --debug-jvm`
 
 [[execute-an-integration-test-suite]]
 ==== Execute an integration test suite
@@ -526,7 +521,7 @@ listens on port *5005*
 
 listens on port *5005*
 
-`gradlew "ofbizDebug --test component=entity --test suitename=entitytests"`
+`gradlew "ofbiz --test component=entity --test suitename=entitytests" --debug-jvm`
 
 
 
diff --git a/build.gradle b/build.gradle
index 98c7d93..b8465bc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -963,13 +963,6 @@ tasks.addRule('Pattern: ofbiz <Commands>: Execute OFBiz startup commands') { Str
     }
 }
 
-tasks.addRule('Pattern: ofbizDebug <Commands>: Execute OFBiz startup commands in remote debug mode') { String taskName ->
-    if (taskName ==~ /^ofbizDebug\s.*/ || taskName == 'ofbizDebug') {
-        def arguments = (taskName - 'ofbizDebug').tokenize(' ')
-        createOfbizCommandTask(taskName, arguments).with { debug = true }
-    }
-}
-
 tasks.addRule('Pattern: ofbizBackground <Commands>: Execute OFBiz startup commands in background and output to console.log') { String taskName ->
     if (taskName ==~ /^ofbizBackground\s.*/ || taskName == 'ofbizBackground') {
         createOfbizBackgroundCommandTask(taskName)

Reply | Threaded
Open this post in threaded view
|

[ofbiz-framework] 04/05: Improved: Lint ‘SEOContextFilter#doFilter’ (OFBIZ-11278)

mthl
In reply to this post by mthl
This is an automated email from the ASF dual-hosted git repository.

mthl pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit c764a90b181df054c9623958bcaf1fd02e2e8b85
Author: Mathieu Lirzin <[hidden email]>
AuthorDate: Sun Dec 1 16:54:15 2019 +0100

    Improved: Lint ‘SEOContextFilter#doFilter’ (OFBIZ-11278)
---
 .../ofbiz/product/category/SeoContextFilter.java   | 55 +++++++++++++---------
 build.gradle                                       |  2 +-
 2 files changed, 34 insertions(+), 23 deletions(-)

diff --git a/applications/product/src/main/java/org/apache/ofbiz/product/category/SeoContextFilter.java b/applications/product/src/main/java/org/apache/ofbiz/product/category/SeoContextFilter.java
index de9030f..9bc6868 100644
--- a/applications/product/src/main/java/org/apache/ofbiz/product/category/SeoContextFilter.java
+++ b/applications/product/src/main/java/org/apache/ofbiz/product/category/SeoContextFilter.java
@@ -58,11 +58,11 @@ import org.apache.oro.text.regex.Perl5Matcher;
 /**
  * SeoContextFilter - Restricts access to raw files and configures servlet objects.
  */
-public class SeoContextFilter implements Filter {
+public final class SeoContextFilter implements Filter {
 
-    public static final String module = SeoContextFilter.class.getName();
+    private static final String MODULE = SeoContextFilter.class.getName();
 
-    protected Set<String> webServlets = new HashSet<>();
+    private Set<String> webServlets = new HashSet<>();
     private FilterConfig config;
     private String allowedPaths = "";
     private String redirectPath = "";
@@ -79,7 +79,8 @@ public class SeoContextFilter implements Filter {
             allowedPathList = StringUtil.split(allowedPaths, ":");
         }
 
-        Map<String, ? extends ServletRegistration> servletRegistrations = config.getServletContext().getServletRegistrations();
+        Map<String, ? extends ServletRegistration> servletRegistrations = config.getServletContext()
+                        .getServletRegistrations();
         for (Entry<String, ? extends ServletRegistration> entry : servletRegistrations.entrySet()) {
             Collection<String> servlets = entry.getValue().getMappings();
             for (String servlet : servlets) {
@@ -94,24 +95,25 @@ public class SeoContextFilter implements Filter {
     }
 
     @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+                throws IOException, ServletException {
         HttpServletRequest httpRequest = (HttpServletRequest) request;
         HttpServletResponse httpResponse = (HttpServletResponse) response;
 
         String uri = httpRequest.getRequestURI();
 
-        Map<String, String[]> parameterMap =request.getParameterMap();
+        Map<String, String[]> parameterMap = request.getParameterMap();
         if (!parameterMap.isEmpty()) {
-            List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
-            request.getParameterMap().forEach((name, values) -> {
-                for(String value : values) {
+            List<BasicNameValuePair> params = new ArrayList<>();
+            parameterMap.forEach((name, values) -> {
+                for (String value : values) {
                     params.add(new BasicNameValuePair(name, value));
                 }
             });
             String queryString = URLEncodedUtils.format(params, Charset.forName("UTF-8"));
             uri = uri + "?" + queryString;
         }
-        
+
         boolean forwarded = forwardUri(httpResponse, uri);
         if (forwarded) {
             return;
@@ -124,7 +126,7 @@ public class SeoContextFilter implements Filter {
             controllerConfig = ConfigXMLReader.getControllerConfig(controllerConfigURL);
             requestMaps = controllerConfig.getRequestMapMap();
         } catch (WebAppConfigurationException e) {
-            Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module);
+            Debug.logError(e, "Exception thrown while parsing controller.xml file: ", MODULE);
             throw new ServletException(e);
         }
         Set<String> uris = requestMaps.keySet();
@@ -134,7 +136,9 @@ public class SeoContextFilter implements Filter {
         String contextUri = null;
         if (httpRequest.getAttribute(ControlFilter.FORWARDED_FROM_SERVLET) == null) {
             requestPath = httpRequest.getServletPath();
-            if (requestPath == null) requestPath = "";
+            if (requestPath == null) {
+                requestPath = "";
+            }
             if (requestPath.lastIndexOf('/') > 0) {
                 if (requestPath.indexOf('/') == 0) {
                     requestPath = '/' + requestPath.substring(1, requestPath.indexOf('/', 1));
@@ -144,7 +148,9 @@ public class SeoContextFilter implements Filter {
             }
 
             String requestInfo = httpRequest.getServletPath();
-            if (requestInfo == null) requestInfo = "";
+            if (requestInfo == null) {
+                requestInfo = "";
+            }
             if (requestInfo.lastIndexOf('/') >= 0) {
                 requestInfo = requestInfo.substring(0, requestInfo.lastIndexOf('/')) + "/*";
             }
@@ -166,13 +172,15 @@ public class SeoContextFilter implements Filter {
             if (pathItemList != null) {
                 viewName = pathItemList.get(0);
             }
-            
+
             String requestUri = UtilHttp.getRequestUriFromTarget(httpRequest.getRequestURI());
 
             // check to make sure the requested url is allowed
-            if (!allowedPathList.contains(requestPath) && !allowedPathList.contains(requestInfo) && !allowedPathList.contains(httpRequest.getServletPath())
+            if (!allowedPathList.contains(requestPath) && !allowedPathList.contains(requestInfo)
+                        && !allowedPathList.contains(httpRequest.getServletPath())
                     && !allowedPathList.contains(requestUri) && !allowedPathList.contains("/" + viewName)
-                    && (UtilValidate.isEmpty(requestPath) && UtilValidate.isEmpty(httpRequest.getServletPath()) && !uris.contains(viewName))) {
+                    && (UtilValidate.isEmpty(requestPath) && UtilValidate.isEmpty(httpRequest.getServletPath())
+                                && !uris.contains(viewName))) {
                 String filterMessage = "[Filtered request]: " + contextUri;
 
                 if (redirectPath == null) {
@@ -186,7 +194,8 @@ public class SeoContextFilter implements Filter {
                             try {
                                 error = Integer.parseInt(errorCode);
                             } catch (NumberFormatException nfe) {
-                                Debug.logWarning(nfe, "Error code specified would not parse to Integer : " + errorCode, module);
+                                Debug.logWarning(nfe,
+                                        "Error code specified would not parse to Integer : " + errorCode, MODULE);
                             }
                         }
                         filterMessage = filterMessage + " (" + error + ")";
@@ -209,9 +218,10 @@ public class SeoContextFilter implements Filter {
                         httpResponse.setHeader("Location", redirectPath);
                     }
                 }
-                Debug.logWarning(filterMessage, module);
+                Debug.logWarning(filterMessage, MODULE);
                 return;
-            } else if ((allowedPathList.contains(requestPath) || allowedPathList.contains(requestInfo) || allowedPathList.contains(httpRequest.getServletPath())
+            } else if ((allowedPathList.contains(requestPath) || allowedPathList.contains(requestInfo)
+                        || allowedPathList.contains(httpRequest.getServletPath())
                     || allowedPathList.contains(requestUri) || allowedPathList.contains("/" + viewName))
                     && !webServlets.contains(httpRequest.getServletPath())) {
                 request.setAttribute(SeoControlServlet.REQUEST_IN_ALLOW_LIST, Boolean.TRUE);
@@ -230,7 +240,7 @@ public class SeoContextFilter implements Filter {
 
     /**
      * Forward a uri according to forward pattern regular expressions. Note: this is developed for Filter usage.
-     *
+     *
      * @param uri String to reverse transform
      * @return String
      */
@@ -239,7 +249,8 @@ public class SeoContextFilter implements Filter {
         boolean foundMatch = false;
         Integer responseCodeInt = null;
 
-        if (SeoConfigUtil.checkUseUrlRegexp() && SeoConfigUtil.getSeoPatterns() != null && SeoConfigUtil.getForwardReplacements() != null) {
+        if (SeoConfigUtil.checkUseUrlRegexp() && SeoConfigUtil.getSeoPatterns() != null
+                        && SeoConfigUtil.getForwardReplacements() != null) {
             Iterator<String> keys = SeoConfigUtil.getSeoPatterns().keySet().iterator();
             while (keys.hasNext()) {
                 String key = keys.next();
@@ -265,7 +276,7 @@ public class SeoContextFilter implements Filter {
             }
             response.setHeader("Location", uri);
         } else {
-            Debug.logInfo("Can NOT forward this url: " + uri, module);
+            Debug.logInfo("Can NOT forward this url: " + uri, MODULE);
         }
         return foundMatch;
     }
diff --git a/build.gradle b/build.gradle
index b8465bc..9d1981c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -285,7 +285,7 @@ checkstyle {
     // the sum of errors that were present before introducing the
     // ‘checkstyle’ tool present in the framework and in the official
     // plugins.
-    tasks.checkstyleMain.maxErrors = 37776
+    tasks.checkstyleMain.maxErrors = 37769
     // Currently there are a lot of errors so we need to temporarily
     // hide them to avoid polluting the terminal output.
     showViolations = false

Reply | Threaded
Open this post in threaded view
|

[ofbiz-framework] 05/05: Fixed: Remove obsolete references to WorkflowActivity (OFBIZ-11303)

mthl
In reply to this post by mthl
This is an automated email from the ASF dual-hosted git repository.

mthl pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit d0f522aeed689d5642d2f497a71692f54afd0e22
Author: Mathieu Lirzin <[hidden email]>
AuthorDate: Sun Dec 1 19:16:58 2019 +0100

    Fixed: Remove obsolete references to WorkflowActivity (OFBIZ-11303)
   
    The workflow component is in attic since 7 years.
---
 .../order/groovyScripts/order/OrderView.groovy     | 10 ------
 applications/order/template/order/Transitions.ftl  | 41 ----------------------
 2 files changed, 51 deletions(-)

diff --git a/applications/order/groovyScripts/order/OrderView.groovy b/applications/order/groovyScripts/order/OrderView.groovy
index 756a14f..2be8208 100644
--- a/applications/order/groovyScripts/order/OrderView.groovy
+++ b/applications/order/groovyScripts/order/OrderView.groovy
@@ -498,16 +498,6 @@ if (workEffortId && assignPartyId && assignRoleTypeId && fromDate) {
             if ("WF_RUNNING".equals(workEffortStatus) || "WF_SUSPENDED".equals(workEffortStatus))
                 context.inProcess = true
         }
-
-        if (workEffort) {
-            if ("true".equals(delegate) || "WF_RUNNING".equals(workEffortStatus)) {
-                activity = from("WorkflowActivity").where("packageId", workEffort.workflowPackageId, "packageVersion", workEffort.workflowPackageVersion, "processId", workEffort.workflowProcessId, "processVersion", workEffort.workflowProcessVersion, "activityId", workEffort.workflowActivityId).queryOne()
-                if (activity) {
-                    transitions = activity.getRelated("FromWorkflowTransition", null, ["-transitionId"], false)
-                    context.wfTransitions = transitions
-                }
-            }
-        }
     }
 }
 
diff --git a/applications/order/template/order/Transitions.ftl b/applications/order/template/order/Transitions.ftl
index f5e8b72..23317fc 100644
--- a/applications/order/template/order/Transitions.ftl
+++ b/applications/order/template/order/Transitions.ftl
@@ -64,44 +64,3 @@ under the License.
 </div>
 </#if>
 <br />
-<#if wfTransitions?? && wfTransitions?has_content>
-<div class="screenlet">
-  <div class="screenlet-title-bar">
-    <ul>
-      <li class="h3">${uiLabelMap.OrderProcessingTransitions}</li>
-    </ul>
-    <br class="clear"/>
-  </div>
-  <div class="screenlet-body">
-    <table class="basic-table" cellspacing='0'>
-      <tr>
-        <td>
-          <form action="<@ofbizUrl>completeassignment</@ofbizUrl>" method="post" name="transitionForm">
-            <input type="hidden" name="workEffortId" value="${workEffortId}" />
-            <input type="hidden" name="partyId" value="${assignPartyId}" />
-            <input type="hidden" name="roleTypeId" value="${assignRoleTypeId}" />
-            <input type="hidden" name="fromDate" value="${fromDate}" />
-            <table class="basic-table" cellspacing='0'>
-              <tr>
-                <td>
-                  <select name="approvalCode">
-                    <#list wfTransitions as trans>
-                      <#if trans.extendedAttributes?has_content>
-                        <#assign attrs = Static["org.apache.ofbiz.base.util.StringUtil"].strToMap(trans.extendedAttributes)>
-                        <#if attrs.approvalCode??>
-                          <option value="${attrs.approvalCode}">${trans.transitionName}</option>
-                        </#if>
-                      </#if>
-                    </#list>
-                  </select>
-                </td>
-                <td valign="center">
-                  <a href="javascript:document.transitionForm.submit()" class="buttontext">${uiLabelMap.CommonContinue}</a>
-                </td>
-              </tr>
-            </table>
-          </form>
-        </td>
-      </tr>
-    </table>
-</#if>
\ No newline at end of file