/**
* i-net software provides programming examples for illustration only, without warranty
* either expressed or implied, including, but not limited to, the implied warranties
* of merchantability and/or fitness for a particular purpose. This programming example
* assumes that you are familiar with the programming language being demonstrated and
* the tools used to create and debug procedures. i-net software support professionals
* can help explain the functionality of a particular procedure, but they will not modify
* these examples to provide added functionality or construct procedures to meet your
* specific needs.
*
* Copyright © 1999-2026 i-net software GmbH, Berlin, Germany.
**/
package com.inet.taskplanner.databaseaction;
import static com.inet.taskplanner.databaseaction.TaskPlannerDatabaseActionServerPlugin.MSG;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.inet.id.GUID;
import com.inet.taskplanner.TaskPlannerServerPlugin;
import com.inet.taskplanner.server.api.action.ResultActionDefinition;
import com.inet.taskplanner.server.api.action.ResultActionFactory;
import com.inet.taskplanner.server.api.action.ResultActionInfo;
import com.inet.taskplanner.server.api.common.SummaryEntry;
import com.inet.taskplanner.server.api.common.SummaryInfo;
import com.inet.taskplanner.server.api.error.ValidationException;
import com.inet.taskplanner.server.api.field.Field;
import com.inet.taskplanner.server.api.field.PasswordField;
import com.inet.taskplanner.server.api.field.TextField;
import com.inet.taskplanner.server.api.result.ResultFlavor;
/**
* A result action factory defines how the action is presented to the user and may include validation of user-configurable properties.
*
* This factory produces actions that allow saving files into the configured database, using the JDBC URL for connection.
*/
public class JdbcDatabaseResultActionFactory extends ResultActionFactory {
/**
* Key of the JDBC URL property.
*/
public static final String JDBC_URL = "url";
/**
* Key of the user name property.
*/
public static final String USERNAME = "username";
/**
* Key of the password property.
*/
public static final String PASSWORD = "password";
/**
* Key of the table property.
*/
public static final String TABLE = "table";
/**
* Key of the file column property.
*/
public static final String FILE_COLUMN = "filecolumn";
/**
* Key of the optional identifier column property.
*/
public static final String IDENTIFIER_COLUMN = "identifiercolumn";
/**
* Key of the value property for identifier column. Type of value may be long or string.
*/
public static final String IDENTIFIER_VALUE = "identifiervalue";
/**
* Creates instance of the factory.
*/
public JdbcDatabaseResultActionFactory() {
super( "action.database.jdbc" );
}
/**
* {@inheritDoc}
*/
@Override
public List getSupportedFlavors( ResultActionDefinition definition ) {
// Actions produced by this factory are interested in file results
return Arrays.asList( ResultFlavor.FILE );
}
/**
* {@inheritDoc}
*/
@Override
public ResultActionInfo getInformation( @Nullable GUID taskID ) {
String name = MSG.getMsg( "taskplanner.databaseaction.name.jdbc" ); // A meaningful name
String description = MSG.getMsg( "taskplanner.databaseaction.description.jdbc" ); // A short description
URL icon = getClass().getResource( "/com/inet/taskplanner/databaseaction/taskplanner_databaseaction_32.png" ); // white and transparent icon in 32x32 pixels
String helpkey = null; // The result action does not have its own help page
List fields = new ArrayList(); // Fields displayed for configuration. Use null if no configuration is required.
fields.add( new TextField( JDBC_URL, MSG.getMsg( "taskplanner.databaseaction.jdbcurl" ) ) );
fields.add( new TextField( USERNAME, MSG.getMsg( "taskplanner.databaseaction.username" ) ) );
fields.add( new PasswordField( PASSWORD, MSG.getMsg( "taskplanner.databaseaction.password" ) ) );
fields.add( new TextField( TABLE, MSG.getMsg( "taskplanner.databaseaction.table" ) ) );
fields.add( new TextField( FILE_COLUMN, MSG.getMsg( "taskplanner.databaseaction.filecolumn" ) ) );
TextField identifierColumnField = new TextField( IDENTIFIER_COLUMN, MSG.getMsg( "taskplanner.databaseaction.identifiercolumn" ) );
identifierColumnField.setPlaceholder( MSG.getMsg( "taskplanner.databaseaction.placeholder.optional" ) );
fields.add( identifierColumnField );
TextField identifierValueField = new TextField( IDENTIFIER_VALUE, MSG.getMsg( "taskplanner.databaseaction.identifiervalue" ) );
identifierValueField.setPlaceholder( MSG.getMsg( "taskplanner.databaseaction.placeholder.optional" ) );
fields.add( identifierValueField );
return new ResultActionInfo( getExtensionName(), name, description, icon, helpkey, fields );
}
/**
* {@inheritDoc}
*/
@Override
protected void validate( @Nonnull ResultActionDefinition definition, @Nullable GUID taskID ) throws ValidationException {
String jdbcURL = definition.getProperty( JDBC_URL );
String username = definition.getProperty( USERNAME );
String decodedPassword = decodePassword( definition.getProperty( PASSWORD ) );
String table = definition.getProperty( TABLE );
String fileColumn = definition.getProperty( FILE_COLUMN );
// Checks whether table and column names are provided (other settings will be verified when attempting to establish connection)
if( table == null || table.trim().isEmpty() ) {
throw new ValidationException( MSG.getMsg( "taskplanner.databaseaction.table.empty" ) );
}
if( fileColumn == null || fileColumn.trim().isEmpty() ) {
throw new ValidationException( MSG.getMsg( "taskplanner.databaseaction.filecolumn.empty" ) );
}
// Checks whether connection to the database is possible and whether configured table exists
String sql = "SELECT COUNT(*) FROM " + table;
try (Connection con = DriverManager.getConnection( jdbcURL, username, decodedPassword ); Statement stm = con.createStatement(); ResultSet rs = stm.executeQuery( sql )) {
if( rs.next() ) {
rs.getInt( 1 );
}
} catch( Exception ex ) {
throw new ValidationException( ex.getMessage() );
}
}
/**
* {@inheritDoc}
*/
@Override
protected JdbcDatabaseResultAction createInstanceFrom( @Nonnull ResultActionDefinition definition ) {
String jdbcURL = definition.getProperty( JDBC_URL );
String username = definition.getProperty( USERNAME );
String decodedPassword = decodePassword( definition.getProperty( PASSWORD ) );
String table = definition.getProperty( TABLE );
String fileColumn = definition.getProperty( FILE_COLUMN );
String identifierColumn = definition.getProperty( IDENTIFIER_COLUMN );
String identifierValue = definition.getProperty( IDENTIFIER_VALUE );
if( identifierColumn == null || identifierColumn.trim().isEmpty() ) {
return new JdbcDatabaseResultAction( jdbcURL, username, decodedPassword, table, fileColumn );
}
return new JdbcDatabaseResultAction( jdbcURL, username, decodedPassword, table, fileColumn, identifierColumn, identifierValue );
}
/**
* {@inheritDoc}
*/
@Override
public SummaryInfo getSummary( @Nonnull ResultActionDefinition definition ) {
List result = new ArrayList<>();
result.add( new SummaryEntry( MSG.getMsg( "taskplanner.databaseaction.jdbcurl" ), definition.getProperty( JDBC_URL ) ) );
result.add( new SummaryEntry( MSG.getMsg( "taskplanner.databaseaction.table" ), definition.getProperty( TABLE ) ) );
result.add( new SummaryEntry( MSG.getMsg( "taskplanner.databaseaction.filecolumn" ), definition.getProperty( FILE_COLUMN ) ) );
String identifierColumn = definition.getProperty( IDENTIFIER_COLUMN );
if( identifierColumn != null && !identifierColumn.trim().isEmpty() ) {
result.add( new SummaryEntry( MSG.getMsg( "taskplanner.databaseaction.identifiercolumn" ), identifierColumn ) );
}
String identifierValue = definition.getProperty( IDENTIFIER_VALUE );
if( identifierValue != null && !identifierValue.trim().isEmpty() ) {
result.add( new SummaryEntry( MSG.getMsg( "taskplanner.databaseaction.identifiervalue" ), identifierValue ) );
}
return new SummaryInfo( result );
}
/** Decodes password, if provided.
* @param password password to decode.
* @return decoded password.
*/
private String decodePassword( String password ) {
if( password != null && !password.isEmpty() ) {
try {
password = PasswordField.decodePassword( password );
} catch( IOException ex ) {
TaskPlannerServerPlugin.LOGGER.error( ex );
}
}
return password;
}
}