UNPKG

jmms

Version:

Jmms cli tools, Jmms is a java meta-micro-service framework

361 lines (306 loc) 11.1 kB
const _ = require('lodash'); const chalk = require('chalk'); const metas = require('./metas'); const log = require('./log'); const utils = require('./utils'); const fs = require('fs'); const path = require('path'); const proc = require('child_process'); const pkg = require('../package.json'); const app = require('./app'); const table = require('text-table'); const cheerio = require('cheerio'); const iconv = require('iconv-lite'); const os = require('os'); const setupDefaultArgumentsAndOptions = (generator, opts) => { //setup meta var meta = opts.meta; if(!meta) { meta = utils.resolveMeta(module.parent); } generator.meta = meta; //setup arguments. if(meta.arg) { const args = opts.args ? opts.args : []; const arg = meta.arg; if(arg.required && args.length == 0) { log.error(`Missing argument ${chalk.yellow(arg.name)} ${chalk.white('- ' + arg.desc + "")}`); process.exit(0); } if(args.length > 0) { opts[arg.name] = args[0]; }else if(arg.defautls) { opts[arg.name] = arg.default; } } //setup options if(meta.opts) { Object.keys(meta.opts).forEach((name) => { generator.option(name, meta.opts[name]); }); } //delete unused properties from options. delete opts.meta; delete opts.args; } var Generator = require('yeoman-generator'); module.exports = class extends Generator { constructor(args, opts, checkProjectDirectory) { super(args, opts, pkg); if(opts.help) { this._printHelpAndExit(opts.meta); } //setup default argument & options setupDefaultArgumentsAndOptions(this, opts); //check and setup app. const appDirectory = path.join(this.destinationRoot(), 'app'); const configFile = utils.existsJsonOrYaml(path.join(appDirectory, 'config')); const isProjectDir = null != configFile; if(!isProjectDir && !(checkProjectDirectory === false)) { log.warn(`'${this.destinationRoot()}' is not a jmms project directory, exit!`); process.exit(0); } if(isProjectDir) { //setup app this.app = app.init(appDirectory, this.options); this.api = this.app.api; //resolve parent pom const xml = fs.readFileSync(this.destinationPath('./pom.xml')); let $ = cheerio.load(xml, { xmlMode: true }); this.parentRelativePath = $("project > parent > relativePath").text(); if(this.parentRelativePath.length <= 0) { this.parentRelativePath = undefined; } } } _printHelpAndExit(meta) { const optionsHelp = this._optionsHelp(meta); if(optionsHelp) { console.log(`${optionsHelp}`); } const argumentsHelp = this._argumentsHelp(meta); if(argumentsHelp) { console.log(`\n\n${argumentsHelp}`); } const usage = this._usageHelp(); if(usage.length > 0) { console.log(`\n\n${usage}\n`); }else{ console.log('\n'); } process.exit(0); } _argumentsHelp(meta) { if(!meta.arg) { return undefined; } const arg = meta.arg; const rows = []; rows.push([ '', '', arg.required ? '<' + arg.name + '>' : '[' + arg.name + ']', arg.type ? arg.type.name : '_', 'defaults:' + (arg.defaults ? arg.defaults : 'none'), '-', arg.desc ]) return " Arguments:\n\n" + table(rows); } _optionsHelp(meta) { if(!meta.opts) { return undefined; } const opts = meta.opts; const rows = []; Object.keys(opts).forEach((name) => { const opt = opts[name]; rows.push([ '', '', (opt.alias ? '-' + opt.alias : '') + ',', '--' + name, opt.desc ]) }); return "\n ---\n\n" + table(rows); } _usageHelp() { const filepath = path.resolve(this.sourceRoot(), '../USAGE'); const exists = fs.existsSync(filepath); if(exists) { return fs.readFileSync(filepath, 'utf8'); }else { return ''; } } _findExecutableJar() { const targetDir = path.join(this.destinationRoot(), 'target'); if(fs.existsSync(targetDir)) { const files = fs.readdirSync(targetDir); for (var i = 0, len = files.length; i < len; i++) { const filename = files[i]; if(filename.endsWith('-sources.jar')) { continue; } if(filename.endsWith('-javadoc.jar')) { continue; } if(filename.endsWith('-docker-info.jar')) { continue; } if(filename.endsWith('.jar')) { const fullPath = path.join(targetDir, filename); return fullPath; } } } return undefined; } _findOrCreateJar(done) { done.call(this,this._findOrCreateJarOnly()); } _findOrCreateJarOnly() { var jar = this._findExecutableJar(); if(!jar) { if(this.parentRelativePath) { log.info("Packaging executable jar use parent '" + this.parentRelativePath + "' ....\n"); this.spawnCommandSync('mvn',['package', '-Dmaven.javadoc.skip=true', '-Dmaven.test.skip=true', '-f', this.parentRelativePath]); }else { log.info("Packaging executable jar....\n"); this.spawnCommandSync('mvn',['package', '-Dmaven.javadoc.skip=true', '-Dmaven.test.skip=true']); } //find again. jar = this._findExecutableJar(); } return jar; } _runJavaCli(callback) { var mvnArgs = ''; if(log.isDebugEnabled()) { mvnArgs = '-X'; } var execArgs = ''; const profile = this.options['profile']; if(profile) { execArgs = '-Dapp.profile=' + profile; } //const jar = this._findOrCreateJarOnly(); //mvn exec:java -Dexec.mainClass=jmms.devtools.CliMain const cmd = 'mvn'; const args = _.compact([mvnArgs, 'exec:java', '-Dexec.mainClass=jmms.devtools.CliMain', '-Dexec.daemonThreadJoinTimeout=1', execArgs]); log.exec(cmd, args); const crossSpawn = require('cross-spawn'); const spawn = crossSpawn(cmd, args); /* const spawn = proc.spawn('java',['-classpath', jar, '-Djmms.dir=' + path.join(this.destinationRoot(), 'app'), '-Dloader.main=jmms.devtools.CliMain', 'org.springframework.boot.loader.PropertiesLauncher']); */ var init; var buf = ''; var result; var done; var closed = false; const cli = { exec: function(command, params, callback) { done = callback; log.debug(`Send cli command '${command}' ...`); spawn.stdin.write(JSON.stringify({command:command,params:params}) + '\n'); }, exit: function() { closed = true; spawn.stdin.write('exit'); spawn.stdin.end(); } }; // todo: how to get the default charset of system? var charset = 'utf8'; if(process.platform == 'win32'){ charset = 'gbk'; } spawn.stderr.pipe(iconv.decodeStream(charset)).pipe(process.stderr); spawn.stdout.on('data', (out) => { var s = iconv.decode(out, charset); //wait until the buf contains one or more lines. buf = buf + s; if(buf.indexOf('\n') < 0) { return; } if(!init && buf.indexOf('Waiting command') >= 0) { init = true; log.debug('Waiting command from callback...'); callback.call(this, cli); } //split lines var lastLineFully = buf.endsWith('\n'); var lines = _.split(buf, '\n'); for(var i=0;i<lines.length;i++) { var line = lines[i]; const start = line.indexOf('**start**'); const end = line.indexOf('**end**'); if(start >= 0 && end > 0) { result = line.substring(start + 9, end); }else { if(i < lines.length - 1 || lastLineFully) { if(log.isDebugEnabled()) { console.log(line); } } } } //reset the buf if(!lastLineFully) { buf = lines[lines.length-1]; }else { buf = ''; } if(result) { const response = JSON.parse(result); buf = undefined; result = undefined; const err = response.status >= 300; if(err) { if(response.status >= 500) { log.info(`\n${chalk.red.bold('ERROR!')} \n\n${chalk.red(response.result)}\n`); }else { log.info(`\n${chalk.yellow('Error :' + response.result)}\n`); } } const exit = done?done.call(this, err, response.result):undefined; if(!closed && (exit == undefined || exit === true)) { spawn.stdin.write('exit'); spawn.stdin.end(); } } }); spawn.stdin.setDefaultEncoding("utf8"); spawn.on('close', (code) => { closed = true; //log.info('Java cli exit ' + code); }); } _execCliCommand(command, param,callback) { this._runJavaCli((cli) => { cli.exec(command, param,(err, r) => { cli.exit(); callback.call(this, err, r); }); }); } _isMavenProject() { const pom = this.destinationPath('./pom.xml'); return fs.existsSync(pom); } _execSync(cmd, args) { if(_.isArray(args)) { args = _.pull(args, ''); } log.exec(cmd, args); return this.spawnCommandSync(cmd, args); } _jmms() { return require(this.destinationPath('.jmms.json')); } }