Gradle start script: Hide console - gradle

I'm using the Gradle application plugin for my Java app.
The start scripts Gradle generates work fine but I'd prefer if the console wouldn't pop up when the user starts the application.
Is there a way to do this?

By modifying the start script I got what I wanted (just for Windows for now).
build.gradle:
apply from: "IO.gradle"
// Modify the Windows start script so that no console is shown when the user starts the app.
// This also creates a copy of the original start script in case we want to use the console for debugging
startScripts << {
def startScriptDir = outputDir.getAbsolutePath()
def winStartScript = startScriptDir + "/" + applicationName + ".bat"
def winStartScriptCopy = startScriptDir + "/" + applicationName + "WithConsole.bat"
def overwriteExistingFile = true
copyFile(winStartScript, winStartScriptCopy, overwriteExistingFile)
modifyFile(winStartScript) {
// javaw.exe doesn't have a console
if(it.contains("java.exe")){
return it.replace("java.exe", "javaw.exe")
}
// Command that launches the app
else if(it.startsWith("\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS%")){
return "start \"\" /b " + it
}
// Leave the line unchanged
else{
return it
}
}
}
installApp {
// Include the additional start script
into("bin/"){
from(startScripts.outputDir)
}
}
IO.gradle:
import java.nio.*
import java.nio.file.*
/**
* This will completely re-write a file, be careful.
*
* Simple Usage:
*
* modifyFile("C:\whatever\whatever.txt") {
* if(it.contains("soil"))
* return null // remove dirty word
* else
* return it
* }
*
* The closure must return the line passed in to keep it in the file or alter it, any alteration
* will be written in its place.
*
* To delete an entire line instead of changing it, return null
* To add more lines after a given line return: it + "\n" + moreLines
*
* Notice that you add "\n" before your additional lines and not after the last
* one because this method will normally add one for you.
*/
def modifyFile(srcFile, Closure c) {
modifyFile(srcFile, srcFile, c)
}
def modifyFile(srcFile, destFile, Closure c={println it;return it}) {
StringBuffer ret = new StringBuffer();
File src = new File(srcFile)
File dest = new File(destFile)
src.withReader{reader->
reader.eachLine{
def line=c(it)
if(line != null) {
ret.append(line)
ret.append("\n")
}
}
}
dest.delete()
dest.write(ret.toString())
}
/**
* Copies a file specified at 'origin' to 'destination'.
* If 'overwrite' is set to true any existing file at 'destination' is overwritten (defaults to false).
*/
def copyFile(String origin, String destination, boolean overwrite=false){
Path origPath = Paths.get(origin)
Path destPath = Paths.get(destination)
def fileAtDestination = destPath.toFile()
if(fileAtDestination.exists()){
if(overwrite) {
fileAtDestination.delete()
Files.copy(origPath, destPath)
}
else{
println("Won't overwrite existing file $fileAtDestination")
println("Call 'copyFile(orig, dest, true)' to delete the existing file first")
}
}
else {
// There's no file at the destination yet
Files.copy(origPath, destPath)
}
}
// Define methods visible to other Gradle scripts
ext{
modifyFile = this.&modifyFile
copyFile = this.&copyFile
}
modifyFile is authored by Bill K.

This is an old post, but I stumbled across it with the same problem.
The modifications to the start script in the answer by Matthias Braun are good, however I think it's cleaner to do it in the following way:
Modify the default template for windows with the modifications pointed out (use javaw.exe and modify the startup command to silence the console).
Then modify the startScript template instead of modifying the generated scripts in place: This can be done as shown in How do I change unixStartScriptGenerator.template in the createStartScripts task so that distTar uses my custom template file in build.gradle?:
startScripts {
def tplName = 'windowsStartScriptWithoutConsoleTemplate.txt'
assert project.file(tplName).exists()
unixStartScriptGenerator.template = resources.text.fromFile(tplName)
}
Clearly, this does not also add a second startScript with the console present, but for me that is not necessary.

Related

How to walk a folder under resources folder in springboot in jar file execution

