Category Archives: IT Stuff

Find on different collections to create a document using mongoose

I need to query multiple collections to prepare a MongoDB document and then save it using Mongoose and NodeJS. The solution is to use async.parallel
I have two source collections, Robot, Target and the destination collection Activity.

For first, async must be added:

var async = require('async');

then:

async.parallel({
 
    robotFind: function(cb) { Robot.find({ "_id": jsonContent.robotId }).exec(cb); },
    targetFind: function(cb) { Target.find({ "_id": jsonContent.targetId }).exec(cb); }
 
}, function(err, result) {
 
    activity.robot = result.robotFind[0];
    activity.action = result.actionFind[0];
    activity.target = result.targetFind[0];
 
    activity.execution_date = jsonContent.execution_date;
    activity.alert = jsonContent.alert;
    activity.result = executionResult;
 
    activity.description = jsonContent.description;
 
    activity.save(function(err) {
        if (err) {
            console.log('[postActivity] ' + err)
            res.status(500).json({ error: err.message })
        } else {
            console.log('[postActivity] Saved!')
            res.status(200).json({ message: activity })
        }
    })
}

So, first part queries the MongoDB and fill the object result. Second part consumes result object, create the new document and save it.

PS: if you like it please, click on the banner :)

Tagged , ,

How to use Spring DataSource bean as data source for Log4j 2 JDBC appender

I would like to log log4j2 messages into a relational database using the datasource defined on application context and initialized using spring using log4j 2.10.

One possibility is to add a JDBC appender inside log4j2 xml configuration but, Log4j is initialized before Spring so, dataSource won’t be available at runtime so the only solution is to add an appender programmatically. Of course, it is possible to use a log4j2 jdbc appender but, this approach allow to have the possibility to use spring.properties file to override the spring application context with proper environment settings in according to my profile.

This is the datasource defined on application context xml:

<!--  ############ SQLSERVER DATABASE SECTION ############ -->
<bean id="dataSourceMSSqlServer" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
    <property name="url" value="jdbc:sqlserver://${sqlserver.hostname};databaseName=${sqlserver.database};" />
    <property name="username" value="${sqlserver.user}" />
    <property name="password" value="${sqlserver.pass}" />
</bean>

this allow me to configure different database for every environment.
This is the table on which I want to log the entries:

[Id] [INT] IDENTITY(1,1) NOT NULL,
[CreatedTimeStamp] [datetimeoffset](7) NOT NULL,
[Level] [INT] NOT NULL,
[SOURCE] [nvarchar](MAX) NULL,
[Message] [nvarchar](MAX) NULL,
[Content] [nvarchar](MAX) NULL,
[ProductName] [nvarchar](MAX) NULL,
[Version] [nvarchar](MAX) NULL,
[LogType] [INT] NOT NULL DEFAULT ((0)),
[AuditEventType] [INT] NULL,
[UserId] [nvarchar](128) NULL,

The plan is to create a spring bean, inject the DataSource bean, and add JDBC Appender configuration dynamically in @PostConstruct method.

package com.afm.web.utility;
 
