Search

Entry Inspector 2.5 Released!

Hi Everyone,

Version 2.5 of the Entry Inspector has been released.  This is a huge update over the last published version as I have been working on updates / fixes on and off for over a year with some key beta testers.  I am happy with this version finally and decided to release it to a greater audience.

You can grab this version here:  

As always, if you are interested in a full version that removes the 20 record limit on actions, please click on the buy link below or email me at support@soleauthority.net to arrange alternate payment.

Click below to Purchase

(Will re-directed to a PayPal storefront):


Upon check-out, you will receive your key via email in 1-2 business days. If you have any questions at all pre/post purchase you can reach me at support@soleauthority.net

Cheers,

Published updated version of AIFGenerator tool

A few people had been reporting issues with the AIFGenerator tool that allows you to quickly clone an existing SRM AIF (based on template or your own custom AIF).  I’ve refreshed the utilities dependencies, upped the JRE requirements to 1.8 to take advantage of some newer Java language features and fixed up a few corner case issues that could have been the cause of some of the issues reported.

If you have tried the utility in the past and it hasn’t worked for you, assuming you still have a need for quick AIFs, give this new version a try and let me know if there are any issues:

AIFGenerator

Entry Inspector 2.1 Released!

Hi Everyone,

Version 2.1 of the Entry Inspector has been released.  This is mostly a stability release rather then full of enhancements but there are a few nice ones that made the cut.

Here is a summary of the changes introduced in this version:

  • Added ability to do Create and Service actions against Display-Only Forms
  • Changed the field selection drop-downs to support auto-completion
  • Managing columns pop-up will allow for multiple actions now
  • Replaced existing login managers backend with an embedded database

That last item is an important first step towards other capabilities I’ll be including in future versions as this new settings database will eventually allow for persisting additional types of settings like saved search queries, commonly used SQL queries, results list column defaults per form, etc.

Because of this change to the new login backend, if you had previously installed Entry Inspector v2.0, your existing profiles.xml file will be automatically removed and replaced with a settings.mv.db.  You will need to re-add all your profiles manually though.

You can grab this version here:  

As always, if you are interested in a full version that removes the 20 record limit on actions, please click on the buy link below or email me at support@soleauthority.net to arrange alternate payment.

 

Click below to Purchase

(Will re-directed to a PayPal storefront):




Upon check-out, you will receive your key via email in 1-2 business days. If you have any questions at all pre/post purchase you can reach me at support@soleauthority.net

Cheers,

Login Converter – Utility for mass updating Login IDs

Updating a Remedy system when a Login ID has to changed can be a long and slow process if you want to keep the integrity of the data after changing the Login ID.  BMC provides the Data Wizard tool to do this however it has many limitations such as:

  • Only being able to update one LoginID at a time
  • Requires customization to target custom forms/fields
  • Is generally slow and doesn’t search within all fields (only target fields important to ITSM workings)