My springboot application comprises of an H2 database, where in code I traverse through a folder (rules) inside src/main/resources and execute SQL statements by reading files inside src/main/resources/rules.
rules folder again has few folders inside it. My current code, which executes well when run in IntelliJ is below:
private void loadDb() {
log.info("Hello!") // this prints Hello! when executed via jar
File rulesDir = new File(getClass().getClassLoader().getResource("/rules").getFile())
log.info("Read file: ", rulesDir) // this prints empty when run via jar
if (rulesDir.exists() && rulesDir.isDirectory()) {
File[] fileArr = rulesDir.listFiles()
log.info("**********************************************")
log.info("Files from rules directory : " + rulesDir)
log.info("**********************************************")
recursiveLoad(fileArr, 0)
}
}
private void recursiveLoad(File[] fileArr, int level) {
for (File f : fileArr) {
for (int i = 0; i < level; i++)
System.out.print(TAB)
if (f.isFile() && f.getName().substring(f.getName().indexOf(DOT) + 1) == "insert") {
log.info(f.getName())
executeSqlStatements(f)
} else if (f.isDirectory()) {
log.info("[" + f.getName() + "]")
recursiveLoad(f.listFiles(), level + 1)
}
}
}
So, my issue is, this above code doesn't work when executed via JAR. I did some research and found out that we need to read files as streams for it to work via JAR execution but I am not sure how to implement above in a way that it works in JAR execution.

Programmatically create a file in Gradle build script

I'm sure it is trivial, but I cannot find a way to do it...
In my build.gradle I want processResources task to create (not e.g. copy or fill some tempate) a resource file to be loaded by Java program.
I achieved the following:
processResources {
...
// This is a collection of files I want to copy into resources.
def extra = configurations.extra.filter { file -> file.isFile () }
// This actually copies them to 'classes/extra'. It works.
into ('extra') {
from extra
}
doLast {
// I want to write this string (list of filenames, one per
// line) to 'classes/extra/list.txt'.
println extra.files.collect { file -> file.name }.join ("\n")
}
}
You can see above a println that prints exactly what I need. But how do I write this string to a file instead of the console?
You can use the following code
task writeToFile {
// sample list.(you already have it as extra.files.collect { file -> file.name })
List<String> sample = [ 'line1','line2','line3' ] as String[]
// create the folders if it does not exist.(otherwise it will throw exception)
File extraFolder = new File( "${project.buildDir}/classes/extra")
if( !extraFolder.exists() ) {
extraFolder.mkdirs()
}
// create the file and write text to it.
new File("${project.buildDir}/classes/extra/list.txt").text = sample.join ("\n")
}
One way to implement this would be to define a custom task that will generate this "index" file from the extra configuration, and make the existing processResources task depend on this custom task.
Something like that would work:
// Task that creates the index file which lists all extra libs
task createExtraFilesIndex(){
// destination directory for the index file
def indexFileDir = "$buildDir/resources/main"
// index filename
def indexFileName = "extra-libs.index"
doLast{
file(indexFileDir).mkdirs()
def extraFiles = configurations.extra.filter { file -> file.isFile () }
// Groovy concise syntax for writing into file; maybe you want to delete this file first.
file( "$indexFileDir/$indexFileName") << extraFiles.files.collect { file -> file.name }.join ("\n")
}
}
// make processResources depends on createExtraFilesIndex task
processResources.dependsOn createExtraFilesIndex
def dirA = "${rootDir}/dirA" //creates dirA directory in root project directory
new File("${dirA}/yourFile.txt").append("\n") //creates yourFile.txt file in above created directory

Gradle how to change version number in source code