import java.sql.Connection;
import java.sql.SQLException;
 
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
 
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.db.ColumnMapping;
import org.apache.logging.log4j.core.appender.db.jdbc.ColumnConfig;
import org.apache.logging.log4j.core.appender.db.jdbc.ConnectionSource;
import org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender;
import org.apache.logging.log4j.core.config.AppenderRef;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class JDBCLog {
 
    @Autowired
    private DataSource dataSourceMSSqlServer;
 
    // Inner class
    class Connect implements ConnectionSource {
 
	private DataSource dsource;
 
	public Connect(DataSource dsource) {
	    this.dsource = dsource;
	}
 
	@Override
	public Connection getConnection() throws SQLException {
	    return this.dsource.getConnection();
	}
 
    }
 
    public JDBCLog() {}
 
    @PostConstruct
    private void init(){
 
	System.out.println("####### JDBCLog init() ########");      
	final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); 
	final Configuration config = ctx.getConfiguration();
 
	// Here I define the columns I want to log. 
	ColumnConfig[] columnConfigs = new ColumnConfig[] {
	    ColumnConfig.newBuilder()
                .setName("CreatedTimeStamp")
                .setPattern(null)
                .setLiteral(null)
                .setEventTimestamp(true)
                .setUnicode(false)
                .setClob(false).build(),
	    ColumnConfig.newBuilder()
                .setName("Source")
                .setPattern("%K{className}")
                .setLiteral(null)
                .setEventTimestamp(false)
                .setUnicode(false)
                .setClob(false).build(),
	    ColumnConfig.newBuilder()
                .setName("Level")
                .setPattern("%level")
                .setLiteral(null)
                .setEventTimestamp(false)
                .setUnicode(false)
                .setClob(false).build(),
	    ColumnConfig.newBuilder()
                .setName("Message")
                .setPattern("%K{message}")
                .setLiteral(null)
                .setEventTimestamp(false)
                .setUnicode(false)
                .setClob(false).build(),
	    ColumnConfig.newBuilder()
                .setName("Content")
                .setPattern("%K{exception}")
                .setLiteral(null)
                .setEventTimestamp(false)
                .setUnicode(false)
                .setClob(false).build(),
	    ColumnConfig.newBuilder()
                .setName("ProductName")
                .setPattern(null)
                .setLiteral("'DHC'")
                .setEventTimestamp(false)
                .setUnicode(false)
                .setClob(false).build(),
	    ColumnConfig.newBuilder()
                .setName("Version")
                .setPattern(null)
                .setLiteral("'1.0'")
                .setEventTimestamp(false)
                .setUnicode(false)
                .setClob(false).build(),
	    ColumnConfig.newBuilder()
                .setName("AuditEventType")
                .setPattern("%K{eventId}")
                .setLiteral(null)
                .setEventTimestamp(false)
                .setUnicode(false)
                .setClob(false).build(),
	    ColumnConfig.newBuilder()
                .setName("UserId"
                .setPattern("%K{userId}")
                .setLiteral(null)
                .setEventTimestamp(false)
                .setUnicode(false)
                .setClob(false).build(),
	    ColumnConfig.newBuilder()
                .setName("LogType")
                .setPattern("%K{logType}")
                .setLiteral(null)
                .setEventTimestamp(false)
                .setUnicode(false)
                .setClob(false).build()
        };
 
	Appender jdbcAppender = JdbcAppender.newBuilder()
		.setBufferSize(0)
                .setColumnConfigs(columnConfigs)
                .setColumnMappings(new ColumnMapping[]{})
                .setConnectionSource(new Connect(dataSourceMSSqlServer))
                .setTableName("dhc.LogItems")
                .withName("databaseAppender")
                .withIgnoreExceptions(true)
                .withFilter(null)
                .build();
 
	jdbcAppender.start();
	config.addAppender(jdbcAppender);
 
	// Create an Appender reference.
	// @param ref The name of the Appender.
	// @param level The Level to filter against.
	// @param filter The filter(s) to use.
	// @return The name of the Appender.
	AppenderRef ref= AppenderRef.createAppenderRef("JDBC_Appender", null, null);
        AppenderRef[] refs = new AppenderRef[] {ref};
 
        /*
         * Factory method to create a LoggerConfig.
         *
         * @param additivity true if additive, false otherwise.
         * @param level The Level to be associated with the Logger.
         * @param loggerName The name of the Logger.
         * @param includeLocation whether location should be passed downstream
         * @param refs An array of Appender names.
         * @param properties Properties to pass to the Logger.
         * @param config The Configuration.
         * @param filter A Filter.
         * @return A new LoggerConfig.
         * @since 2.6
         */
        LoggerConfig loggerConfig = LoggerConfig.createLogger(
                false, Level.DEBUG, "JDBC_Logger", null, refs, null, config, null);        
        loggerConfig.addAppender(jdbcAppender, null, null);
 
        config.addLogger("JDBC_Logger", loggerConfig);       
        ctx.updateLoggers();  
 
        System.out.println("####### JDBCLog init() - DONE ########");  
 
    }
 
    public DataSource getDataSource() {
	return dataSourceMSSqlServer;
    }
 
    public void setDataSource(DataSource dataSourceMSSqlServer) {
	this.dataSourceMSSqlServer = dataSourceMSSqlServer;
    }	  
 
}