There are also other third party tools developed for the same goal such as the RRR|LoginConv (https://rrr.se/cgi/tools/main?tool=rrrLoginConv) build which utilize arsPerl and since arsPerl has fallen a bit behind from a support perspective, an more up-to-date alternative is something that has been asked for on the ARSList.

Building upon some previous work I’ve done, I’ve put together a utility based upon the Java API that will perform the following tasks:

  • High performance updates from an old LoginID to a new LoginID utilizing merge action that bypasses workflow
    • Updates all Character Fields (regardless of size)
      • Match exact
      • (optional) Match surrounded by single/double quotes
      • (optional) Match surrounded by separators such as white-space, comma, period, pipe, semi-colon or colon
    • (optional) Updates all Status History entries
    • (optional) Updates all Diary Field entries

Here is a peak at what it looks like running through a multiple Login ID update:


LoginConverter_Small

Some key features of this utility include:

  • Ability to load in list of conversions from file when doing multiple updates per run
  • Pre-loaded list of all data forms from your connected AR System into the Specific Form field (with auto-fill capabilities)
  • Ability to specify a list of specific forms to update or all forms
  • Ability to specify a semi-colon delimited list of specific fields to update or all matching character/diary/status history fields
  • Two progress bars show displaying progress by the currently processing form and the overall end to end process.
  • Ability to control the speed by way of a delay slider that will slow down or speed up the processing dynamically
  • Ability to pause / resume the processing
  • Ability to cancel in-flight updates (doesn’t not roll back, just stops wherever it happens to be)
  • Ability to toggle on/off the scope/matching capabilities
  • Ability to specify all forms to be checked or a specific form only
  • Ability to ‘simulate’ the run without actually make any changes
  • Detailed output provided on forms processed and matches found

Recommend you test this in a non-production environment and gauge the results prior to running this against a production system.  I also recommend using JRE 1.7 (64-bit) while directly on the AR System server you are connecting to to execute this program for optimum performance.  It will run just fine from any host that can connect to the AR System via API but due to the type of work being done, running this utility on the server itself will increase the throughput considerably and take the network layer out of the equation.

NOTICE:  If you have a very large system (10s or 100s of millions of rows), the initial time to get the entry totals may take awhile before you see the program start to process forms.

NOTICE:  If you begin to see too much performance degradation on your AR System and want to lessen the impact, simply move up the slider bar to introduce delay between processing which will elongate the processing but reduce the load on the system greatly.  You can even pause it during prime business hours to resume it later when there are less users on the system.

If any issues arise, please let me know and I will make adjustments.   You can download the program below

Cheers,

Version 1.1 Released – October 28

  • Added ability to pre-load list of Login IDs to update via file import

Version 1.2 Released – October 28

  • Fixed some visual anomalies
  • Added more detailed output to found matches, you’ll now see all the fields that matched and will be updated

Version 2.0 Released – October 28

  • Reworked the UI to be more efficient and consistent
  • Changed specific form field into menu with pre-loaded remedy forms with auto-fill support
  • Improved how detailed output is generated and provided more consistent information on how many updates per form and what field specifically are update

Version 2.1 Released – October 30

  • Added ability to specify multiple specific forms rather then just one or all
  • Added ability to specify multiple field IDs in a semi-colon (;) delimited list instead of always running against all character/diary/status history fields
  • Added ability to show/collapse Options section while job is running in order to give more real-estate to the detailed output window when needed.
  • Added additional error checking for bad input from user in various sections
  • Small re-factoring and UI fixes

Version 2.2 Released – Nov 18

  • Switched to H2 database for login profiles and lay groundwork for eventual capability of saving configuration profiles for repeatable updates
  • Don’t stop when a form throws an error in the pre-processing phase

Version 2.5

  • Returned to SQLite DB
  • Added ability to specify forms via import file
  • Added debugging capabilities with the -debug option flag
  • Updated User Interface and Libraries

Version 2.6 (yanked – do not use)

  • Faster processing
  • Attempt to fix a bug with some versions of AR System / Customer environment terminating after the first match

Version 2.7

  • Reduced number of API calls to the system considerably for better speed-ups compared to v2.6
  • FIx some outputting errors / ommissions in the display panel
  • Fix corner case where status history was not being updated correctly

 

 

Find String in Fields – Utility

I was recently tasked with how to find all occurrence of a specific string that could exist in any field in any form on a Remedy system.  This poses an interesting challenge and my first gut check for this type of request would be to go-to the database so after some searching, I had found the following which gave me most of the information I was looking for:

http://stackoverflow.com/questions/9185871/how-to-search-sql-server-database-for-string

The above would provide output that would list the T tables and C columns where a match was found which got me part of the way there, would still need to then go search on that form/field to find the entries in question.

So after see this in action, I decided to see what the difference in performance would be to do something similar but using only the Remedy API and came up with the attached program.  It actually ran in about the same amount of time and I can get a bit more detail then the SQL proc gave me.

You can download the program at the bottom of this post and run the program like this:

java -jar FindStringInFields.jar -x <servername> -u <username> -p <password> -t <port> -s <string to search for in doublequotes> -o <name of output file, defaults to output.csv if not set here>

While the program is running, you will be kept up to date on the progress via a progress bar and when completed will tell you how many matches have been found.  After completion, the results will be provided in a CSV style file with formName, fieldName, fieldID and entryID for all found occurrences.

Here is a screenshot of it in action where I search my own local system for server name references:

FindStringInField

Hope this is useful.

Remedy Image Browser – Simple tool for browsing Image Objects

Inspired by an Idea on the BMC Communities by Jason Miller (https://communities.bmc.com/ideas/7609), I put together a very simple Java app that will allow you to log into a target AR System, grab all the Image objects and display them (with image preview in-line) in a table.  Upon selecting an image, it is rendered in full scale to the right of the table.

Here is a screenshot of it in action:

RemedyImageBrowser_small

For those interested, you can download it below

Release of my first commercial tool… Entry Inspector

I have published a new utility that I hope many will find useful.  It started as a standalone application based upon my Entry Inspector Java API Plugin tutorial and  then started quickly evolving into what it is today.  This utility will give you a lot of control over Remedy data where sometimes the Mid-Tier can be frustrating to work with.

If you are interested, please click the link below and be taken to the project page where you can download the evaluation version and also purchase the full version for a small fee.  I’ve also put in a walk through of the tool with screenshots there as well.

 

Entry Inspector – View, Modify, Export & Delete Data Quickly

Quickly remove __c from fields in custom forms

In my personal opinion, I see no value in the __o and __c suffix conventions that overlay/best practice mode have brought to the AR System as it relates to fully custom forms. There are many other areas within Developer Studio and within the Objects themselves at an API level that allow us to know if we are working on unmodified, overlaid or custom objects so making it visible to the administrators by way of the database name is annoying. While __c is important when adding custom fields to forms ‘owned’ by BMC (to avoid possible name collisions in future versions), it doesn’t serve much value on custom forms which is the point behind this utility.

There is an active BMC Communities idea for making this a toggle in dev studio (or possibly out-right removing it) here, please vote if you feel the same way as me:

https://communities.bmc.com/ideas/6043

Now in order to work around this for now in situations where you have many fields you want to strip the __c from (like the example in the idea above with large custom join forms), here is a sample Java API method that will do this for you, simply pass it in the name of the form and it will do it’s magic.

private static void cleanDBNameForFields(String targetForm) {
    // Allow us to work on Custom & Overlay Objects vs Base Objects
    ctx.setBaseOverlayFlag(false);
    ctx.setOverlayFlag(true);
    ctx.setOverlayGroup(String.valueOf(Constants.AR_GROUP_ID_ADMINISTRATOR));
    ctx.setDesignOverlayGroup(String.valueOf(Constants.AR_GROUP_ID_ADMINISTRATOR));
    List<Field> fieldsToClean = new ArrayList<Field>();
    try {
        // Get list of all the fields on the targetForm
        List<Fields> fields = ctx.getListFieldObjects(targetForm);
        for (Field field : fields) {
            String fieldName = field.getName();
            // Find field that have __c
            if (fieldName.endsWith("__c")) {
                // Chop off last 3 characters to get rid of __c and then add it to our list of Fields to update
                field.setNewName(fieldName.substring(0, fieldName.length() - 3));
                fieldsToClean.add(field);
            }
        }
        // Update all the fields in the same transaction
        ctx.setMultipleFields(fieldsToClean);
        System.out.println("   " + fieldsToClean.size() + " fields have been cleaned successfully on " + targetForm);
    } catch (ARException e) {
        System.out.println(e.getMessage());
    }
}

The code is simple enough, we simply get a list of all the fields on the target form, find the ones that have __c at the end, strip it out and then add the updated Field object to a new list of Fields. Once we have our list of fields, we simply feed it to setMultipleFields() so that it commits them all in a single transaction like Developer Studio does when you make changes to forms. This method is very fast, on my system, a join form with ~720 fields that contained __c took only 1.5 seconds to ‘clean’.

I’ve attached below a full program that can be run from the command line and it’s source that makes use of the example above, just feed it your connection info on the command line as well as the target form and it will give you back hours in your day 🙂

Here is a sample run with results shown:

CleanFieldDBNames

CleanFieldDBNames

Icon

CleanFieldDBNames - Source 5.96 KB 360 downloads

...
Icon

CleanFieldDBNames - Utility 7.34 MB 406 downloads

...

    Service Request Purging – Easily delete Service Requests

    A colleague of mine recently asked if I knew a good way to delete a subset of Service Requests (based around BMC Service Request Management module) so that they could go live without any test service requests that had accumulated over the past few weeks leading up to the upcoming go-live.  Normally, cleaning out test transactional data like Incidents, Changes, etc. now days is pretty easy as in recent versions BMC had added the correct OnDelete workflow that if you deleted the parent entry (e.g. HPD:Help Desk entry), it would delete all the related child data.  I was surprised to find that SRM did not follow this behavior and actually had workflow explicitly stopping you from doing so which was a bit of a pain.

    After a bit of search it was clear that this has been like this for SRM from the start and others have run into similar situations, wanting to basically start from scratch with Service requests or at minimum be able to delete a subset of the service requests that may have been created for testing purposes requires a bit of elbow greese.  Here are some of the BMC Community links that talk about this:

    https://communities.bmc.com/message/267841

    https://communities.bmc.com/message/269781

    https://communities.bmc.com/message/225416

    In one of those posts, somebody actually went through the trouble of identifying all the related records that get created when you submit a service request and even went as far as detailing out their relationship to the parent SRM:Request entry which is the basis for this post.

    Based on the information in those posts, I’ve put together a little utility that automates the deletion of either all or a subset of service requests along with their related data.  I’ve also added in version 2.0 the ability to specify related transactional data such as incidents, work orders, changes, etc as well as AIFs related to the service requests being deleted.

    Here is a screenshot of it in action deleting the single cancelled status service request I had in my own system:

    ServiceRequestPurge

    You’ll notice it is a command line tool with a simple parameters list that basically just needs the info to connect to your AR Server as well as an optional -q parameter for specifying a query against the SRM:Request form for optionally specifying which Service Requests to delete.  If you don’t include the -q it will simply delete all service requests.  The thing to remember about the -q is in order to pass it in as a parameter on the command line, you need to escape things like double-quotes like I do in the example screenshot above.  For a listing of all the parameters you can just pass in -h to receive help info.

    Because there really isn’t anything secretive about this and that the target forms and their relationships were outlined already in the community posts, I’ve also posted the code behind this utility for anybody to leverage/extend to their liking (the posting is a couple of years old so in newer versions of SRM their may be additional forms, if anybody knows of other areas that should be cleaned, please comment below and I will add them).  You’ll notice that this will also take care of the Filter that normally stops you from doing a Delete against the SRM:Request form by overlaying it, disabling it, running the delete jobs and then when all done, deleting the overlay of that filter to restore it to OOTB.

    /**
     * Copyright (c) 2014 Curtis Gallant <cgallant@soleauthority.net>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    package net.soleauthority.arsys.utils;
    
    /**
     * ServiceRequestPurge - This program will allow you to delete BMC Service Request entries with all their supporting data
     */
    
    import com.bmc.arsys.api.ARException;
    import com.bmc.arsys.api.ARServerUser;
    import com.bmc.arsys.api.Constants;
    import com.bmc.arsys.api.Entry;
    import com.bmc.arsys.api.EntryListInfo;
    import com.bmc.arsys.api.Filter;
    import com.bmc.arsys.api.OverlaidInfo;
    import com.bmc.arsys.api.QualifierInfo;
    import com.bmc.arsys.api.Value;
    import org.apache.commons.cli.CommandLine;
    import org.apache.commons.cli.CommandLineParser;
    import org.apache.commons.cli.GnuParser;
    import org.apache.commons.cli.HelpFormatter;
    import org.apache.commons.cli.OptionBuilder;
    import org.apache.commons.cli.Options;
    import org.apache.commons.cli.ParseException;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class ServiceRequestPurge {
        private static final ARServerUser ctx = new ARServerUser();
        private static String serverName = null;
        private static String userName = null;
        private static String password = null;
        private static int port = 0;
        private static String query = null;
        private static boolean isTransactionalDelete = false;
        private static boolean isAIFDelete = false;
        private static List<String> childSchemas = new ArrayList<String>();
        private static String[] possibleChildSchemas = {"AP:Detail", "AP:Signature",
                "APR:Non-ApprovalNotifications", "AST:CMDB Associations",
                "CHG:Associations", "HPD:Associations", "NTE:Notifier Log",
                "PBM:Investigation Associations", "PBM:Known Error Associations",
                "PBM:Solution DB Associations", "SLM:EventSchedule", "SLM:Measurement",
                "SLM:MilestoneLogging", "SRM:AppInstanceBridge", "SRM:AppInstanceFlow",
                "SRM:Associations", "SRM:QuestionResponse", "SRM:SR_AuditLog",
                "SRM:Survey", "SRM:WorkInfo", "WOI:Associations", "HPD:Help Desk",
                "WOI:WorkOrder", "CHG:Infrastructure Change"};
    
    
        public static void main(String[] args) {
            System.out.println("\nService Request Purge v2.0 - Created by Curtis Gallant (cgallant@soleauthority.net)");
            parseArguments(args);
            ctx.setServer(serverName);
            ctx.setUser(userName);
            ctx.setPassword(password);
            ctx.setPort(port);
            try {
                ctx.login();
                ctx.setBaseOverlayFlag(false);
                ctx.setOverlayFlag(true);
                ctx.setOverlayGroup(String.valueOf(Constants.AR_GROUP_ID_ADMINISTRATOR));
                ctx.setDesignOverlayGroup(String.valueOf(Constants.AR_GROUP_ID_ADMINISTRATOR));
                toggleDeleteSafetyFilter("DISABLE");
                getListOfFormsToPurge();
                delServiceRequests(query);
                toggleDeleteSafetyFilter("ENABLE");
                ctx.logout();
            } catch (ARException e) {
                System.out.println("Can't login: " + e.getMessage());
            }
        }
    
        @SuppressWarnings("static-access")
        private static void parseArguments(String[] args) {
            // create the command line parser
            CommandLineParser parser = new GnuParser();
            HelpFormatter formatter = new HelpFormatter();
    
            Options options = new Options();
            options.addOption(OptionBuilder.withArgName("ServerName")
                    .hasArg()
                    .isRequired()
                    .withDescription("Please provide the AR System Server Name")
                    .create("x"));
            options.addOption(OptionBuilder.withArgName("UserName")
                    .hasArg()
                    .isRequired()
                    .withDescription("Enter the Remedy administrator user name")
                    .create("u"));
            options.addOption(OptionBuilder.withArgName("Password")
                    .hasArg()
                    .isRequired()
                    .withDescription("Enter the Remedy administrator password")
                    .create("p"));
            options.addOption(OptionBuilder.withArgName("Port")
                    .hasArg()
                    .withDescription("Enter the TCP Port for the Remedy Server")
                    .create("t"));
            options.addOption(OptionBuilder.withArgName("Query")
                    .hasArg()
                    .withDescription("Enter the query for Service Requests to be deleted, use DB field names only and " +
                            "remember to escape, e.g \"'7' = \\\"\"Cancelled\\\"\"")
                    .create("q"));
            options.addOption("h", false, "Print usage details");
            options.addOption("trans", false, "Delete directly created transactional records, e.g. Incidents, Work Orders, etc.");
            options.addOption("aif", false, "Delete AIFs associated with target Server Request records");
    
            try {
                CommandLine line = parser.parse(options, args);
                if (line.hasOption("h")) {
                    formatter.printHelp("ServiceRequestPurge", options, true);
                    System.exit(0);
                }
                if (line.hasOption("x")) {
                    serverName = line.getOptionValue("x");
                }
                if (line.hasOption("u")) {
                    userName = line.getOptionValue("u");
                }
                if (line.hasOption("p")) {
                    password = line.getOptionValue("p");
                }
                if (line.hasOption("t")) {
                    port = Integer.parseInt(line.getOptionValue("t"));
                }
                if (line.hasOption("q")) {
                    query = line.getOptionValue("q");
                }
                if (line.hasOption("trans")) {
                    isTransactionalDelete = true;
                }
                if (line.hasOption("aif")) {
                    isAIFDelete = true;
                }
            } catch (ParseException exp) {
                System.out.println(exp.getMessage());
                formatter.printHelp("ServiceRequestPurge", options, true);
                System.exit(1);
            }
        }
    
        private static void toggleDeleteSafetyFilter(String cmd) {
            String deleteFilterName = "SRM:SHR:Permission_001_DeleteError";
            try {
                Filter filter = ctx.getFilter(deleteFilterName);
                Value overlayProperty = filter.getProperties().get(Constants.AR_SMOPROP_OVERLAY_PROPERTY);
                if (cmd.equals("DISABLE")) {
                    if (overlayProperty == null) {
                        createOverlayForObject(Constants.AR_STRUCT_ITEM_FILTER, filter.getName());
                    }
                    Thread.sleep(2500);
                    Filter overlaidFilter = ctx.getFilter(deleteFilterName);
                    overlaidFilter.setEnable(false);
                    ctx.setFilter(overlaidFilter);
                    Thread.sleep(2500);
                } else if (cmd.equals("ENABLE")) {
                    ctx.deleteFilter(deleteFilterName, Constants.AR_DEFAULT_DELETE_OPTION);
                }
            } catch (ARException e) {
                System.out.println(e.getMessage());
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }
        }
    
        private static void createOverlayForObject(int objectStructType, String object) {
            OverlaidInfo overlaidInfo = new OverlaidInfo();
            overlaidInfo.setName(object);
            overlaidInfo.setObjType(objectStructType);
            try {
                ctx.createOverlay(overlaidInfo);
            } catch (ARException e) {
                System.out.println(e.getMessage());
            }
        }
    
        private static void getListOfFormsToPurge() {
            try {
                List<String> formsList = ctx.getListForm(0, Constants.AR_LIST_SCHEMA_ALL_WITH_DATA | Constants.AR_HIDDEN_INCREMENT);
                for (String form : formsList) {
                    for (String childForm : possibleChildSchemas) {
                        if (form.equals(childForm)) {
                            childSchemas.add(form);
                        }
                    }
                }
            } catch (ARException e) {
                System.out.println(e.getMessage());
            }
        }
    
        private static void delServiceRequests(String query) {
            List<Entry> serviceRequests;
            QualifierInfo qual;
            int[] fields = {1, 1000000829, 179, 303503400, 302825900};
            try {
                qual = ctx.parseQualification("SRM:Request", query);
                serviceRequests = ctx.getListEntryObjects("SRM:Request", qual, 0, 999999999, null, fields, false, null);
                for (Entry serviceRequest : serviceRequests) {
                    System.out.println(" \nDeleting Service Request: " + serviceRequest.get(1000000829));
                    if (isAIFDelete) {
                        if (serviceRequest.get(303503400) != null) {
                            if (serviceRequest.get(303503400).toString().equals("2000")) {
                                int[] AIFFormNameField = {302826200};
                                String qualStr = "'179' = \"" + serviceRequest.get(302825900) + "\"";
                                QualifierInfo AIFFormQual = ctx.parseQualification("SRS:CFGAdvancedInterface", qualStr);
                                List<EntryListInfo> AIFForms = ctx.getListEntry("SRS:CFGAdvancedInterface", AIFFormQual, 0, 1, null, null, false, null);
                                for (EntryListInfo AIFFormEntryInfo : AIFForms) {
                                    Entry AIFFormEntry = ctx.getEntry("SRS:CFGAdvancedInterface", AIFFormEntryInfo.getEntryID(), AIFFormNameField);
                                    String AIFFormName = AIFFormEntry.get(302826200).toString();
                                    childSchemas.add(AIFFormName);
                                }
                            }
                        }
                    }
                    for (String childSchema : childSchemas) {
                        try {
                            delChildRecordManager(childSchema, serviceRequest);
                        } catch (Exception ignored) {
                        }
                    }
                    ctx.deleteEntry("SRM:Request", serviceRequest.getEntryId(), 0);
                }
            } catch (ARException e) {
                System.out.println(e.getMessage());
            }
        }
    
        private static void delChildRecordManager(String childSchema, Entry entry) {
            if (childSchema.equals("AP:Detail") || childSchema.equals("AP:Signature")) {
                String qualStr = "'8' = \"" + entry.get(1) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("SRM:SR_AuditLog")) {
                String qualStr = "'450' = \"" + entry.get(1) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("NTE:Notifier Log")) {
                String qualStr = "'1000000205' = \"" + entry.get(1000000829) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("AST:CMDB Associations") || childSchema.equals("CHG:Associations") ||
                    childSchema.equals("PBM:Investigation Associations") || childSchema.equals("PBM:Known Error Associations") ||
                    childSchema.equals("PBM:Solution DB Associations") || childSchema.equals("WOI:Associations") ||
                    childSchema.equals("HPD:Associations")) {
                String qualStr = "'1000000204' = \"" + entry.get(1000000829) + "\" AND '1000000211' = 23000";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("SRM:Survey")) {
                String qualStr = "'301693900' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("SRM:WorkInfo")) {
                String qualStr = "'10001821' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("SRM:QuestionResponse")) {
                String qualStr = "'301368700' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("SRM:Associations")) {
                String qualStr = "'302774000' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("SRM:AppInstanceFlow")) {
                String qualStr = "'301373600' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("SRM:AppInstanceBridge")) {
                String qualStr = "'301368700' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("SLM:MilestoneLogging") || childSchema.equals("SLM:Measurement")) {
                String qualStr = "'490009000' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("SLM:EventSchedule")) {
                String qualStr = "'400030500' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("APR:Non-ApprovalNotifications")) {
                String qualStr = "'300717400' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("HPD:Help Desk") && isTransactionalDelete) {
                String qualStr = "'301368700' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("WOI:WorkOrder") && isTransactionalDelete) {
                String qualStr = "'301368700' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else if (childSchema.equals("CHG:Infrastructure Change") && isTransactionalDelete) {
                String qualStr = "'301368700' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            } else { // Dynamic AIF Schema
                String qualStr = "'301368700' = \"" + entry.get(179) + "\"";
                delChildRecord(childSchema, qualStr);
            }
        }
    
        private static void delChildRecord(String childSchema, String qualStr) {
            try {
                QualifierInfo qual = ctx.parseQualification(childSchema, qualStr);
                List<EntryListInfo> entriesToDelete = ctx.getListEntry(childSchema, qual, 0, 999999999, null, null, false, null);
                for (EntryListInfo entryListInfo : entriesToDelete) {
                    System.out.println("   Deleting entry " + entryListInfo.getEntryID() + " from: " + childSchema);
                    ctx.deleteEntry(childSchema, entryListInfo.getEntryID(), 0);
                }
            } catch (ARException e) {
                System.out.println(e.getMessage());
            }
        }
    }
    

    I hope this is useful to anybody in a similar situation where the need to delete Service Requests arise.  You can download the utility and code below and as always, happy coding 🙂

    Version 2.0

    • Added new arguments for specifying to optionally transactional data and aif data related to the service requests being deleted. The options are -trans and -aif.
    • Added 2.5 second delay after making overlay change to SRM Delete filter to allow slower systems to catch up and another 2.5 seconds after saving it in disabled state

    Version 1.1

    • Added the method for only attempting to delete from froms that actually exists on the target server to reduce “form does not exist” error noise

    Version 1.0

    • Initial release

    Service Request Purge

    Icon

    Service Request Purge - Source 15.39 KB 320 downloads

    ...
    Icon

    Service Request Purge - Utility 7.34 MB 328 downloads

    ...

      AIFs – Simplifying their creation via automation

      Since the inception of the BMC Service Request Management solution, there has been the concept of AIFs (Advanced Interface Forms) that allow for creating service requests as complicated and feature rich as required when the ‘simple’ wizard based service requests simply don’t cut it.  Over the years, more and more capabilities have been added to the ‘simple’ service requests to reduce some of the needs for AIFs but there are still some key lacking capabilities that still require us to build AIFs.  Some examples that most organizations run into are things like procurement or new hire requests where there is a ton of details that need to be gather and sometimes in a 1:N fashion that requires tables fields, support forms, complex validation workflow etc.

      Now creating AIFs have always been above and beyond what we could call simple, unlike the simple ‘wizard’ based service requests that don’t actually require any actual Remedy workflow to be developed, creating a working AIF requires a bit of elbow grease and essentially is remedy app-dev using some boilerplate objects.  Luckily, BMC includes templates that can be used as starting points but even with those to simply create your own version has some initial annoyances as well as some repetitive workflow requirements that must be done as part of your AIFs boilerplate before you can actually start molding it in your image.

      The steps involved in the above have been well documented in the SRM documentation as well as in posts such as the following: https://communities.bmc.com/community/bmcdn/bmc_remedy_ondemand/blog/2013/12/05/the-pulse-using-advanced-interface-form-aif-to-streamline-operations

      As well as the steps and procedure is documented, when you have to create many AIFs it can be a very tedious endeavor, and that first one is the worst of them all as the need to overlay hundreds of pieces of workflow the first time can be heartbreaking (some workarounds exist, e.g.: https://communities.bmc.com/message/274872#274872 and Ideas published asking to improve the situation https://communities.bmc.com/ideas/4954).  There should be an easier way for all this but unfortunately at the time of writing this there really isn’t so I decided to try and put in a stop-gap until this process is made more streamlined by BMC.

      Let’s review at a high level what we need to do to make this a seamless process:

      1. We need to be able to make a copy of an AIF to be used as a template for our new AIF (shouldn’t be limited to just the BMC provided samples)
      2. We need to be able to give our new AIF a name and a title as well as register it in the appropriate SRM configuration form
      3. We need to be able to automatically overlay the required remedy objects (if required) and then add new the AIF as a related form (except for some specific ones) to the overlaid objects
      4. We need to be able to make copies of specific workflow excluded from step 3 and make it relevant to us and then add it in the appropriate place in the appropriate guides

      With the above functional requirements, building a utility to automate all of it really isn’t that difficult and a lot of what is needed to make this work I’ve already published in my Replicating Save-As functionality of Developer Studio so right there we can cover off essentially steps 1 and 2 without a lot of work.  Step 3 and 4 require a bit more work as we ideally aren’t just following a fixed list of workflow (as workflow can change from version to version) so we need to inspect the original AIF we used as our template and then based on if it is a WithMapping or WithOutMapping (or display-only) AIF, take it’s workflow and make overlays if they are not already, add your new form to them and then again based on the type of AIF it is, generate and add custom active links to some existing guides.

      Finding out if your base AIF is a WithMapping or WithOutMapping template is as simple as check if one of the mapping fields exists, for me I used fieldID 300070001 for my check which is SR Type Field 1.  Knowing what kind of template you are basing yours on is important so that you know to make some WithMapping active links or WithoutMapping active links.  As stated in the previously linked documents, the custom workflow required to be built and then added to guides are either:

      • SRS:ADN:WithMapping_OnSubmit_109_SubmitRequest_CheckReqFields
      • SRS:ADN:WithMapping_OnSubmit_109_AddToCart_SetFlag

      or

      • SRS:ADN:WithoutMapping_OnSubmit_109_SubmitRequest_CheckReqFields
      • SRS:ADN:WithoutMapping_OnSubmit_109_AddToCart_SetFlag

      In either scenario, you then add your custom workflow into the appropriate guides at the appropriate location (under the equivalent OOTB active links).

      That really is all it takes, with the above performed, you are ready to start tailoring your new AIF to your liking and then linking it to an SRD.  Let’s look at the utility I built to do all the above in a single swoop.  Here is an example of executing the utility from end to end:

      AIFGenerator_Terminal

      From the above we can see a few things, first off after providing the necessary arguments to start the utility (for usage examples / requirements you can pass -h), you are presented with a list of all the registered and active AIFs on the target Remedy system, this is accomplished by querying the SRS:CFGAdvancedInterface form.  After you have selected the template you want to use, you must give it a form name and a title.  The reason I also prompt for the title is so that we can auto create a new entry for our AIF in the SRS:CFGAdvancedInterface form when we are done.  We then ask for the Prefix for the new objects, this is to support those custom active links that must be added to the guides.  The end-to-end process above once you have answered all of the questions takes about 30 seconds if this is the first time you are adding an AIF since much workflow must be overlaid and then subsequent AIF creations take about 15-20 seconds (this is based upon time on my own system, maybe faster or slower on yours).

      Let’s look at the results of the above run:

      AIFGenerator_ResultDevStudio

      AIFGenerator_WorkingList

      As we can see, we have a perfect clone of the AIF we based ourselves on created for us as well as all the required workflow objects have been related and created where appropriate and linked to the new AIF as seen by the Working List above showing 201 items touching our new AIF form (can also see our 2 custom active links in that screenshot as well).

      AIFGenerator_SRSCFGAdvancedInterface

      Above we see that our AIF was also registered for us so we can begin using our new AIF right away.  You can download the utility below (unzip it to get the Jar file), it has only been tested on SRM 8.1 however I don’t see why this wouldn’t work on older versions as well.  The only real dependency on version is that this is built for 7.6.04+ as it leverages overlays but it hasn’t been tested outside of my sandbox.  Please let me know if you run into any bugs or if you think after using this utility it has been useful to you.

      As always, happy coding 🙂

      ***Version 2.1

      • Changed the way I package the application.  Instead of packing everything into a single JAR, the zip file now contains a lib folder as well, make sure to always have the lib folder in the same folder as the AIFGenerator.jar file
      • Added capability for handling custom field submission for the AIFs, e.g. it will create a new version of the SRS:ADN:SHR_OnSubmit_120_SubmitCustomForm active link that will allow for adding your custom fields into as well as created a new version of the SRS:ADN:SHR_ServiceCall_SubmitCustomForm filter that actually does the Push into the AIF form.  This Service Call -> Push fields approach is new to 8.1 AIFs so this change will only happen if the SRM version is found to be 8.1 or higher.

      ***Version 2.0

      Large update – Added the ability to specify how to handle custom workflow objects from the template AIF

      ***Version 1.1

      Fixed several issues with custom object handling as well as improved some of the interactive options

      ***Version 1.0

      Initial release of utility