Java code:
public static String VERSION = "version_number";
Gradle build.gradle
version = '1.0'
How to set the version in java code from grade? The version must be in source code.
Is there a convenient way? A not-so-nice way:
copy the java file to another location, e.g. build/changed-source
change the version in the source, by replacing token
add the build/changed-source in main source set.
I'd do similar to Michael Easter but with these differences
Store generated sources separately from main sources (src/main/java and $buildDir/generated/java). This has the added benefit of not needing custom gitignore
Generate in a subdirectory of $buildDir so that clean task will delete the generated sources
Use a separate task for code generation with proper up-to-date & skip support
Use Copy.expand(Map) to do the token replacement
Since its directory based, everything in src/template/java will have tokens replaced. You can easily add more templates in future
src/template/java/com/foo/BuildInfo.java
package com.foo;
public class BuildInfo {
public static String getVersion() {
return "${version}";
}
}
build.gradle
task generateJava(type:Copy) {
def templateContext = [version: project.version]
inputs.properties templateContext // for gradle up-to-date check
from 'src/template/java'
into "$buildDir/generated/java"
expand templateContext
}
sourceSets.main.java.srcDir "$buildDir/generated/java" // add the extra source dir
compileJava.dependsOn generateJava // wire the generateJava task into the DAG
One method is to similar to your not-so-nice way, but slightly easier. Consider a file in templates/BuildInfo.java:
package __PACKAGE;
public class BuildInfo {
private static final String version = "__VERSION";
private static final String buildTimestamp = "__BUILD_TIMESTAMP";
public String toString() {
return "version : " + version + "\n" +
"build timestamp : " + buildTimestamp + "\n";
}
}
This file can then be "stamped" with information as first thing in the compileJava task and written to src/main/java/your/package/BuildInfo.java:
def targetPackage = 'net/codetojoy/util'
def targetPackageJava = 'net.codetojoy.util'
def appVersion = project.appVersion // from gradle.properties
def buildTimeStamp = new Date().toString()
compileJava {
doFirst {
ant.mkdir(dir: "${projectDir}/src/main/java/${targetPackage}")
def newBuildInfo = new File("${projectDir}/src/main/java/${targetPackage}/BuildInfo.java")
def templateBuildInfo = new File("${projectDir}/templates/TemplateBuildInfo.java")
newBuildInfo.withWriter { def writer ->
templateBuildInfo.eachLine { def line ->
def newLine = line.replace("__PACKAGE", targetPackageJava)
.replace("__VERSION", appVersion)
.replace("__BUILD_TIMESTAMP", buildTimeStamp)
writer.write(newLine + "\n");
}
}
}
}
A working example is provided here. Everything would be stored in source-control except the src/main/java/your/package/BuildInfo.java file. Note the version would be stored in gradle.properties.

How to do test coverage with blanket and mocha in sailsjs