At this point, is possible to call the logger from the code in this way:

Logger jdbcLogger = LogManager.getContext(false).getLogger("JDBC_Logger"); 
jdbcLogger.info(new StringMapMessage()
    .with("eventId", AuditEventType.Logger_General.toString())
    .with("exception", "")
    .with("userId", "TESTUSER")
    .with("message", "TEST!!")
    .with("className", 
        this.getClass().getPackage().toString().replaceAll("package ", "") 
        + "." + this.getClass().getSimpleName() 
        + "." + new Object() {}.getClass().getEnclosingMethod().getName())
);

If you are using Log4j in a Servlet 2.5 web application, or if you have disabled auto-initialization with the isLog4jAutoInitializationDisabled context parameter, you must configure the Log4jServletContextListener and Log4jServletFilter in the deployment descriptor or programmatically. The filter should match all requests of any type. The listener should be the very first listener defined in your application, and the filter should be the very first filter defined and mapped in your application. This is easily accomplished using the following web.xml code:

<listener>
    <listener-class>
        org.apache.logging.log4j.web.Log4jServletContextListener
    </listener-class>
</listener>
 
<filter>
    <filter-name>log4jServletFilter</filter-name>
    <filter-class>
        org.apache.logging.log4j.web.Log4jServletFilter
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>log4jServletFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
    <!-- 
        Servlet 3.0 w/ disabled auto-initialization only; 
        not supported in 2.5 
    -->
    <dispatcher>ASYNC</dispatcher>
</filter-mapping>

This dependency must be added to pom.xml:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-web</artifactId>
    <version>2.10.0</version>
</dependency>

Of course, if you are not already included, add it to the component-scan into Spring application-context:

<context:component-scan base-package="com.afm.web.utility" />

PS: if you like it please, click on the banner :)

Tagged , ,

Warning about SSL connection when connecting to MySQL database

After a recent update of mySql, I get this warning:

WARN: Establishing SSL connection without server's identity verification is not recommended. 
According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established 
by default if explicit option isn't set. For compliance with existing applications not using SSL 
the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL 
by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.

To disable disable SSL and also suppress the SSL warning, it’s possible to set to false the useSSL parameter on the connection string:

jdbc:mysql://localhost:3306/myDb?autoReconnect=true&useSSL=false

On applicationContext, something like this:

<!--  ############ MY SQL DATABASE SECTION ############ -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">	
    <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
    <property name="url">
        <value>jdbc:mysql://${mysql.hostname}:${mysql.port}/${mysql.db}?useSSL=false</value>
    </property>
    <property name="username"><value>${mysql.user}</value></property>
    <property name="password"><value>${mysql.password}</value></property>
</bean>
Tagged ,

Convert timestamp long to normal date format

One simply way to convert a Long time stamp into a formatted string is (time paramter is Long timestamp):

Date date = new Date(time);
Format format = new SimpleDateFormat("yyyy MM dd HH:mm:ss");
return format.format(date);

These packages must be included.

import java.sql.Date;
import java.text.Format;
import java.text.SimpleDateFormat;

Date and Time Patterns

