Java Utils

Nordstrom Java Utils is a small collection of general-purpose utility classes with wide applicability.

License

License

Categories

Categories

Java Languages
GroupId

GroupId

com.github.nordstrom.tools
ArtifactId

ArtifactId

java-utils
Last Version

Last Version

1.3.1
Release Date

Release Date

Type

Type

jar
Description

Description

Java Utils
Nordstrom Java Utils is a small collection of general-purpose utility classes with wide applicability.
Project URL

Project URL

https://github.com/Nordstrom/Java-Utils
Source Code Management

Source Code Management

https://github.com/Nordstrom/Java-Utils/tree/master

Download java-utils

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.nordstrom.tools/java-utils/ -->
<dependency>
    <groupId>com.github.nordstrom.tools</groupId>
    <artifactId>java-utils</artifactId>
    <version>1.3.1</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.nordstrom.tools/java-utils/
implementation 'com.github.nordstrom.tools:java-utils:1.3.1'
// https://jarcasting.com/artifacts/com.github.nordstrom.tools/java-utils/
implementation ("com.github.nordstrom.tools:java-utils:1.3.1")
'com.github.nordstrom.tools:java-utils:jar:1.3.1'
<dependency org="com.github.nordstrom.tools" name="java-utils" rev="1.3.1">
  <artifact name="java-utils" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.nordstrom.tools', module='java-utils', version='1.3.1')
)
libraryDependencies += "com.github.nordstrom.tools" % "java-utils" % "1.3.1"
[com.github.nordstrom.tools/java-utils "1.3.1"]

Dependencies

test (1)

Group / Artifact Type Version
org.testng : testng jar 6.11

Project Modules

There are no modules declared in this project.

Maven Central

NORDSTROM JAVA UTILS

Nordstrom Java Utils is a small collection of general-purpose utility classes with wide applicability.

UncheckedThrow

The UncheckedThrow class uses type erasure to enable client code to throw checked exceptions as unchecked. This allows methods to throw checked exceptions without requiring clients to handle or declare them. It should be used judiciously, as this exempts client code from handling or declaring exceptions created by their own actions. The target use case for this facility is to throw exceptions that were serialized in responses from a remote system. Although the compiler won't require clients of methods using this technique to handle or declare the suppressed exception, the JavaDoc for such methods should include a @throws declaration for implementers who might want to handle or declare it voluntarily.

    ...
    
    String value;
    try {
        value = URLDecoder.decode(keyVal[1], "UTF-8");
    } catch (UnsupportedEncodingException e) {
        throw UncheckedThrow.throwUnchecked(e);
    }
    
    ...

DatabaseUtils

DatabaseUtils provides facilities that enable you to define collections of database queries and stored procedures in an easy-to-execute format.

Query collections are defined as Java enumerations that implement the QueryAPI interface:

  • getQueryStr - Get the query string for this constant. This is the actual query that's sent to the database.
  • getArgNames - Get the names of the arguments for this query. This provides diagnostic information if the incorrect number of arguments is specified by the client.
  • getConnection - Get the connection string associated with this query. This eliminates the need for the client to provide this information.
  • getEnum - Get the enumeration to which this query belongs. This enables executeQuery(Class, QueryAPI, Object[]) to retrieve the name of the query's enumerated constant for diagnostic messages.

Store procedure collections are defined as Java enumerations that implement the SProcAPI interface:

  • getSignature - Get the signature for this stored procedure object. This defines the name of the stored procedure and the modes of its arguments. If the stored procedure accepts varargs, this will also be indicated.
  • getArgTypes - Get the argument types for this stored procedure object.
  • getConnection - Get the connection string associated with this stored procedure. This eliminates the need for the client to provide this information.
  • getEnum - Get the enumeration to which this stored procedure belongs. This enables executeStoredProcedure(Class, SProcAPI, Object[]) to retrieve the name of the stored procedured's enumerated constant for diagnostic messages.

To maximize usability and configurability, we recommend the following implementation strategy:

  • Define your collection as an enumeration:

    • Query collections implement QueryAPI.
    • Stored procedure collections implement SProcAPI.
  • Define each constant:

    • (query) Specify a property name and a name for each argument (if any).
    • (sproc) Declare the signature and the type for each argument (if any).
  • To assist users of your queries, preface their names with a type indicator (GET or UPDATE).

  • Back query collections with configurations that implement the Settings API:

    • groupId: com.nordstrom.test-automation.tools
    • artifactId: settings
    • className: com.nordstrom.automation.settings.SettingsCore
  • To support execution on multiple endpoints, implement QueryAPI.getConnection() or SProcAPI.getConnection() with sub-configurations or other dynamic data sources (e.g. - web service).