I have a Sails project with a test/ folder containing all my mocha tests and want to create a test coverage report using following command:
mocha --require blanket --reporter html-cov > coverage.html
The blanket configuration inside my package.json looks following:
"blanket": {
"pattern": ["lib", "api", "config"],
"data-cover-never": "node_modules",
"data-cover-reporter-options": {
"shortnames": true
}
}
I included both Sails folders api/ and config/ as they probably contain testable code and a folder lib/ containing most of my application's logic.
Sadly the blanket coverage module only covers files that are directly included in my test files. Since Sails loads most of my files in api/ and config/ dynamically they don't show up in my coverage reports.
Any ideas in how to integrate the Sails framework with blanket?
I am unfamilair with Sails but I had the same problem using Blanket.js and posted a comment with a work-around on the Blanket.js bugtracker, here it is:
https://github.com/alex-seville/blanket/issues/361#issuecomment-34002054
The workaround I suggested there felt very much like a hack. I eventually abandoned Blanket in favor of Istanbul: https://github.com/gotwarlost/istanbul
Istanbul gives you both more metrics (statement, line, function and branch coverage) and outputs an excellent bunch of .html files allowing you to analyze how to improve your code.
Blanket.js appears not to be maintained very well given the 79+ open issues currently.
If you do want to stick to blanket.js you can follow the suggestion I posted on the Blanket.js bug tracker and try to include all files within the test run by recursively looping through all relevant code directories. The code I used to do that at the time was as the following (I would definitely refactor this, but it shows the intent):
'use strict';
/**
* This file is loaded by blanket.js automatically before it instruments code to generate a code coverage report.
*/
var fs = require('fs');
var log = require('winston');
var packageJson = require('./package.json');
// For some reason the blanket config in package.json does not work automatically, set the settings manually instead
require('blanket')({
// Only files that match this pattern will be instrumented
pattern: packageJson.config.blanket.pattern
});
/**
* Walks through a directory structure recursively and executes a specified action on each file.
* #param dir {(string|string[])} The directory path or paths.
* #param action {function} The function that will be executed on any files found.
* The function expects two parameters, the first is an error object, the second the file path.
*/
function walkDir(dir, action) {
// Assert that action is a function
if (typeof action !== "function") {
action = function (error, file) {
};
}
if (Array.isArray(dir)) {
// If dir is an array loop through all elements
for (var i = 0; i < dir.length; i++) {
walkDir(dir[i], action);
}
} else {
// Make sure dir is relative to the current directory
if (dir.charAt(0) !== '.') {
dir = '.' + dir;
}
// Read the directory
fs.readdir(dir, function (err, list) {
// Return the error if something went wrong
if (err) return action(err);
// For every file in the list, check if it is a directory or file.
// When it is a directory, recursively loop through that directory as well.
// When it is a file, perform action on file.
list.forEach(function (file) {
var path = dir + "/" + file;
fs.stat(path, function (err, stat) {
if (stat && stat.isDirectory()) {
walkDir(path, action);
} else {
action(null, path);
}
});
});
});
}
};
// Loop through all paths in the blanket pattern
walkDir(packageJson.config.blanket.pattern, function (err, path) {
if (err) {
log.error(err);
return;
}
log.error('Including ' + path + ' for blanket.js code coverage');
require(path);
});
My advice would be to drop Blanket.js for something else.

Dynamics AX 2009: Batch Trouble with AsciiIO class

I have a custom class, we'll call it FileProcessUpload and it extends RunBaseBatch. It more or less creates a CSV file and then uploads it to an FTP server. When the class is run manually, everything works fine. However, when submitted as a Batch Job, there is an error in the infolog stating "AsciiIO object not initialized".
Probably the most important thing to note here is that this Batch Job is being delegated to a different AOS.
Here is a cropped down version of the offending code:
void CreateFiles()
{
#File
AsciiIO asciiio;
FileIOPermission permission;
ATable aTable;
str outputFile;
str directory;
;
directory = #'C:\Uploads';
ouptutFile = directory + #'\output.csv';
if (!WinAPI::folderExists(directory))
{
WinAPI::createDirectory(directory);
}
// Try to assert the appropriate file access mode
permission = new FileIOPermission(outputFile, #io_write);
permission.assert();
// Try to open the file for writing
asciiio = new AsciiIO(outputFile, #io_write);
if (asciiio != null)
{
while select aTable
{
// Write the necessary lines into the file
asciiio.write(aTable.field1 + ',' + aTable.field2);
}
}
else
{
error('Could not create file: ' + outputFile);
}
// Close file and release permission assertion
asciiio = null;
CodeAccessPermission::revertAssert();
}
Does the service user that Ax is running under have permissions to read/write the file?
You are using the WinAPI class, but should you be using WinAPIServer class instead? You may be executing on the server of course.
Do you need to add to your class the following public boolean runsImpersonated() { return false; } and run this class on a client?
Good luck
Edit: Executing your code via the server static void mainOnServer(Args args) method signature is commonly used (see PurchFormLetter class for it's usage) to make sure that you execute on the server. It is called from static void main(Args args)
Use file path and file name instead of str as directory and name
If runbasebatch then should put pack/uppack filePath and fileName and put it into currentVersion control at classdeclaration.
If you move/delete/encrytion/read file, using system.io.file /system.io.stream, or streamreader, or system.net.ftpwebrequest, and system.net.ftpwebresponse, remember to run on server static void method for this...
Any file format I have done, txt, .csv, .gpg, I move around file easily in/out ax to other server, no problem to just write a file inside of AX by fellowing the above rule..

Resources