Access the Angular-CLI Internal Web Server From Another Computer

Posted 10/29/2018 8:19 PM by Corey Klass

By default, the Angular-CLI internal web server only listens on the localhost network interface (127.0.0.1). If you want to access your development web application from another computer, you can start the internal web server with the following command:

ng serve --host 0.0.0.0 --disableHostCheck true

Groovy Deep Copy Objects

Posted 10/26/2018 2:33 PM by Corey Klass

There is no built-in functionality to perform a deep-copy of a Groovy object. Below is a convenience class that will perform a deep copy of an object.

import org.grails.web.json.JSONArray
import org.grails.web.json.JSONObject

class ObjectUtility {



    /**
     * Duplicates an object
     * @param object
     * @return
     */
    static Object duplicate(Object object) {
        def duplicateObject = null

        if (object instanceof JSONObject) {
            duplicateObject = ObjectUtility.duplicateJSONObject(object)

        } else if (object instanceof JSONArray) {
            duplicateObject = ObjectUtility.duplicateJSONArray(object)

        } else if (object instanceof Map) {
            duplicateObject = ObjectUtility.duplicateMap(object as Map<Object, Object>)

        } else if (object instanceof List) {
            duplicateObject = ObjectUtility.duplicateList(object as List<Object>)

        } else if (object instanceof Collection) {
            duplicateObject = ObjectUtility.duplicateCollection(object as Collection<Object>)

        } else if (object instanceof Set) {
            duplicateObject = ObjectUtility.duplicateSet(object as Set<Object>)

        } else {
            duplicateObject = object.getClass().newInstance(object)
        }

        return duplicateObject
    }



    /**
     * Duplicates a Map
     * @param map
     */
    private static Map<Object, Object> duplicateMap(Map<Object, Object> map) {
        def duplicateMap = (map.getClass().newInstance() as Map)

        map.each { Object key, Object value ->
            duplicateMap[key] = ObjectUtility.duplicate(value)
        }

        return duplicateMap
    }




    /**
     * Duplicates a JSONObject
     * @param map
     */
    private static JSONObject duplicateJSONObject(JSONObject jsonObject) {
        def duplicateObject = (jsonObject.getClass().newInstance() as JSONObject)

        jsonObject.each { Object key, Object value ->
            duplicateObject[key] = ObjectUtility.duplicate(value)
        }

        return duplicateObject
    }



    /**
     * Duplicates a List
     * @param list
     * @return
     */
    private static List<Object> duplicateList(List<Object> list) {
        def duplicateList = (list.getClass().newInstance() as List)

        list.each { Object indexItem ->
            duplicateList.push(ObjectUtility.duplicate(indexItem))
        }

        return duplicateList
    }




    /**
     * Duplicates a JSONArray
     * @param list
     * @return
     */
    private static JSONArray duplicateJSONArray(JSONArray array) {
        def duplicateArray = (array.getClass().newInstance() as JSONArray)

        array.each {Object indexItem ->
            duplicateArray.push(ObjectUtility.duplicate(indexItem))
        }

        return duplicateArray
    }




    /**
     * Duplicates a Collection
     * @param collection
     * @return
     */
    private static Collection<Object> duplicateCollection(Collection<Object> collection) {
        def duplicateCollection = (collection.getClass().newInstance() as Collection)

        collection.each { Object indexItem ->
            duplicateCollection.add(ObjectUtility.duplicate(indexItem))
        }

        return duplicateCollection
    }



    /**
     * Duplicates a Set
     * @param set
     * @return
     */
    private static Set<Object> duplicateSet(Set<Object> set) {
        def duplicateSet = (set.getClass().newInstance() as Set)

        set.each { Object indexItem ->
            duplicateSet.add(ObjectUtility.duplicate(set))
        }

        return duplicateSet
    }



}

Run the Angular-CLI Internal Web Server on an Alternate Port

Posted 10/20/2018 10:24 AM by Corey Klass

The Angular-CLI internal web server runs on port 4200 by default. To change the port number, launch it with the following command:

ng serve --port 8888

Path-less SASS Imports in your Angular App

Posted 10/14/2018 3:34 PM by Corey Klass

One of the nice features of SASS in your Angular-CLI app is that you can include an @import "variables"; command to include the SASS variable definitions from an external file. You can easily make your SASS import file globally available for @import statements, so that you only refer to it by filename, by modifying your /angular.json file in the following hierarchy: Root -> "projects" -> "my-project-name" -> "architect" -> "build" -> "options"