Query Collection Example

public class OpctConfig extends SettingsCore<OpctConfig.OpctValues> {

    private static final String SETTINGS_FILE = "OpctConfig.properties";

    private OpctConfig() throws ConfigurationException, IOException {
        super(OpctValues.class);
    }

    public enum OpctValues implements SettingsCore.SettingsAPI, QueryAPI {
        /** args: [  ] */
        GET_RULE_HEAD_DETAILS("opct.query.getRuleHeadDetails"),
        /** args: [ name, zone_id, priority, rule_type ] */
        GET_RULE_COUNT("opct.query.getRuleCount", "name", "zone_id", "priority", "rule_type"),
        /** args: [ role_id, user_id ] */
        UPDATE_USER_ROLE("opct.query.updateRsmUserRole", "role_id", "user_id"),
        /** MST connection string */
        MST_CONNECT("opct.connect.mst"),
        /** RMS connection string */
        RMS_CONNECT("opct.connect.rms");

        private String key;
        private String[] args;
        private String query;

        private static OpctConfig config;
        private static String mstConnect;
        private static String rmsConnect;

        private static EnumSet<OpctValues> rmsQueries = EnumSet.of(UPDATE_USER_ROLE);

        static {
            try {
                config = new OpctConfig();
            } catch (ConfigurationException | IOException e) {
                throw new RuntimeException("Unable to instantiate OPCT configuration object", e);
            }
        }

        OpctValues(String key, String... args) {
            this.key = key;
            this.args = args;
        }

        @Override
        public String key() {
            return key;
        }

        @Override
        public String getQueryStr() {
            if (query == null) {
                query = config.getString(key);
            }
            return query;
        }

        @Override
        public String[] getArgNames() {
            return args;
        }

        @Override
        public String getConnection() {
            if (rmsQueries.contains(this)) {
                return getRmsConnect();
            } else {
                return getMstConnect();
            }
        }

        @Override
        public Enum<OpctValues> getEnum() {
            return this;
        }

        /**
         * Get MST connection string.
         * 
         * @return MST connection string
         */
        public static String getMstConnect() {
            if (mstConnect == null) {
                mstConnect = config.getString(OpctValues.MST_CONNECT.key());
            }
            return mstConnect;
        }

        /**
         * Get RMS connection string.
         * 
         * @return RMS connection string
         */
        public static String getRmsConnect() {
            if (rmsConnect == null) {
                rmsConnect = config.getString(OpctValues.RMS_CONNECT.key());
            }
            return rmsConnect;
        }
    }

    @Override
    public String getSettingsPath() {
        return SETTINGS_FILE;
    }

    /**
     * Get OPCT configuration object.
     *
     * @return OPCT configuration object
     */
    public static OpctConfig getConfig() {
        return OpctValues.config;
    }

    public enum SProcValues implements SProcAPI {
        /** args: [  ] */
        SHOW_SUPPLIERS("SHOW_SUPPLIERS()"),
        /** args: [ coffee_name, supplier_name ] */
        GET_SUPPLIER_OF_COFFEE("GET_SUPPLIER_OF_COFFEE(>, <)", Types.VARCHAR, Types.VARCHAR),
        /** args: [ coffee_name, max_percent, new_price ] */
        RAISE_PRICE("RAISE_PRICE(>, >, =)", Types.VARCHAR, Types.REAL, Types.NUMERIC),
        /** args: [ str, val... ] */
        IN_VARARGS("IN_VARARGS(<, >:)", Types.VARCHAR, Types.INTEGER),
        /** args: [ val, str... ] */
        OUT_VARARGS("OUT_VARARGS(>, <:)", Types.INTEGER, Types.VARCHAR);

        private int[] argTypes;
        private String signature;

        SProcValues(String signature, int... argTypes) {
            this.signature = signature;
            this.argTypes = argTypes;
        }

        @Override
        public String getSignature() {
            return signature;
        }

        @Override
        public int[] getArgTypes () {
            return argTypes;
        }

        @Override
        public String getConnection() {
            return OpctValues.getRmsConnect();
        }

