Repository: aliyun/fun
Pull request #166 (2019-03-05 10:44:05)

fix fun local start could not receive logs bug


Patch:
diff --git a/examples/local_http/nodejs8/index.js b/examples/local_http/nodejs8/index.js
index 8617acf..2e82dad 100644
--- a/examples/local_http/nodejs8/index.js
+++ b/examples/local_http/nodejs8/index.js
@@ -1,7 +1,11 @@
 var getRawBody = require('raw-body')
 
-module.exports.handler = function (request, response, context) {
-    
+module.exports.initializer = function(context, callback) {
+    console.log("initializer invoked");
+    callback(null, '');
+}
+
+module.exports.handler = function (request, response, context) {    
     // get request body
     getRawBody(request, function (err, body) {
         var respBody = {
diff --git a/examples/local_http/template.yml b/examples/local_http/template.yml
index 4d7f3c5..a48c793 100644
--- a/examples/local_http/template.yml
+++ b/examples/local_http/template.yml
@@ -12,6 +12,7 @@ Resources:
         CodeUri: nodejs8/
         Description: 'http trigger demo with nodejs8!'
         Runtime: nodejs8
+        Initializer: index.initializer
       Events:
         http-test:
           Type: HTTP
diff --git a/lib/commands/local/start.js b/lib/commands/local/start.js
index b705204..bed8b9e 100644
--- a/lib/commands/local/start.js
+++ b/lib/commands/local/start.js
@@ -17,25 +17,46 @@ const definition = require('../../definition');
 const { getDebugPort, getDebugIde } = require('../../debug');
 
 const serverPort = 8000;
+const SERVER_CLOSE_TIMEOUT = 5000;
 
 function registerSigintForExpress(server) {
   var sockets = {}, nextSocketId = 0;
 
   // close express server 
   // https://stackoverflow.com/questions/14626636/how-do-i-shutdown-a-node-js-https-server-immediately/14636625#14636625
-  server.on('connection', function(socket) {
+  server.on('connection', function (socket) {
     let socketId = nextSocketId;
     sockets[socketId] = socket;
-    socket.on('close', function() {
+    socket.on('close', function () {
       delete sockets[socketId];
     });
   });
 
-  process.on('SIGINT', () => {
-    server.close();
+  process.once('SIGINT', () => {
+    
+    console.log('begin to close server');
+
+    // force close if gracefully closing failed
+    // https://stackoverflow.com/a/36830072/6602338
+    const serverCloseTimeout = setTimeout(() => {
+      console.log('server close timeout, force to close server');
+
+      server.emit('close');
+
+      // if force close failed, exit directly
+      setTimeout(() => {
+        process.exit(-1);
+      }, SERVER_CLOSE_TIMEOUT);
+      
+    }, SERVER_CLOSE_TIMEOUT);
+
+    // gracefully close server
+    server.close(() => {
+      clearTimeout(serverCloseTimeout);
+    });
 
     for (let socketId in sockets) {
-      if (!{}.hasOwnProperty.call(sockets, socketId)) {continue;}
+      if (!{}.hasOwnProperty.call(sockets, socketId)) { continue; }
 
       sockets[socketId].destroy();
     }
@@ -43,7 +64,7 @@ function registerSigintForExpress(server) {
 }
 
 function startExpress(app) {
-    
+
   const server = app.listen(serverPort, function () {
     console.log(`function compute app listening on port ${serverPort}!`);
     console.log();
diff --git a/lib/docker.js b/lib/docker.js
index 1fff0af..ff7c16d 100644
--- a/lib/docker.js
+++ b/lib/docker.js
@@ -61,12 +61,22 @@ function waitingForContainerStopped() {
     const jobs = [];
   
     for (let container of containers) {
-      console.log(`stopping container ${container}`);
-      jobs.push(docker.getContainer(container).stop());
+      try {
+        const c = docker.getContainer(container);
+
+        await c.inspect();
+
+        console.log(`stopping container ${container}`);
+
+        jobs.push(c.stop());
+      } catch(error) {
+        debug('get container instance error, ignore container to stop, error is', error);
+      }
     }
 
     try {
       await Promise.all(jobs);
+      console.log('all containers stopped');
     } catch (error) {
       console.error(error);
       process.exit(-1);
@@ -176,7 +186,7 @@ async function imageExist(imageName) {
   return images.length > 0;
 }
 
-function generateDockerCmd(functionProps, httpMode) {
+function generateDockerCmd(functionProps, httpMode, invokeInitializer = true) {
   const cmd = ['-h', functionProps.Handler];
 
   // always pass event using stdin mode
@@ -188,7 +198,7 @@ function generateDockerCmd(functionProps, httpMode) {
 
   const initializer = functionProps.Initializer;
 
-  if (initializer) {
+  if (initializer && invokeInitializer) {
     cmd.push('-i', initializer);
   }
 
@@ -396,7 +406,9 @@ function resolveDockerUser(nasConfig) {
   return `${uid}:${gid}`;
 }
 
-async function startContainer(opts) {
+// outputStream, errorStream used for http invoke
+// because agent is started when container running and exec could not receive related logs
+async function startContainer(opts, outputStream, errorStream) {
 
   const container = await docker.createContainer(opts);
 
@@ -408,6 +420,27 @@ async function startContainer(opts) {
     console.error(err);
   }
 
+  const logs = outputStream || errorStream;
+
+  if (logs) {
+    if (!outputStream) {
+      outputStream = devnull();
+    }
+
+    if (!errorStream) {
+      errorStream = devnull();
+    }
+
+    // dockerode bugs on windows. attach could not receive output and error, must use logs
+    const logStream = await container.logs({
+      stdout: true,
+      stderr: true,
+      follow: true
+    });
+
+    container.modem.demuxStream(logStream, outputStream, errorStream);
+  }
+
   return {
     stop: async () => {
       await container.stop();
diff --git a/lib/init/vcs.js b/lib/init/vcs.js
index a9c9778..cb6387c 100644
--- a/lib/init/vcs.js
+++ b/lib/init/vcs.js
@@ -33,21 +33,12 @@ function isVCSInstalled(repoType) {
 function makeSurePathExists(p) {
   if (fs.existsSync(p)) {
     return true;
-  }
+  } 
   if (makeSurePathExists(path.dirname(p))) {
     fs.mkdirSync(p);
     return true;
   }
-
-}
-
-function cloneArguments(repoType, repoUrl) {
-  switch (repoType) {
-  case 'git':
-    return ['clone', '--depth=1', repoUrl];
-  default:
-    return ['clone', repoUrl];
-  }
+  
 }
 
 async function clone(repoUrl, cloneToDir = '.', checkout) {
@@ -76,7 +67,7 @@ async function clone(repoUrl, cloneToDir = '.', checkout) {
   debug('repoDir is %s', repoDir);
   await promptForExistingPath(repoDir, `You've downloaded ${repoDir} before. Is it okay to delete and re-download it?`, true);
   console.log('start cloning...');
-  spawnSync(repoType, cloneArguments(repoType, repoUrl), { cmd: repoDir, stdio: 'inherit' });
+  spawnSync(repoType, ['clone', repoUrl], { cmd: repoDir, stdio: 'inherit' });
   console.log('finish cloning.');
 
   if (checkout) {
diff --git a/lib/local/http-invoke.js b/lib/local/http-invoke.js
index eeb56a4..010a433 100644
--- a/lib/local/http-invoke.js
+++ b/lib/local/http-invoke.js
@@ -24,6 +24,7 @@ class HttpInvoke extends Invoke {
 
     this.isAnonymous = authType === 'ANONYMOUS';
     this.endpointPrefix = endpointPrefix;
+    this._invokeInitializer = true;
   }
 
   async _disableRunner(evt, name) {
@@ -41,6 +42,7 @@ class HttpInvoke extends Invoke {
 
       this.runner = null;
       this.containerName = docker.generateRamdomContainerName();
+      this._invokeInitializer = true;
 
       console.log('reloading success, stop old container background...');
 
@@ -70,8 +72,6 @@ class HttpInvoke extends Invoke {
           if (!this.runner) {
             debug('acquire invoke lock success, ready to create runner');
 
-            await docker.pullImageIfNeed(this.imageName);
-
             if (!this.watcher) {
               watch(this.codeMount.Source, { recursive: true, persistent: false }, (evt, name) => {
                 if (this.runner) {
@@ -82,17 +82,7 @@ class HttpInvoke extends Invoke {
               });
             }
 
-            const envs = await docker.generateDockerEnvs(this.functionProps, this.debugPort, null);
-
-            const opts = await dockerOpts.generateLocalStartRunOpts(this.runtime,
-              this.containerName,
-              this.mounts,
-              ['--server'],
-              this.debugPort,
-              envs,
-              this.dockerUser);
-
-            this.runner = await startContainer(opts);
+            await this._generateRunner();            
           } else {
             debug('acquire invoke lock success, but runner already created, skipping...');
           }
@@ -101,6 +91,21 @@ class HttpInvoke extends Invoke {
     }
   }
 
+  async _generateRunner() {
+
+    const envs = await docker.generateDockerEnvs(this.functionProps, this.debugPort, null);
+
+    const opts = await dockerOpts.generateLocalStartRunOpts(this.runtime,
+      this.containerName,
+      this.mounts,
+      ['--server'],
+      this.debugPort,
+      envs,
+      this.dockerUser);
+
+    this.runner = await startContainer(opts, process.stdout, process.stderr);
+  }
+
   async doInvoke(req, res) {
     // only one invoke can be processed
     await lock.acquire('invoke', async () => {
@@ -137,7 +142,7 @@ class HttpInvoke extends Invoke {
         // reuse container
         debug('http doInvoke, acquire invoke lock');
 
-        const cmd = [dockerOpts.resolveMockScript(this.runtime), ...docker.generateDockerCmd(this.functionProps, true)];
+        const cmd = [dockerOpts.resolveMockScript(this.runtime), ...docker.generateDockerCmd(this.functionProps, true, this._invokeInitializer)];
 
         debug(`http doInvoke, cmd is : ${cmd}`);
 
@@ -146,14 +151,35 @@ class HttpInvoke extends Invoke {
           if (!await validateSignature(req, res, req.method)) { return; }
         }
 
-        await this.runner.exec(cmd, {
-          env: envs,
-          event,
-          outputStream,
-          errorStream,
-          verbose: true
-        });
-
+        try {
+          await this.runner.exec(cmd, {
+            env: envs,
+            event,
+            outputStream,
+            errorStream,
+            verbose: true
+          });
+
+          this._invokeInitializer = false;
+        } catch(error) {
+          // errors for runtime error
+          // for example, when using nodejs, use response.send(new Error('haha')) will lead to runtime error
+          // and container will auto exit, exec will receive no message
+          res.status(500);
+          res.setHeader('Content-Type', 'application/json');
+
+          res.send({
+            'errorMessage': `Process exited unexpectedly before completing request`
+          });
+
+          // for next invoke
+          this.runner = null;
+          this.containerName = docker.generateRamdomContainerName();
+          
+          console.error(error);
+          return ;
+        }
+                
         debug('http doInvoke exec end, begin to response');
       }
 
diff --git a/lib/package/ignore.js b/lib/package/ignore.js
index b4bdeae..686fed6 100644
--- a/lib/package/ignore.js
+++ b/lib/package/ignore.js
@@ -5,7 +5,7 @@ const parser = require('git-ignore-parser'),
   fs = require('fs'),
   path = require('path');
 
-const ignoredFile = ['.git', '.svn', '.env'];
+const ignoredFile = ['.git', '.svn', '.env', '.fun/nas'];
 
 module.exports = function (baseDir) {
 
diff --git a/package-lock.json b/package-lock.json
index 5149caf..030c2c8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -136,15 +136,6 @@
       "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
       "dev": true
     },
-    "@sinonjs/commons": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz",
-      "integrity": "sha512-9jHK3YF/8HtJ9wCAbG+j8cD0i0+ATS9A7gXFqS36TblLPNy6rEEc+SB0imo91eCboGaBYGV/MT1/br/J+EE7Tw==",
-      "dev": true,
-      "requires": {
-        "type-detect": "4.0.8"
-      }
-    },
     "@sinonjs/formatio": {
       "version": "2.0.0",
       "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
@@ -154,11 +145,14 @@
         "samsam": "1.3.0"
       }
     },
-    "@sinonjs/text-encoding": {
-      "version": "0.7.1",
-      "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
-      "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
-      "dev": true
+    "@sinonjs/samsam": {
+      "version": "2.1.0",
+      "resolved": "http://registry.npm.taobao.org/@sinonjs/samsam/download/@sinonjs/samsam-2.1.0.tgz",
+      "integrity": "sha1-uLj1uBlgW9Y2Aabt5FkVaIDzjqM=",
+      "dev": true,
+      "requires": {
+        "array-from": "^2.1.1"
+      }
     },
     "JSONStream": {
       "version": "1.3.2",
@@ -301,7 +295,7 @@
     },
     "array-flatten": {
       "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
       "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
     },
     "array-from": {
@@ -617,7 +611,7 @@
     },
     "buffer": {
       "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+      "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
       "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
       "requires": {
         "base64-js": "^1.0.2",
@@ -2361,7 +2355,7 @@
     },
     "int64-buffer": {
       "version": "0.1.9",
-      "resolved": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-0.1.9.tgz",
+      "resolved": "http://registry.npmjs.org/int64-buffer/-/int64-buffer-0.1.9.tgz",
       "integrity": "sha1-ngOdoEOyT3ixlrKD4EZT716ZD2E="
     },
     "into-stream": {
@@ -2716,6 +2710,12 @@
         }
       }
     },
+    "just-extend": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-3.0.0.tgz",
+      "integrity": "sha512-Fu3T6pKBuxjWT/p4DkqGHFRsysc8OauWr4ZRTY9dIx07Y9O0RkoR5jcv28aeD1vuAwhm3nLkDurwLXoALp4DpQ==",
+      "dev": true
+    },
     "kind-of": {
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
@@ -2947,7 +2947,7 @@
     },
     "mkdirp": {
       "version": "0.5.1",
-      "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
       "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
       "requires": {
         "minimist": "0.0.8"
@@ -3062,44 +3062,26 @@
       "integrity": "sha512-kgq1CuvLyUcbcIuTiCA93cQ2IJFSlRwXcN+hLcb2qLJwC2qrePHGZZa7IipyWqaWF6tQjdax2pQnVxdq19Zzwg=="
     },
     "nise": {
-      "version": "1.4.10",
-      "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.10.tgz",
-      "integrity": "sha512-sa0RRbj53dovjc7wombHmVli9ZihXbXCQ2uH3TNm03DyvOSIQbxg+pbqDKrk2oxMK1rtLGVlKxcB9rrc6X5YjA==",
+      "version": "1.4.6",
+      "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.6.tgz",
+      "integrity": "sha512-1GedetLKzmqmgwabuMSqPsT7oumdR77SBpDfNNJhADRIeA3LN/2RVqR4fFqwvzhAqcTef6PPCzQwITE/YQ8S8A==",
       "dev": true,
       "requires": {
-        "@sinonjs/formatio": "^3.1.0",
-        "@sinonjs/text-encoding": "^0.7.1",
-        "just-extend": "^4.0.2",
+        "@sinonjs/formatio": "3.0.0",
+        "just-extend": "^3.0.0",
         "lolex": "^2.3.2",
-        "path-to-regexp": "^1.7.0"
+        "path-to-regexp": "^1.7.0",
+        "text-encoding": "^0.6.4"
       },
       "dependencies": {
         "@sinonjs/formatio": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz",
-          "integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==",
-          "dev": true,
-          "requires": {
-            "@sinonjs/commons": "^1",
-            "@sinonjs/samsam": "^3.1.0"
-          }
-        },
-        "@sinonjs/samsam": {
-          "version": "3.2.0",
-          "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.2.0.tgz",
-          "integrity": "sha512-j5F1rScewLtx6pbTK0UAjA3jJj4RYiSKOix53YWv+Jzy/AZ69qHxUpU8fwVLjyKbEEud9QrLpv6Ggs7WqTimYw==",
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.0.0.tgz",
+          "integrity": "sha512-vdjoYLDptCgvtJs57ULshak3iJe4NW3sJ3g36xVDGff5AE8P30S6A093EIEPjdi2noGhfuNOEkbxt3J3awFW1w==",
           "dev": true,
           "requires": {
-            "@sinonjs/commons": "^1.0.2",
-            "array-from": "^2.1.1",
-            "lodash": "^4.17.11"
+            "@sinonjs/samsam": "2.1.0"
           }
-        },
-        "just-extend": {
-          "version": "4.0.2",
-          "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz",
-          "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==",
-          "dev": true
         }
       }
     },
@@ -4415,9 +4397,9 @@
       }
     },
     "tablestore": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/tablestore/-/tablestore-4.3.0.tgz",
-      "integrity": "sha512-B2XelmC3hOO4qrtxRd5xsv+nbx3L8+xZ2ajPhqHRNCL1BUANFqQOPfQfH4Uhd3obEh4F3sThb2i6s4pG5gwj4g==",
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/tablestore/-/tablestore-4.2.0.tgz",
+      "integrity": "sha512-usAhh7VA+ilP5qMNLFuj8V6NVbbCxeofHSjVkkFBJ1E4AZth//jC4k8O8n5ijKdee3ImboMrmJPs3GmjvBbObA==",
       "requires": {
         "buffer": "4.9.1",
         "int64-buffer": "0.1.9",
@@ -4452,6 +4434,12 @@
       "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=",
       "dev": true
     },
+    "text-encoding": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
+      "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
+      "dev": true
+    },
     "text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
diff --git a/package.json b/package.json
index 2e166a4..939a81b 100644
--- a/package.json
+++ b/package.json
@@ -69,7 +69,7 @@
     "require-from-string": "^2.0.2",
     "rimraf": "^2.6.2",
     "semver": "^5.5.0",
-    "tablestore": "^4.3.0",
+    "tablestore": "^4.2.0",
     "urlencode": "^1.1.0"
   },
   "files": [
diff --git a/templates/helloworld-java8/{{ projectName }}/.funignore b/templates/helloworld-java8/{{ projectName }}/.funignore
deleted file mode 100644
index f456ba2..0000000
--- a/templates/helloworld-java8/{{ projectName }}/.funignore
+++ /dev/null
@@ -1,8 +0,0 @@
-src/
-.idea/
-target/
-.DS_Store
-*.iml
-pom.xml
-template.yml
-fun.yml
\ No newline at end of file
diff --git a/templates/helloworld-java8/{{ projectName }}/.gitignore b/templates/helloworld-java8/{{ projectName }}/.gitignore
deleted file mode 100644
index f1debb2..0000000
--- a/templates/helloworld-java8/{{ projectName }}/.gitignore
+++ /dev/null
@@ -1,9 +0,0 @@
-.DS_Store
-.idea/
-*.iml
-lib/
-target/
-.settings/
-.project
-.classpath
-bin/
\ No newline at end of file
diff --git a/templates/helloworld-java8/{{ projectName }}/README.md b/templates/helloworld-java8/{{ projectName }}/README.md
deleted file mode 100644
index 969102c..0000000
--- a/templates/helloworld-java8/{{ projectName }}/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# 函数计算 Java 项目模板
-
-## 本地运行
-
-```bash
-mvn package && fun local invoke {{ projectName }}
-```
-
-## 部署
-
-```bash
-mvn package && fun deploy
-```
-
-## 执行
-
-```bash
-fcli function invoke -s {{ projectName }} -f {{ projectName }}
-```
\ No newline at end of file
diff --git a/templates/helloworld-java8/{{ projectName }}/pom.xml b/templates/helloworld-java8/{{ projectName }}/pom.xml
index d88af98..532a4c5 100644
--- a/templates/helloworld-java8/{{ projectName }}/pom.xml
+++ b/templates/helloworld-java8/{{ projectName }}/pom.xml
@@ -6,7 +6,7 @@
   <packaging>jar</packaging>
   <version>1.0-SNAPSHOT</version>
   <name>{{ projectName }}</name>
-
+  <url>http://maven.apache.org</url>
   <dependencies>
     <dependency>
       <groupId>junit</groupId>
@@ -20,47 +20,34 @@
       <version>1.2.0</version>
     </dependency>
   </dependencies>
-
   <build>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-dependency-plugin</artifactId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+            </manifest>
+          </archive>
+          <descriptorRefs>
+            <descriptorRef>jar-with-dependencies</descriptorRef>
+          </descriptorRefs>
+        </configuration>
         <executions>
           <execution>
-            <id>copy-dependencies</id>
-            <phase>prepare-package</phase>
+            <id>make-assembly</id>
+            <phase>package</phase>
             <goals>
-              <goal>copy-dependencies</goal>
+              <goal>single</goal>
             </goals>
-            <configuration>
-              <outputDirectory>${basedir}/lib</outputDirectory>
-              <includeScope>runtime</includeScope>
-            </configuration>
           </execution>
         </executions>
       </plugin>
-      <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-jar-plugin</artifactId>
-          <configuration>
-            <outputDirectory>${basedir}/lib</outputDirectory>
-          </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <version>3.6.1</version>
-        <configuration>
-          <source>1.8</source>
-          <target>1.8</target>
-        </configuration>
-      </plugin>
     </plugins>
   </build>
-
   <properties>
-    <maven.compiler.target>1.8</maven.compiler.target>
     <maven.compiler.source>1.8</maven.compiler.source>
+    <maven.compiler.target>1.8</maven.compiler.target>
   </properties>
 </project>
diff --git a/templates/helloworld-java8/{{ projectName }}/src/main/java/example/App.java b/templates/helloworld-java8/{{ projectName }}/src/main/java/example/App.java
index 5df5bca..0dfb415 100644
--- a/templates/helloworld-java8/{{ projectName }}/src/main/java/example/App.java
+++ b/templates/helloworld-java8/{{ projectName }}/src/main/java/example/App.java
@@ -6,21 +6,21 @@ import java.io.OutputStream;
 
 import com.aliyun.fc.runtime.Context;
 import com.aliyun.fc.runtime.StreamRequestHandler;
-import com.aliyun.fc.runtime.FunctionInitializer;
 
 /**
  * Hello world!
  *
  */
-public class App implements StreamRequestHandler, FunctionInitializer {
-
-    public void initialize(Context context) throws IOException {
-        //TODO
+public class App implements StreamRequestHandler
+{
+    public static void main( String[] args )
+    {
+        System.out.println( "Hello World!" );
     }
 
     @Override
     public void handleRequest(
             InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
-        outputStream.write(new String("hello world\n").getBytes());
+        outputStream.write(new String("hello world").getBytes());
     }
 }
diff --git a/templates/helloworld-java8/{{ projectName }}/template.yml b/templates/helloworld-java8/{{ projectName }}/template.yml
index 38dceb5..fd6eafc 100644
--- a/templates/helloworld-java8/{{ projectName }}/template.yml
+++ b/templates/helloworld-java8/{{ projectName }}/template.yml
@@ -9,6 +9,5 @@ Resources:
       Type: 'Aliyun::Serverless::Function'
       Properties:
         Handler: example.App::handleRequest
-        Initializer: example.App::initialize
         Runtime: java8
-        CodeUri: './'
+        CodeUri: './target/{{ projectName }}-1.0-SNAPSHOT-jar-with-dependencies.jar'


Test patch:
diff --git a/test/docker.test.js b/test/docker.test.js
index bcbab63..2866993 100644
--- a/test/docker.test.js
+++ b/test/docker.test.js
@@ -63,6 +63,19 @@ describe('test generateDockerCmd', () => {
       '3'
     ]);
   });
+
+  it('test generate docker http cmd without initializer', () => {
+    const cmd = docker.generateDockerCmd(functionProps, true, false);
+
+    expect(cmd).to.eql([
+      '-h',
+      'index.handler',
+      '--stdin',
+      '--http',
+      '--initializationTimeout',
+      '3'
+    ]);
+  });
 });
 
 
@@ -255,7 +268,10 @@ describe('test docker run', async () => {
 
     sandbox.stub(DockerCli.prototype, 'pull').resolves({});
     sandbox.stub(DockerCli.prototype, 'run').resolves({});
-    sandbox.stub(DockerCli.prototype, 'getContainer').returns({ 'stop': sandbox.stub() });
+    sandbox.stub(DockerCli.prototype, 'getContainer').returns({ 
+      'stop': sandbox.stub(),
+      'inspect': sandbox.stub().resolves({})
+    });
 
     streamMock = {
       'write': sandbox.stub(),
@@ -384,6 +400,7 @@ describe('test docker run', async () => {
     await sleep(10);
 
     assert.calledWith(DockerCli.prototype.getContainer, 'containerId');
+    assert.calledOnce(DockerCli.prototype.getContainer().inspect);
     assert.calledOnce(DockerCli.prototype.getContainer().stop);
   });
 });
diff --git a/test/init/vcs.test.js b/test/init/vcs.test.js
index 6dd24bb..d53ef2d 100644
--- a/test/init/vcs.test.js
+++ b/test/init/vcs.test.js
@@ -71,7 +71,7 @@ describe('vcs', () => {
     commandExists.sync.returns(true);
     const repoUrl = 'https://github.com/foo/bar.git';
     const repoDir = await vcsStub.clone(repoUrl, '.');
-    sandbox.assert.calledWith(child_process.spawnSync, 'git', ['clone', '--depth=1', repoUrl], {cmd: repoDir, stdio: 'inherit'});
+    sandbox.assert.calledWith(child_process.spawnSync, 'git', ['clone', repoUrl], {cmd: repoDir, stdio: 'inherit'});
     expect(repoDir).to.be('.bar');
 
   });
diff --git a/test/local/http-invoke.test.js b/test/local/http-invoke.test.js
index 234869f..66007dc 100644
--- a/test/local/http-invoke.test.js
+++ b/test/local/http-invoke.test.js
@@ -101,6 +101,7 @@ describe('test http response', async () => {
   let app;
   let server;
   let restoreProcess;
+  let httpInvoke;
   
   beforeEach(async () => {
 
@@ -125,7 +126,7 @@ def handler(environ, start_response):
 `);
   });
     
-  afterEach(async function () {
+  afterEach(async () => {
     rimraf.sync(projectDir);
     restoreProcess();
 
@@ -142,7 +143,7 @@ def handler(environ, start_response):
     const endpointPrefix = `/2016-08-15/proxy/${serviceName}/${functionName}`;
     const endpoint = `${endpointPrefix}*`;
 
-    const httpInvoke = new HttpInvoke(serviceName, httptriggerServiceRes,
+    httpInvoke = new HttpInvoke(serviceName, httptriggerServiceRes,
       functionName, httpTriggerFunctionRes, null, null, ymlPath, 'ANONYMOUS', endpointPrefix);
 
     app.get(endpoint, async (req, res) => {
@@ -157,6 +158,8 @@ def handler(environ, start_response):
     const body = await httpx.read(resp, 'utf8');
 
     expect(body).to.contain('Hello world!');
+
+    await httpInvoke.runner.stop();
   });
 
   it('test http local invoke with authType function with invalid signature', async () => {
@@ -170,7 +173,7 @@ def handler(environ, start_response):
     const endpointPrefix = `/2016-08-15/proxy/${serviceName}/${functionName}`;
     const endpoint = `${endpointPrefix}*`;
 
-    const httpInvoke = new HttpInvoke(serviceName, httptriggerServiceRes,
+    httpInvoke = new HttpInvoke(serviceName, httptriggerServiceRes,
       functionName, httpTriggerFunctionRes, null, null, ymlPath, 'FUNCTION', endpointPrefix);
 
     app.get(endpoint, async (req, res) => {
@@ -185,6 +188,8 @@ def handler(environ, start_response):
     const body = await httpx.read(resp, 'utf8');
 
     expect(body).to.contain('Signature doesn\'t match, request signature is');
+    
+    await httpInvoke.runner.stop();
   });
 
   it('test http local invoke with authType function with valid signature', async () => {
@@ -222,5 +227,7 @@ def handler(environ, start_response):
     const body = await httpx.read(resp, 'utf8');
 
     expect(body).to.contain('Hello world!');
+    
+    await httpInvoke.runner.stop();
   });
 });