Under "options", create a new map entry called "stylePreprocessorOptions" if it does not exist. Within that, create an array entry called "includePaths". Within "includePaths", you can specify the relative path to your SASS file from the root of your project. For example, it may be located at "src/assets/sass".

Global CSS/SASS Includes in Angular-CLI

Posted 10/09/2018 1:34 PM by Corey Klass

You can globally include additional CSS/SASS style sheets into your Angular-CLI application by modifying your /angular.json file in the following hierarchy: Root -> "projects" -> "my-project-name" -> "architect" -> "build" -> "options"

Under "options", create a new array entry called "styles" if it does not exist. Within that, you can specify the relative path to your CSS/SASS file from the root of your project. For example, it may be located at "src/assets/sass".

NPM Module / Angular Compilation Errors

Posted 10/02/2018 1:12 PM by Corey Klass

Barrels

It's considered good practice to separate Angular functionality into NPM modules for ease of re-use. The easiest way of importing modules is to import from the root of the module, and you accomplish this by creating a barrel.

In each of your module folders, create an index.ts file that looks like the following:

export * from './file1';
export * from './file2';
export * from './file3';

Then in the root of your module, add export statements for each of the index.ts files that you created above.

export * from './folder1/index';
export * from './folder2/index';

You are then able to import any file definition in your Angular application with a basic module reference:

import {File1} from '@coreyklass/module1';

Errors

Barrels are very useful, but they can result in compilation errors when building your Angular application for production.

The most common of these will be:

ERROR in : Unexpected value 'undefined' exported by the module 'Module1'

Solution

When building your module definition TS file, ensure that all of the import statements reference the actual file that the code is contained in, not the barrel file that you created above. You may not have noticed, but your IDE may have used the barrel for the automatic import statement.

Instead of this in your module definition file: import {File1} from './folder1';

Use this: import {File1} from './folder1/file1';

This should solve your compilation problem.

Grails / CORS / Spring Security Plugin

Posted 04/11/2018 7:05 AM by Corey Klass

It used to be that when using the Spring Security Plugin with Grails, you needed to include in your build.gradle file a separate plugin for handling CORS requests. In more recent versions of Grails, the functionality is built-in, so you only need to add a configuration item in the application.yml file.

In the first grails section, you need to add the following setting:

grails:
    profile: web
    ... (Other Stuff Here)
    cors:
        enabled: true

Set a Timeout on a Block of Code

Posted 04/09/2018 5:51 PM by Corey Klass

Sometimes you have a segment of code that you want to kill if it runs any longer than a specified time period, for example a database query, external API call, or other type of long-running request.

To do this, you want to wrap the block of code to time out in a Callable, and use the ExecutorService to execute the code with a time limit. It will return an array of Future objects, which you can then reference with the .get() method to retrieve the Callable return value.

In your build.gradle file, you will need to include the Grails Executor plug-in if you want to have any GORM/Hibernate functionality in your Callable statement.

    compile "org.grails.plugins:grails-executor:0.4"

You can then use the following code to put a timeout on your executing code. When the timeout is reached, the thread that was spawned is terminated.

    class CallableTestService {

        ExecutorService executorService

        Object performAction() {
            def callable = ({
                // do some logic here
                def resultValue = true

                // return the value
                return resultValue
            } as Callable<Object>)

            def callables = [callable]
            def result = null

            try {
                def timeLimitMS = 1000

                def futures = this.executorService.invokeAll(callables, timeLimitMS, TimeUnit.MILLISECONDS)
                def future = futures?.first()

                result = future.get()

            } catch (error) {
              // error handling goes here
            }


            // do something with the result
            return result
        }
    }

Install Clustered SQL Server 2008 R2 on Windows Server 2012 R2

Posted 03/22/2018 11:49 AM by Corey Klass

It turns out that installing a SQL Server 2008 R2 cluster on Windows Server 2012 R2 is a little more involved than point-and-click.

Install Windows Failover Cluster Support Feature

The SQL Server installer check throws an error that there are no shared disks available:

The cluster on this computer does not have a shared disk available. To continue, at least one shared disk must be available.

This is because the SQL Server 2008 R2 installer uses Failover Cluster features that have been deprecated from Windows Server 2012 R2, and which are not installed by default.