        @Override
        public Enum<SProcValues> getEnum() {
            return this;
        }
    }
}

Registering JDBC Drivers

To provide maximum flexibility, JDBC interacts with database instances through a defined interface (java.sql.Driver). Implementations of this interface translate its methods into their vendor-specific protocol, in classes called drivers. For example, OracleDriver enables JDBC to interact with Oracle database products.

In JDBC connection URLs, the vendor and driver are specified as suffixes to the jdbc protocol. For the Oracle "thin" driver, this is jdbc:oracle:thin. This protocol/vendor/driver combination is handled by OracleDriver, and JDBC needs this class to be registered to handle this vendor-specific protocol.

To simplify the process of registering vendor-specific JDBC drivers, DatabaseUtils loads these for you through the Java ServiceLoader facility. Declare the driver(s) you need in a ServiceLoader provider configuration file at META-INF/services/java.sql.Driver:

oracle.jdbc.OracleDriver

This sample provider configuration file will cause DatabaseUtils to load the JDBC driver class for Oracle database products. The JAR that declares this class needs to be on the class path for this to work. For Maven projects, you just need to add the correct dependency:

[pom.xml]
<project ...>
  [...]
  
  <dependencies>
    [...]
    <dependency>
      <groupId>com.oracle.jdbc</groupId>
      <artifactId>ojdbc6</artifactId>
      <version>11.2.0.4.0</version>
    </dependency>
  </dependencies>
  
  [...]
</project>

PathUtils

The PathUtils getNextPath method provides a method to acquire the next file path in sequence for the specified base name and extension in the indicated target folder. If the target folder already contains at least one file that matches the specified base name and extension, the algorithm used to select the next path will always return a path whose index is one more than the highest index that currently exists. (If a single file with no index is found, its implied index is 0.)

Example usage of getNextPath
    ...
    
    /*
     * This example gets the next path in sequence for base name `artifact`
     * and extension `txt` in the TestNG output directory.
     * 
     * For purposes of this example, the output directory already contains
     * the following files: `artifact.txt`, `artifact-3.txt`
     */

    Path collectionPath = Paths.get(testContext.getOutputDirectory());
    // => C:\git\my-project\test-output\Default suite

    Path artifactPath;
    try {
        artifactPath = PathUtils.getNextPath(collectionPath, "artifact", "txt");
        // => C:\git\my-project\test-output\Default suite\artifact-4.txt
    } catch (IOException e) {
        provider.getLogger().info("Unable to get output path; no artifact was captured", e);
        return;
    }
    
    ...

Params Interface

The Params interface defines concise methods for the creation of named parameters and parameter maps. This facility can make your code much easier to read and maintain. The following example, which is extracted from the Params unit test class, demonstrates a few basic features.

Params Example

package com.nordstrom.example;

import static com.nordstrom.common.params.Params.param;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

import java.util.Optional;
import org.testng.annotations.Test;
import com.nordstrom.common.params.Params;

public class ParamTest implements Params {
    
    @Test
    public void testDefault() {
        assertFalse(Params.super.getParameters().isPresent());
    }
    
    @Test
    public void testParam() {
        Param param = param("boolean", true);
        assertEquals(param.getKey(), "boolean");
        verifyBoolean(param.getVal());
    }
    
    @Test
    public void testParams() {
        Optional<Map<String, Object>> optParameters = getParameters();
        assertTrue(optParameters.isPresent());
        Map<String, Object> parameters = optParameters.get();
        assertFalse(parameters.isEmpty());
        
        assertTrue(parameters.containsKey("boolean"));
        verifyBoolean(parameters.get("boolean"));
    }
    
    private void verifyBoolean(Object value) {
        assertTrue(value instanceof Boolean);
        assertTrue((Boolean) value);
    }
    
    @Override
    public Optional<Map<String, Object>> getParameters() {
        return Params.mapOf(param("boolean", true), param("int", 1), param("String", "one"),
                        param("Map", Params.mapOf(param("key", "value"))));
    }
}

This code uses a static import to eliminate redundant references to the Params interface. It also shows the unrestricted data types of parameter values. The use of Optional objects enables you to provide an indication that no value was returned without the risks associated with null.

com.github.nordstrom.tools

Nordstrom, Inc.

Nordstrom, Inc. is a specialty fashion retailer, headquartered in Seattle, Washington

Versions

Version
1.3.1