Date and time formats are specified by date and time pattern strings. Within date and time pattern strings, unquoted letters from 'A' to 'Z' and from 'a' to 'z' are interpreted as pattern letters representing the components of a date or time string. Text can be quoted using single quotes (') to avoid interpretation. "''" represents a single quote.
All other characters are not interpreted; they’re simply copied into the output string during formatting or matched against the input string during parsing.

The following pattern letters are defined (all other characters from 'A' to 'Z' and from 'a' to 'z' are reserved):

Letter Date or Time Component Presentation Examples
G Era designator Text AD
y Year Year 1996; 96
M Month in year Month July; Jul; 07
w Week in year Number 27
W Week in month Number 2
D Day in year Number 189
d Day in month Number 10
F Day of week in month Number 2
E Day in week Text Tuesday; Tue
a Am/pm marker Text PM
H Hour in day (0-23) Number 0
k Hour in day (1-24) Number 24
K Hour in am/pm (0-11) Number 0
h Hour in am/pm (1-12) Number 12
m Minute in hour Number 30
s Second in minute Number 55
S Millisecond Number 978
z Time zone General time zone Pacific Standard Time; PST; GMT-08:00
Z Time zone RFC 822 time zone -0800
Tagged

Date based query using milliseconds (Java long) time on MongoDB

Let’s suppose I need to search all records that match a date condition. On MongoDB I’ve a bunch of data like this:

{
    "_id" : "9ed3b937-0f43-4613-bd58-cb739a8c5bf6",
    "userModels" : {
        "5080" : {
            "generated_date_timestamp" : NumberLong(1413382499442),
            "model_id" : 5080,
        },
    }
    "values" : {}
}

This is the query:

db.anonProfile.find({ 
   "userModels.5080.generated_date_timestamp" : { 
      "$gte" : ISODate("2013-10-01T00:00:00.000Z").getTime() 
   }
});
.getTime()

allows to translate ISODate into a NumberLong timestamp.

Tagged

Gap di competenze per Industria 4.0

«Non vedo un rischio di disoccupazione maggiore provocato dalle tecnologie, il saldo non sarà negativo», spiega ancora l’economista Ocse che però vede una minaccia nell’aumento delle diseguaglianze – «sia come stipendi che come prospettive di carriera» – tra chi ha competenze adeguate e chi no: «I lavori intermedi già negli ultimi venti anni sono stati colpiti, ma negli ultimi anni questo processo si è velocizzato».

Gap di competenze per Industria 4.0

Allarme Ocse: in Italia un lavoratore su due ha scarsissime o nulle conoscenze in ambito Ict

Source: www.ilsole24ore.com/art/impresa-e-territori/2016-11-19/gap-competenze-industria-40-171518.shtml?uuid=AD3EaMyB

Tagged

The Noble Art of Maintenance Programming

Yes, I am a big fan of code readability and refactoring. I always have in mind this:

Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it? – Brian Kernighan

Refactoring is a big challenge, you must be twice smarter of the developer who debugged is code (and the challenge of rewriting other people’s code makes it fun as hell).

Read this: The Noble Art of Maintenance Programming

Tagged

Delete Files Older Than x Days on Linux

Command Syntax

find /folder/files* -mtime +15 -exec rm {} \;

The first argument is the path to the files. This can be a path, a directory, or a wildcard as in the example above. The second argument, -mtime, is used to specify the number of days old that the file is. If you enter +15, it will find files older than 15 days. The third argument, -exec, allows you to pass in a command such as rm. The {} \; at the end is required to end the command.

Another way to do it is:

/bin/rm `find /var/tmp/stuff -mtime +15 -print`
Tagged

How do I fix a 65535 bytes limit Stacktrace?

It is possible that after an upgrade you may encounter this error on some of the more complex pages and root cause provided by tomcat console is :

Unable to compile class for JSP
 The code of method _jspService(HttpServletRequest, HttpServletResponse) is exceeding the 65535 bytes limit

To solve the issue you need to locate the file [Tomcat_Home]/conf/web.xml and search the file for ‘JspServlet’. This should return an xml node of containing some values. You will need to add an additional the same as the below.

<init-param>      
        <param-name>mappedfile</param-name>      
        <param-value>false</param-value> 
</init-param>

The resulting block of the web.xml file, once you have inserted the above, should look like the code below.

<servlet> 
        <servlet-name>jsp</servlet-name>
        <servlet-class>
                org.apache.jasper.servlet.JspServlet
        </servlet-class>
        <init-param>
                <param-name>fork</param-name>
                <param-value>false</param-value>
        </init-param>
        <init-param>
                <param-name>xpoweredBy</param-name>
                <param-value>false</param-value>
        </init-param>
        <init-param>
                <param-name>mappedfile</param-name>
                <param-value>false</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
</servlet>

Save the file and restart the Tomcat service.

Tagged ,

How to kill all processes with a given (partial) name?

Usually I open more than on instance of tail -f to monitor my application, so, to kill all with one single command I can use this:

ps -ef | grep tail | grep -v grep | awk '{print $2}' | xargs kill -9
Tagged ,