Open a Powershell prompt and run:

Get-WindowsFeature RSAT-Cluster*

You should see that the Failover Cluster Automation Server is not installed, but rather has an Install State of Available. To install it, you run the command:

Install-WindowsFeature -Name RSAT-Clustering-AutomationServer

Stage the Cluster Name in Active Directory

The Active Directory computer account that the Windows Cluster (NOT the SQL cluster) run as does not, by default, have access to create the SQL Cluster computer object in Active Directory. You should create the computer object before-hand and assign it appropriate permissions. If not, after installation when you attempt to start the SQL Server instance, you will receive a message that the Network Name is unable to be brought online.

  1. Create a Computer object with the name of the SQL cluster that you are about to install.
  2. Disable the computer object that you just created.
  3. Right-click on it, select Properties.
  4. Click the Security tab.
  5. Click Add.
  6. Change the Object Type to search to include Computers.
  7. Search for the name of the Windows Cluster. Click OK to confirm that you want to add it.
  8. Change the security for the Windows Cluster to have Full control.
  9. Click OK to save the Security changes.

Update Installer Files to Service Pack 1

The SQL Server installer will fail at the very end of the installation configuration process, right before it starts the installation. It complains about FILESTREAM access (regardless of whether you have set it up or not), that:

Windows 2003 hotfix KB937444 is not installed

This is because you need at least Service Pack 1 for SQL 2008 R2 to install it correctly. But how can you install Service Pack 1 if you can't even install the base SQL Server? The answer is: Slipstream Service Pack 1 into the installer

  1. Extract the contents of the regular SQL Server installer to the folder: C:\Installer_SQL2008R2_SP1\

  2. You'll need to download Service Pack 1 from Microsoft. Be sure to download all of the architecture packages.

    SQLServer2008R2SP1-KB2528583-IA64-ENU.exe

    SQLServer2008R2SP1-KB2528583-x64-ENU.exe

    SQLServer2008R2SP1-KB2528583-x86-ENU.exe

  3. Extract each of the packages to a temporary folder:

    SQLServer2008R2SP1-KB2528583-IA64-ENU.exe /x:C:\Installer_SQL2008R2_SP1\SP

    SQLServer2008R2SP1-KB2528583-x64-ENU.exe /x:C:\Installer_SQL2008R2_SP1\SP

    SQLServer2008R2SP1-KB2528583-x86-ENU.exe /x:C:\Installer_SQL2008R2_SP1\SP

  4. Copy only the files (not the folders), except the Microsoft.SQL.Chainer.PackageData.dll, in C:\InstallerSQL2008R2SP1\SP\ to C:\InstallerSQL2008R2SP1\ to update the original files:

    robocopy C:\Installer_SQL2008R2_SP1\SP\x86 C:\Installer_SQL2008R2_SP1\x86 /XF Microsoft.SQL.Chainer.PackageData.dll

    robocopy C:\Installer_SQL2008R2_SP1\SP\x64 C:\Installer_SQL2008R2_SP1\x64 /XF Microsoft.SQL.Chainer.PackageData.dll

    robocopy C:\Installer_SQL2008R2_SP1\SP\ia64 C:\Installer_SQL2008R2_SP1\ia64 /XF Microsoft.SQL.Chainer.PackageData.dll

  5. In each of the following locations, locate the DefaultSetup.INI file:

    C:\Installer_SQL2008R2_SP1\x86

    C:\Installer_SQL2008R2_SP1\x64

    C:\Installer_SQL2008R2_SP1\ia64

  6. Add the following line to the end of the file in each location:

    PCUSOURCE=".\SP"

  7. Run setup.exe in C:\SQLServer2008R2_SP1 and the installation should succeed.

SQL Server Cluster Shared Drive - Unable to Add Disk Storage

Posted 03/22/2018 8:08 AM by Corey Klass

I was recently building a SQL Server failover cluster and had an issue where I had two SAN-connected drives to be connected to two separate nodes, but only one drive would appear as available in the Windows Failover Cluster Manager to add to the Disk Storage.

The solution turns out to be a series of Powershell commands.

First, find the ID number of the disk that you're interested in:

Get-Disk

Then you clear the cluster reservation for that disk number:

Clear-ClusterDiskReservation -Disk 2

Go back into the Failover Cluster Manager, and the disk should be available to add to the cluster.