tag:blogger.com,1999:blog-23869432676801556852024-03-22T13:57:53.839+11:00Maximo ConsultantRandom stuff for Maximo peopleViet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.comBlogger83125tag:blogger.com,1999:blog-2386943267680155685.post-91649149832317700402023-04-24T21:55:00.002+10:002023-04-24T23:06:32.712+10:00The curious case of the MIA work orders?<p>Working in IT, we deal with strange issues all the time. However, every once in a while, something would come up that leaves us scratching our heads for days. One such issue happened to us a few years back. It came back to me recently and this time, I thought to myself I should note it down.</p><p><br /></p><p>The issue was first reported to us when users raised a ticket about missing work orders in TechnologyOne, the Finance Management System used by our client. Without work orders created in TechOne, the users won't be able to report actual labor time or other costs. Thus, this is considered a high priority issue.<span></span></p><a name='more'></a><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGWs1fG3dWhiqjF1DQRRWfFjLS1Xi5YOyY_5OAKYxFDXEXCXjkCzseUOQch0ztUek5UDGl6YVVAqrL-TYci4TH6arSld1a6BRqpoTMEc7-WY3CEUJFkrWgkjo934W3E-vPPCC1vvmFtr7xFCKlxh6susNh69O_kzBpvUfjkLo80dCDhH85VJiAR31p/s563/site-under-maintenance.JPG" style="margin-left: 1em; margin-right: 1em;"><img alt="F5 maintenance page for integration should not have HTTP 200 OK status" border="0" data-original-height="294" data-original-width="563" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGWs1fG3dWhiqjF1DQRRWfFjLS1Xi5YOyY_5OAKYxFDXEXCXjkCzseUOQch0ztUek5UDGl6YVVAqrL-TYci4TH6arSld1a6BRqpoTMEc7-WY3CEUJFkrWgkjo934W3E-vPPCC1vvmFtr7xFCKlxh6susNh69O_kzBpvUfjkLo80dCDhH85VJiAR31p/s16000/site-under-maintenance.JPG" title="F5 maintenance page for integration should not have HTTP 200 OK status" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h4 style="text-align: center;">F5 maintenance page for integration should not have HTTP 200 OK status</h4><p>TechOne is integrated with Maximo using WebMethods, an enterprise integration platform. Unlike direct integration, these types of problem are usually easy to deal with when an enterprise integration tool is used. We simply look at the transaction log, identify the failed transactions and what caused it, fix the issue, then resubmit the message. All good integration tools have such fundamental capability.</p><p><br /></p><p>In this case, we looked at WebMethods' transaction history and couldn't find any traces of the missing work orders. We also spent quite some time digging through all of the log files of each server on the cluster to find any error but couldn't find any thing relevant. Of course, that is the case because if there is an error, it should have been picked up and the system should raise alarms and email notifications to a few overlapped monitoring channels we setup for this client.</p><p><br /></p><p>On the other hand, when we looked at Maximo's Message Tracking and log files, everything looks normal with work orders published to WebMethods correctly without interruption. In other words, Maximo said it had sent the message, while WebMethods said it never received anything. This left us in limbo for a few days. And of course, when we have no clue, we did what we application people do best, we blamed the network guys.</p><p><br /></p><p>Network team couldn't find anything strange in their logs too. So, we let the issue slip for a few days without any real progress. During this, users kept reporting new missing work orders not knowing that I didn't really do any troubleshooting work. I was staring at the screen mindlessly all day long. Then of course, when you stare at something long enough, the problem will reveal itself. With enough work orders reported, it became clear that all of the updates only went missing during a period between 9 to 11 PM regardless of the type of work orders or data entered. When this pattern was mentioned, it didn't take long for someone to point out that this is usually the time when IT do their Windows patching.</p><p><br /></p><p>When a server is being updated, IT would set the F5 Load Balancer to re-direct any user requests to a "Site Under Maintenance" page, which makes sense for a normal user accessing a service via the browser. The problem is, when Maximo published an integration message to WebMethods, it received the same web page, which is ok, as it doesn't process any response. However, the status of the response is HTTP 200 which is not ok in this case. Since it's a HTTP 200 OK status, Maximo thought the message has been accepted by WebMethods and thus marked it as a successful delivery. WebMethods, on the other hand, never received such message.</p><p><br /></p><p>The recommendation in this case is to set the status of the Maintenance page to something other than HTTP 2xx. When Maximo receives a status other than 2xx, it marks the messaged as a delivery failure. Which means the administrator shall be notified if monitoring is setup. The failed message will be listed as error and can be resubmitted using the Message Reprocessing app. </p><p><br /></p><p>Due to complex communication chain involved, I never heard back from F5 team on what exactly was done to rectify the issue. However, from a quick search, it looks like it can be achieved easily by updating a rule in F5.</p><p><br /></p><p>This same issue recently came back to me, so I had to note it down to my <a href="https://vietmaximo.blogspot.com/2022/06/common-issues-when-setting-up-maximo.html" target="_blank">list of common issues with Load Balancer</a>. I think it is also fun enough to deserve a separate post. If you made it this far and think it's not fun. I hope at least it will be useful to you at some point.</p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-57787956596209202712023-03-22T22:00:00.009+11:002023-05-26T09:06:14.231+10:00Deployment without downtime?<p>Downtime is costly to the business. As developers, avoiding it can give us a ton of benefits both in term of efficiency and for personal well-being as well. For example, when making changes that require downtime to a shared environment, I have my freedom back since I don’t have to ask or wait to do it at night. </p><p>With the introduction of Automation Script, most of the business logic and front-end changes we need push to production nowadays can be done without downtime. Some of them are:</p><p></p><ul style="text-align: left;"><li>Automation Script</li><li>Escalation</li><li>Application Design<span><a name='more'></a></span></li><li>Conditions</li><li>Workflows</li></ul><p></p><p>However, Database Configuration changes still need Admin Mode or a restart. </p><p>In recent years, many of us have switched to DBC script to deploy changes. Although this approach takes more time to prepare than compared to other methods such as using Migration Manager or doing it by hand. It proves to be very reliable and allows faster deployment with much less risk. </p><p>Then many of us probably realized that, for small changes, we can run DBC script directly when the system is live. But after that, we will still need a quick restart. Doesn’t matter whether it’s a small environment which takes 5 minutes to restart or a massive cluster which needs 30 minutes. A restart is downtime, and any deployment that involved downtime will be treated differently with days or weeks of planning and rounds of approval and review.</p><p>For development, a colleague showed me a trick that, instead of a restart, we can just turn on and off Admin Mode. As part of this process, Maximo cache is refreshed and the changes will take effect. This works quite well in a few instances. However, this is still a downtime and can’t be used for Production. On a big cluster, in many cases, turning on Admin Mode takes more time than a restart.</p><p>My other colleague hinted me for a different method and this is what I ended up with. I have been using this for a while now and can report that it is quite useful. Not only my productivity has improved, it has proven to be valuable a few times when I don’t have to approach cloud vendors to ask for a downtime or restart.</p><p>The approach is very simple, when having a change that requires restart, I’ll script it using DBC. If the change is small, I can get away using Update/Insert SQL to update directly to the configuration tables such as:</p><p></p><ul style="text-align: left;"><li>MAXATTRIBUTE/MAXATTRIBUTECFG</li><li>MAXOBJECT/MAXOBJECTCFG</li><li>SYNONYMDOMAIN</li><li>MAXLOOKUPMAP</li><li>Etc.</li></ul><p></p><p>Next, I will create a super complex autoscript with no launchpoint below:</p><br /><p><script src="https://gist.github.com/viettranit/e3e334d8d0e2150e3d285e2ba107b87c.js"></script><br /></p><p><br /></p><p>Please note, this is not a bullet proof approach officially recommended by IBM. As such, I suggest if you use it for Production, make sure you understand the change and its impact. I will only use it for small changes on areas that have little or no risk of user writing the data to while the change is being applied. For a major deployment, for example, a change to the WORKORDER table, it’s a bad idea to apply it during business hours. For non-production, I don’t see much risk involved. </p><p>A man who don't work at night is a happy man.</p><div><br /></div><div><b><i><u>UPDATE</u>: </i></b></div><div><ul style="text-align: left;"><li>This works well for clustered environment. The "True" parameter passed to the function means Refresh All Servers in the cluster. If you want to refresh only one server, set it to False and run the script from the specific server by accessing the script via the server's specific 908x port.</li></ul></div><p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-4121280741428574372023-03-10T21:20:00.004+11:002023-03-10T21:30:23.345+11:00How to run SQL query in Maximo without DB access using API Automation Script<p>From Maximo version 7.6.0.9, we can now build <b>custom API using automation script</b>. This is a powerful new feature yet it looks to be underutilized by the community.</p><p>The first obviously use case is it gives us the freedom to build any API we wanted without being restricted to the limitation of the Maximo Integration Framework. For example, we can build an API that returns data in CSV or binary format. Or we can use it to upload data and bypassing the business layer.</p><p>Since it allows us to use the browser to interact with Automation script, and the script framework itself has access to all Java functions of the MBO layer, We can exploit it to execute all sort of weird operations.<span></span></p><a name='more'></a><p></p><p>In an article I posted a few days ago, I use API script to call a Java function to <a href="https://vietmaximo.blogspot.com/2023/03/hot-reset-sequence-without-restarting.html">refresh Maximo sequence</a> and avoid a restart. Using the same approach, we can do a database configuration and deployment without downtime. I will provide more details about that in a future post.</p><p>In this tech note today, I'll demonstrate how we use API script to run SELECT, UPDATE, DELETE SQL statements to Maximo database without direct DB access. This can come in handy when DB access is restricted. Of course, we can <a href="https://vietmaximo.blogspot.com/2018/07/how-to-modify-almost-any-maximo-data.html">use MXLoader to achieve the same result</a>. However, with this new method, it is a lot more convenient.</p><p><br /></p><p><b>Creating an API script</b> is very simple, we just need to create a script without launch point. Then we can call it by accessing this URL on the browser:</p><p><b>https://[MAXIMO_ROOT]/maximo/oslc/script/[SCRIPT_NAME]</b></p><p>If you're already logged in and has a session. That is all it takes. Otherwise, to authenticate the request, you can pass in username and password parameters like you would normally do when calling REST API.</p><p><b>https://[MAXIMO_ROOT]/maximo/oslc/script/[SCRIPT_NAME]?_lid=[USERNAME]&_lpwd=[PASSWORD]</b></p><p><br /></p><p>To run a SELECT query on the database, I created a script named RUNDANGEROUSSQL with the piece of python code I included at the end of this post.</p><p>To use the script to run a query, I typed the SQL query directly in the URL in the <b>sql</b> parameter as below. In this case, the data is returned to the browser in CSV format. If it's a big table, we can also copy/paste the text from the browser into a .CSV file then open it in Excel for better readability.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6SWvHJUxoOaIL2ll0Rk_ea5erGxM4id9GHIkQ9uvawZfkT-EyADhkb0zXv-GBpNVmA3MXVbfBFIDF_Kt3sPXn_Nm-oZSI-txA5rUFcLSH80WT_DQbaEJjbS4pGDIkC0GgMpkpVnPcX0BguP-R1EHFi-YmIaEPBBbUJRAwsLg0JvTsLxxr5TjLxaUq/s1151/run_on_browser.png" style="margin-left: 1em; margin-right: 1em;"><img alt="Typing SQL query directly in URL and get CSV result" border="0" data-original-height="348" data-original-width="1151" height="194" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6SWvHJUxoOaIL2ll0Rk_ea5erGxM4id9GHIkQ9uvawZfkT-EyADhkb0zXv-GBpNVmA3MXVbfBFIDF_Kt3sPXn_Nm-oZSI-txA5rUFcLSH80WT_DQbaEJjbS4pGDIkC0GgMpkpVnPcX0BguP-R1EHFi-YmIaEPBBbUJRAwsLg0JvTsLxxr5TjLxaUq/w640-h194/run_on_browser.png" title="Typing SQL query directly in URL and get CSV result" width="640" /></a></div><div><br /></div>We can use this same autoscript to run DELETE or UPDATE command. Maximo will give an error in the response because there is no resultset return when executing the statement. But statement will still run and update the data. If you don't like to see the error, you can modify the script a bit to handle the case stmt.executeQuery(sql) doesn't return a value.<div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqoKSKvhUWefSCdRYEGPneM5C3djLu0pBiyC2EXvFFywXqBmWZTZRIvRgsimuBabL5zfdM2dKoSMWqkryKhZW7-rRfscVWh7_aWo1atazVwp3E6h4XVYCAclEaOgIwx-38fv_gO-_N1nJEHo7PdRtUQ8vuX8htySaV8jSlN3a6XXrplPgDGfEaRUTS/s1134/run_delete_command.png" style="margin-left: 1em; margin-right: 1em;"><img alt="DELETE or UPDATE statement doesn't return result set" border="0" data-original-height="222" data-original-width="1134" height="125" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqoKSKvhUWefSCdRYEGPneM5C3djLu0pBiyC2EXvFFywXqBmWZTZRIvRgsimuBabL5zfdM2dKoSMWqkryKhZW7-rRfscVWh7_aWo1atazVwp3E6h4XVYCAclEaOgIwx-38fv_gO-_N1nJEHo7PdRtUQ8vuX8htySaV8jSlN3a6XXrplPgDGfEaRUTS/w640-h125/run_delete_command.png" title="DELETE or UPDATE statement doesn't return result set" width="640" /></a></div><div><br /></div><div>Have fun.</div><div><br /></div><div>RUNDANGEROUSSQL Source Code:<br /><script src="https://gist.github.com/viettranit/b52cf6888d7197bb8714abed12fe474b.js"></script><div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div><br /></div></div></div>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com2tag:blogger.com,1999:blog-2386943267680155685.post-6002201579177165772023-03-08T20:59:00.011+11:002023-05-26T09:11:24.902+10:00Hot reset sequence without restarting Maximo<p><span style="font-family: inherit;">One error we often have to deal with is incorrect sequence
when adding new data to Maximo. There are many situations when it might come up,
such as:</span></p><p></p><ul style="text-align: left;"><li><span style="font-family: inherit;">When loading data using MXLoader, or inserting data directly via SQL</span></li><li><span style="font-family: inherit;">Sequence corruption due to unknown cause in Production, probably due to errors caused by cancelled/terminated job</span></li><li><span style="font-family: inherit;">Restoring database from a copy, or after an upgrade.</span></li></ul><p></p><p class="MsoNormal"><span style="font-family: inherit;">When this happens, the user sees an error with duplicated key
value such as “<b>BMXAA4211E - Database error number 2601 has occurred…</b>”<span></span></span></p><a name='more'></a><span style="font-family: inherit;"><o:p></o:p></span><p></p><div>
<p class="MsoNormal"><span style="font-family: inherit;">The solution for this is well documented and
straightforward, we just need to find the current maximum ID value used in the
table, and update the corresponding sequence to use the next value. <o:p></o:p></span></p>
<span style="font-family: inherit; line-height: 107%; mso-ansi-language: EN-AU; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-language: AR-SA; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">For example, if the error occurs to the<b> </b>WORKORDERID
field of the WORKORDER table, we can do this SQL update and restart Maximo.</span></div><div><span style="line-height: 107%;"><p class="MsoNormal"><span style="font-family: inherit;"><span style="color: magenta; line-height: 107%; mso-bidi-font-family: Consolas;">UPDATE</span><span style="line-height: 107%;"> maxsequence </span><span style="color: blue; line-height: 107%; mso-bidi-font-family: Consolas;">SET</span><span style="line-height: 107%;"> maxreserved </span><span style="color: grey; line-height: 107%; mso-bidi-font-family: Consolas;">=</span><span style="color: blue; line-height: 107%; mso-bidi-font-family: Consolas;"> </span><span style="color: grey; line-height: 107%; mso-bidi-font-family: Consolas;">(</span><span style="color: blue; line-height: 107%; mso-bidi-font-family: Consolas;">SELECT</span><span style="line-height: 107%;"> </span><span style="color: magenta; line-height: 107%; mso-bidi-font-family: Consolas;">max</span><span style="color: grey; line-height: 107%; mso-bidi-font-family: Consolas;">(</span><span style="line-height: 107%;">workorderid</span><span style="color: grey; line-height: 107%; mso-bidi-font-family: Consolas;">)</span><span style="line-height: 107%;"> </span><span style="color: grey; line-height: 107%; mso-bidi-font-family: Consolas;">+</span><span style="line-height: 107%;"> 1 </span><span style="color: blue; line-height: 107%; mso-bidi-font-family: Consolas;">FROM</span><span style="line-height: 107%;"> workorder</span><span style="color: grey; line-height: 107%; mso-bidi-font-family: Consolas;">)</span><span style="line-height: 107%;"> </span><span style="color: blue; line-height: 107%; mso-bidi-font-family: Consolas;">WHERE</span><span style="line-height: 107%;"> tbname </span><span style="color: grey; line-height: 107%; mso-bidi-font-family: Consolas;">=</span><span style="line-height: 107%;"> </span><span style="color: red; line-height: 107%; mso-bidi-font-family: Consolas;">'WORKORDER'</span><span style="line-height: 107%;"> </span><span style="color: grey; line-height: 107%; mso-bidi-font-family: Consolas;">and</span><span style="line-height: 107%;"> </span><span style="color: blue; line-height: 107%; mso-bidi-font-family: Consolas;">name</span><span style="line-height: 107%;"> </span><span style="color: grey; line-height: 107%; mso-bidi-font-family: Consolas;">=</span><span style="line-height: 107%;"> </span><span style="color: red; line-height: 107%; mso-bidi-font-family: Consolas;">'WORKORDERID'</span><o:p></o:p></span></p><p class="MsoNormal"><span style="font-family: inherit;"><o:p></o:p></span></p><p class="MsoNormal"><span style="line-height: 107%;"><span style="font-family: inherit;">However, I like to avoid restarting Maximo if possible due
to some obvious problems such as:</span></span></p><p class="MsoNormal"></p><ul style="text-align: left;"><li><span face="Calibri, sans-serif"><span style="font-family: inherit;">I recently had to do a quick deployment which involves uploading some data. For some unknown reasons, loading the data via MXLoader causes random sequence corruption a few times. For this client which has a large cluster, restarting Maximo will require an additional 30-60 minutes down time.</span></span></li><li><span face="Calibri, sans-serif"><span style="font-family: inherit;">A location data hierarchy update requiring me to insert a few thousand new records to the LOCANCESTOR table. I needed update the sequence to a new value for subsequent data upload via MIF to work. Since it is a cloud environment, if I can avoid a restart, we won’t need to be dependent on the availability of the cloud provider.</span></span></li></ul><div><span style="font-family: inherit; line-height: 107%; mso-ansi-language: EN-AU; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-language: AR-SA; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">To
address that problem, the simplest solution I found to hot reset sequence cache without restarting Maximo is by
calling the reset sequence Java function via automation script. The steps are as
follows</span></div><div><ul style="text-align: left;"><li><span style="font-family: inherit; line-height: 107%; mso-ansi-language: EN-AU; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-language: AR-SA; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">Create
a new script with no launch point:</span></li></ul><br /><script src="https://gist.github.com/viettranit/a20e2c45446e8ee4d8f7b4a593b42767.js"></script><br /><span style="font-family: inherit;"><br /></span><ul style="text-align: left;"><li><span style="font-family: inherit; line-height: 107%; mso-ansi-language: EN-AU; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-language: AR-SA; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;"><p class="MsoNormal">Whenever we update maxsequence table with a new value and need to reset the cache, just execute the script by calling it via REST API: <span><b>[MAXIMO_URL]//maximo/oslc/script/runtask?_lid=maxadmin&_lpwd=maxadmin</b></span></p><p class="MsoNormal"><o:p></o:p></p><br /></span></li></ul><div><span style="font-family: inherit;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGt8FT4tXVU28u4Ox1ZsbteyNCa61PBcnEky2lprEbk4RnqaWrsm9EZdi66OH1Ec0qtRsCYT-N0viv28lKr4C6sEiWhQXEecpL0e1922WGQWX9uCDP4ckC0EYnOCw0zxXzUlui4fxMbdwqb-nFX45ts6fLdZZ96k5q4yOqoRzJlK_BTdgqajSfJQ9q/s602/2_run_script_via_REST_request.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="123" data-original-width="602" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGt8FT4tXVU28u4Ox1ZsbteyNCa61PBcnEky2lprEbk4RnqaWrsm9EZdi66OH1Ec0qtRsCYT-N0viv28lKr4C6sEiWhQXEecpL0e1922WGQWX9uCDP4ckC0EYnOCw0zxXzUlui4fxMbdwqb-nFX45ts6fLdZZ96k5q4yOqoRzJlK_BTdgqajSfJQ9q/s16000/2_run_script_via_REST_request.png" /></a></div>No restart during a deployment means we can all go to bed earlier. Best of luck.<br /><b><br /></b></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: inherit;"><br /></span></div></div><span style="font-family: inherit;"><u>UPDATED</u>: </span></span></div><div><span style="line-height: 107%;"><span style="font-family: inherit;">- On a clustered environment, I find it doesn't seem to refresh all the JVMs. Thus, to be sure, we might need to run it on each JVM separately (by accessing the script from the JVM 908x port)</span></span></div><div><span style="line-height: 107%;"><p class="MsoNormal"><br /></p><p class="MsoNormal" style="font-family: Calibri, sans-serif; font-size: 11pt;"><span style="color: red; font-family: Consolas; font-size: 9.5pt; line-height: 107%; mso-bidi-font-family: Consolas;"><br /></span></p></span></div><p class="MsoNormal"><o:p></o:p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-25563260994304459132023-01-31T12:47:00.003+11:002023-01-31T12:47:36.358+11:00Use Maximo webservice with JSON content<p><br /></p><p>While the JSON API in newer version of Maximo is quite useful, for many integration scenarios, I still prefer to use the old API infrastructure with Publish Channel and Web Service. However, the native format for this feature is XML.</p><p>To send or receive JSON with Publish Channel or Enterprise Service, we can translate the default to JSON format before it goes out / into the system. Below is a simple example to set it up.</p><h4 style="text-align: left;"><b>Setup standard Publish Channel to send XML message</b></h4><p></p><ul style="text-align: left;"><li>Create a new Publish Channel: </li></ul><span><a name='more'></a></span><p></p><p></p><ul style="text-align: left;"><ul><li>Name: ZZSR</li><li>Object Structure: MXSR</li></ul></ul><ul style="text-align: left;"><li>Create a new Enterprise Service:</li><ul><li>Name: ZZSR</li><li>Object Structure: MXSR</li></ul></ul><ul style="text-align: left;"><li>Create a new HTTP End Point: ZZWEBHOOK (To keep it simple, we use webhook.site. See more details here)</li></ul><ul style="text-align: left;"><li>Create a new External System:</li><ul><li>Name: ZZTEST</li><li>End Point: ZZWEBHOOK</li><li>Set standard queues.</li><li>In Publish Channels tab, add the ZZSR channel created above.</li><li>Enable the channel, enable the external system, and enable event listener on the channel</li></ul></ul><p></p><p> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgEMjN19lTGxkcfZ23FUvC1jEzARSqZeZTK057W8_nZMfPtixv8lV5INhUJqvqmesC7iF2DZRl7zjLCtUJ8Vrqrnes2ZWSBH5J60GQJKlNjUY55tHASEj39rWh0ONNCjJBgKtDNdlqhDQZm2pvRNpF3Di2Zm-hr-Gwjs3U-tP9TIYwS9KluZJDq3L8R" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="467" data-original-width="940" height="318" src="https://blogger.googleusercontent.com/img/a/AVvXsEgEMjN19lTGxkcfZ23FUvC1jEzARSqZeZTK057W8_nZMfPtixv8lV5INhUJqvqmesC7iF2DZRl7zjLCtUJ8Vrqrnes2ZWSBH5J60GQJKlNjUY55tHASEj39rWh0ONNCjJBgKtDNdlqhDQZm2pvRNpF3Di2Zm-hr-Gwjs3U-tP9TIYwS9KluZJDq3L8R=w640-h318" width="640" /></a></div><br /><p></p><p></p><ul style="text-align: left;"><li>To test and verify our Publish channel is working:</li><ul><li>Create a new Service Request, then save.</li><li>After less than a minute, Webhook should show a new message received in XML format</li></ul></ul><h4 style="text-align: left;">To send JSON instead of XML:</h4><p></p><p></p><ul style="text-align: left;"><li>Update publish channel ZZSR, set Processing Class with value com.ibm.tivoli.maximo.fdmbo.JSONMapperExit</li><li>Go to the JSON Mapping application, create a new mapping:</li><ul><li>Name: ZZTEST.ZZSR.OUT (it must follow this exact format <ExtSys>.<PublishChannel>.OUT)</li><li>Object Structure: MXSR</li><li>JSON Data: { "summary": "Test SR", "details": "Sample Details", "assetnum": "11430" }</li></ul><li>Save the record. Then go to Properties tab. Enter mapping as follows</li><li>To test the mapping works, create another SR. And check in Webhook to ensure it now receive a new message in JSON format</li></ul><p><br /></p><p>To receive JSON on Webservice, use similar step like above, the only key difference is, we have to name the JSON mapping as <ExtSys>.<EntService>.IN. And use a mapping like the example below</p><p><br /></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-44716984620347983352023-01-16T09:27:00.000+11:002023-01-16T09:27:15.186+11:00Message Engine doesn't start after setting up cluster<p>This issue hit me a few times and always took me some time to figure out what happened. So I thought it's a good idea to note it down.</p><p><br /></p><p><b>Symptom:</b></p><p>When setting up cluster environment for Maximo, I will need to setup an integration bus with a message engine for each cluster (IF, UI, Cron etc.)</p><p><br /></p><p>Each message engine will require its own individual schema (and thus individual user if the Oracle DB is used)</p><p><br /></p><p>After integration bus are setup and Maximo cluster started, we see a lot of errors in the log file, usually in the Cron or MIF cluster due to message engine is not available.</p><span><a name='more'></a></span><p><br /></p><p>When restarting the cluster, we can see that the message engine for that cluster has a "partial started" status. But a few minutes after the whole cluster is started, the message engine would show an "unavailable" status.</p><p> </p><p><b>Troubleshoot:</b></p><p></p><ul style="text-align: left;"><li>Check ffdc log under [Websphere_Home]\AppServer\profiles\ctgAppSrv01\logs\ffdc\, check for [MXServer_Name]_exception.log to see if there are any exception related to integration bus or message engine such as:</li></ul><p></p><blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><p style="text-align: left;">com.ibm.ws.sib.msgstore.persistence.DatasourceWrapperStoppedException com.ibm.ws.sib.msgstore.persistence.impl.PersistentMessageStoreImpl.start 1:206:1.47.1.53 D:\IBM\WebSphere\AppServer\profiles\ctgAppSrv01\logs\ffdc\MAXUI-N1-4_58bec328_22.10.24_15.50.24.7956943157914483024680.txt</p></blockquote><blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><p style="text-align: left;">com.ibm.ws.sib.msgstore.PersistenceException com.ibm.ws.sib.msgstore.impl.MessageStoreImpl.start 755 D:\IBM\WebSphere\AppServer\profiles\ctgAppSrv01\logs\ffdc\MAXUI-N1-4_58bec328_22.10.24_15.50.24.8271423565797649410081.txt</p></blockquote><p><br /></p><p></p><ul style="text-align: left;"><li>For each of the above exception, open the file it referenced. We might see some detailed error message which could help us to solve the issue.</li></ul><p></p><p><br /></p><p></p><ul style="text-align: left;"><li>In this case, I have this vague error message below:</li></ul><p></p><blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><p style="text-align: left;">CWSIS1501E: The data source has produced an unexpected exception: com.ibm.ws.sib.msgstore.persistence.DatasourceWrapperStoppedException: New connections cannot be provided because the persistence layer has been stopped</p></blockquote><p><br /></p><p><b>Solution:</b></p><p></p><ul style="text-align: left;"><li>For this specific case, it is caused by a failed setup process in previous setup which left a bunch of tables created under different message engine details. Thus, the current message engine doesn't like to reuse it. To solve this issue, we can simply change the schema name of for the message store so that the next time the cluster starts, it will create a whole new bunch of tables for it own use.</li></ul><div><br /></div><ul style="text-align: left;"><li>If Oracle database is used, schema is linked with the DB account used for login, and is more difficult to change. Thus, I'll just drop all the tables used by that schema. One quick way to identify and delete those tables is running this query:</li></ul><p></p><blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><p style="text-align: left;">select 'drop table ' || table_name || ';' from user_tables;</p></blockquote>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-80866669928632437412022-12-13T10:12:00.003+11:002022-12-13T12:15:12.638+11:00Consider Time Zone with automation script<p>It took me some time to get to this piece of code, but requirement changed and I needed to ditch it. But I'm sure I'll need to use it at some point in the future, so why not put a note here just in case.</p><p><br /></p><p><b>Requirement</b>: </p><p>- A client has many hotels in Sydney and Brisbane which are in two different time zones. Sydney has daylight saving while Brisbane doesn't have. In other words, there is one hour difference for half of the year, and no time difference for the other half.</p><p>- Client wants to display a warning message to the user that when he/she raised a service request out of normal working hours (7AM to 7PM), if it's a high priority item, they should make a phone call instead.</p><span><a name='more'></a></span><p><br /></p><p><b>Finding</b>:</p><p>- User profile has a time zone setting. We can also associate a time zone to a specific site or location</p><p>- Time stored in the database is server time. In this case, the server is in Sydney, as such all date/time values are Sydney time. The time zone associated to site or location won't affect the time values in Work Order (e.g. reporteddate)</p><p>- Time displayed on the screen is converted to user's time zone.</p><p>- Therefore, if we use a conditional expression with a standard SQL where clause, or create a saved query using the time part, it can be inaccurate due to the values are all Sydney time</p><p><br /></p><p><b>Example</b>:</p><p>- During summer time when there is daylight saving in Sydney, there is 1h difference between Brisbane and Sydney. A user in Brisbane creates a ticket at 6:30 PM for a location in Sydney, that would be 7:30 PM. </p><p>- If we consider user's time zone, that's still within working hours. If we consider site's time zone, that's off hours. Based on that, we might want to display our warning accordingly.</p><p>- The custom conditional expression automation script below works for me:</p><p><br /></p><p><br /></p><script src="https://gist.github.com/viettranit/988bf841990bb5e2b5c049c1d8bc3f14.js"></script><p><br /></p><p>- In the case we just want to convert time to one specific zone, we can use the java.util.TimeZone class:</p><p>from java.util import TimeZone</p><p>timeZone = TimeZone.getTimeZone("Australia/Sydney")</p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-37845795430427824922022-08-22T18:01:00.001+10:002022-08-22T18:01:13.638+10:00Issues with Maximo attachment (DOCLINKS)<p> Just a bunch of my own personal notes regarding Maximo
attachment (DOCLINKS) function.</p><p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal"></p><ul style="text-align: left;"><li><o:p> </o:p>When a file is attached to a record in Maximo, it creates
a record in the DOCLINKS and a record in the DOCINFO table to keep the details
of the file. The file is copied to a location on disk, usually on a local
folder (e.g. D:\DOCLINKS) or on a network shared folder. The path for Maximo to
read the file is kept in the URLNAME field of the DOCINFO table.</li><li>To setup this function, refer to this blog post by Bruno
on MaximoDev blog (https://bportaluri.com/2014/06/attachments-doclinks-configuration.html)</li></ul><ul style="text-align: left;"><li>When a user uploads an attachment, the location where Maximo
puts the file will depend on the Folder selected:<span><a name='more'></a></span></li></ul><ul style="text-align: left;"><ul><li>In most applications such as Work Order Tracking
or Assets, there is a <b style="text-indent: -18pt;">Select Action > Attachment Library/Folders >
Manage Folder</b><span style="text-indent: -18pt;"> function, it allows you to specify the location to keep the
file for each Folder.</span></li></ul></ul><ul style="text-align: left;"><ul><li>If no path is specified for a folder, Maximo uses
default value of the System Properties: <span style="text-indent: -18pt;"> </span><b style="text-indent: -18pt;">mxe.doclink.doctypes.defpath</b></li></ul></ul><ul style="text-align: left;"><ul><li>This setting controls where to keep the file
when uploading. After a migration or upgrade in which you moved your file
server location, and if new files still end up in the old location. This is
where you should update your settings</li></ul></ul><ul><li>When a user clicks on the link to download an attachment,
there are two methods for Maximo to serve the file.</li></ul><ul style="text-align: left;"><ul><li>If System Property: <b style="text-indent: -18pt;">mxe.doclink.securedAttachment</b><span style="text-indent: -18pt;">
is set to </span><b style="text-indent: -18pt;">False</b><span style="text-indent: -18pt;"> (default), the HTTP </span><b style="text-indent: -18pt;">Web Server</b><span style="text-indent: -18pt;"> will read the file
from disk, then serve it to the end-users. Maximo </span><b style="text-indent: -18pt;">application server</b><span style="text-indent: -18pt;">
doesn’t play a role in delivering the file here.</span></li></ul></ul><ul style="text-align: left;"></ul><ul style="text-align: left;"><ul><li>If System Property: <b style="text-indent: -18pt;">mxe.doclink.securedAttachment</b><span style="text-indent: -18pt;">
is set to </span><b style="text-indent: -18pt;">True, </b><span style="text-indent: -18pt;">when the end-user downloads an attachment, Maximo
application server will grab the file, encrypt it, then serve it to the
end-user. Thus, in this case, we don’t really need the web server for it to
work. I.e. if you accessing Maximo directly on the application server (via port
908x), it will still work. Thus, in a clustered environment in which a load
balancer distributes load directly to Maximo JVMs without a web server in the
middle, this is the recommended choice.</span></li></ul></ul><ul style="text-align: left;"><li>The link to download an attachment is kept in
DOCINFO.WEBURL non-persistent field. The value of this field is generated by
replacing the URLNAME field of the DOCINFO record, with the rule set in the <b>mxe.doclink.pathXX</b>
property. </li></ul><p></p><blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><p class="MsoNormal" style="text-align: left;">For example, if you have the following rule: </p></blockquote><blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><p style="text-align: left;"><b>mxe.doclink.path01=
D:\DOCLINKS\=https://maximo.company.com/attachments/ </b></p></blockquote><blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><p style="text-align: left;">For a document with
<b>URLNAME=D:\DOCLINKS\drawing\file01.pdf</b>, the WEBURL would be <a href="https://maximo.company.com/attachments/drawing/file01.pdf" style="text-indent: -18pt;">https://maximo.company.com/attachments/drawing/file01.pdf</a><span style="text-indent: -18pt;">.</span></p></blockquote><p class="MsoListParagraph" style="margin-left: 25.5pt; mso-add-space: auto; mso-list: l0 level1 lfo1; text-indent: -18.0pt;"><o:p></o:p></p>
<blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><p class="MsoNormal" style="text-align: left;">If the first part of URLNAME doesn’t match with the
left-side of the PATH01 equation, Maximo will look at <b>mxe.doclink.path02</b>,
<b>path03</b> and so on to replace the value and generate a WEBURL.</p></blockquote><p class="MsoNormal"><o:p></o:p></p>
<blockquote style="border: none; margin: 0 0 0 40px; padding: 0px;"><p class="MsoNormal" style="text-align: left;">If there’s no match, it will generate WEBURL with an
absolute path: file://[URLNAME]. This means, if
the user has access to the file on the shared network path, it will still work without
the need of a web server.</p></blockquote><p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal">In summary, I usually hit with issues with doclinks after an
upgrade or migration, below are my check list to avoid those issues:</p><p class="MsoNormal" style="text-indent: 0px;"></p><ul style="text-align: left;"><li><span style="text-indent: -18pt;">If a new Web server is set up, need to update </span><b style="text-indent: -18pt;">DocumentRoot</b><span style="text-indent: -18pt;">,
and </span><b style="text-indent: -18pt;">Directory</b><span style="text-indent: -18pt;"> setting in the web server’s </span><b style="text-indent: -18pt;">httpd.conf</b><span style="text-indent: -18pt;"> file</span></li></ul><ul style="text-align: left;"><li>If the Web URL to access Maximo changes: need to
update <b>mxe.doclink.path01</b></li></ul><ul style="text-align: left;"><li>If the file server location moved:</li><ul><li>Need to copy the file to new location</li><li><span style="text-indent: -18pt;">Maximo service account should have permission to
access the local/network folder</span></li><li>Update the <span style="text-indent: -18pt;"> </span><b style="text-indent: -18pt;">mxe.doclink.doctypes.topLevelPaths</b><span style="text-indent: -18pt;">, </span><b style="text-indent: -18pt;">mxe.doclink.doctypes.defpath,
mxe.doclink.path01</b></li><li>Update the <b style="text-indent: -18pt;">DOCTYPES.DEFAULTFILEPATH</b><span style="text-indent: -18pt;"> in
the database with a REPLACE sql command</span></li><li>Update all <b style="text-indent: -18pt;">DOCINFO.URLNAME</b><span style="text-indent: -18pt;"> in the
database with a REPLACE sql command</span></li></ul></ul><p></p><p class="MsoListParagraphCxSpLast" style="margin-left: 61.5pt; mso-add-space: auto; mso-list: l0 level2 lfo1; text-indent: -18.0pt;"><o:p></o:p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-14414750362716581502022-07-31T08:15:00.000+10:002022-07-31T08:15:34.505+10:00How to test SMTP with PowerShell for setting up Maximo email notification<p>In an enterprise IT environment, it is sometimes difficult
to setup Maximo to talk with SMTP service due to networking and security restrictions.
To troubleshoot SMTP configuration, in the past, we can use Telnet from CMD
tool. However, in newer versions of Windows Server, Telnet is often not
installed by default. In such cases, we can use PowerShell to test and confirm
the SMTP and firewall setting is working before trying to configure SMTP in
Maximo.</p><p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal">The first thing I would do is checking whether firewall has
been opened so that the port used by SMTP service (e.g. port 25) is reachable
from the Maximo server. <span></span></p><a name='more'></a>I’ve provided details on how to do this with PowerShell
in this post. <a href="http://vietmaximo.blogspot.com/2020/12/check-networkfirewall-status-using.html">http://vietmaximo.blogspot.com/2020/12/check-networkfirewall-status-using.html</a><o:p></o:p><p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Once it is confirmed SMTP service is reachable, the next
step I would do is to ensure we can send email with the provided login details.
Thus, from PowerShell running on the Maximo server, I would do the following
commands:<o:p></o:p></p>
<br />
<script src="https://gist.github.com/viettranit/f31185fa2d498ce60aa6955af6282375.js"></script>
<br />
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Note that if SMTP service doesn’t require authentication, we
don’t need to run the first command and use the parameter <b>-Credential $cred</b> in
the second command.<o:p></o:p></p>
<p class="MsoNormal">Replace placeholder values with actual details required by
your SMTP service, and for the <b>-To</b> parameter, enter your own email address.<o:p></o:p></p>
<p class="MsoNormal">The <b>Send-MailMessage</b> command will send an email using your SMTP
service. If it works, you should receive an email.<o:p></o:p></p>
<p class="MsoNormal">Once SMTP service is confirmed working, I’ll go ahead
putting the details to System Properties in Maximo and that should be it.<o:p></o:p></p>
<p class="MsoNormal">To test that Maximo can send email, the simplest way I would
do is opening any work order, then use the <b>Select Action > Create >
Communication</b>, then entering my own email to the “To” address field. If it
works, once clicking on Send, it should show a successful status, and you’ll
receive an email within seconds. Otherwise, you might want to look at
SystemOut.log file to see if there’s any detailed of the error there.<o:p></o:p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-15407299572611717802022-06-12T09:47:00.005+10:002023-04-24T21:54:20.487+10:00Common issues when setting up Maximo with a Load Balancer<p><span style="font-family: inherit;">Just a couple of my own notes setting up Maximo with a Load Balancer
which I learnt through the hard way:</span></p><p></p><span style="font-family: inherit;"><span style="text-indent: -18pt;">- <b>Property</b></span><span style="text-indent: -18pt;"><b> </b></span><span style="text-indent: -18pt;"><b>mxe.system.useLoadBalancer</b> </span><span style="text-indent: -18pt;">–
should be set to 1. If not enabled, Maximo thinks the IP address of the Load Balancer’s
IP is the client’s and blocks it when the number of requests exceeds a certain
threshold (by default is 50 per 3 seconds). </span></span><div><span style="text-indent: -18pt;"><span style="font-family: inherit;"><br /></span></span></div><div><span style="font-family: inherit;"><span style="text-indent: -18pt;">For more details about the IP blocking
function, read this previous <a href="http://vietmaximo.blogspot.com/2019/07/maximo-76-feature-denial-of-service.html" target="_blank">post</a></span><span style="text-indent: -18pt;"><br /></span><span><a name='more'></a></span><span style="text-indent: -18pt;"><br /></span></span></div><div><span style="font-family: inherit;"><span style="text-indent: -18pt;"><b>- Load Balancer session timeout</b></span><span style="text-indent: -18pt;">: I recently
worked with a client who setup Citrix NetScaler as the LB for Maximo. Its default
session timeout is 2 minutes. A LB should have session stickiness, meaning it should
keep forwarding all requests from one user to the same Maximo application
server throughout the whole user’s session. When the LB session times out while
Maximo session is still active, a new request from the same user will be
directed to a new application server which doesn’t manage the current active
Maximo session, it forces the user back to the login screen. </span></span></div><div><span style="font-family: inherit;"><span style="text-indent: -18pt;"><br /></span></span></div><div><span style="font-family: inherit;"><span style="text-indent: -18pt;">To fix this, we need to increase the LB
session timeout to be more than the timeout setting in Maximo (by default, it’s
30 minutes). </span></span></div><div><span style="font-family: inherit;"><span style="text-indent: -18pt;"><br /></span></span></div><div><span style="font-family: inherit;"><span style="text-indent: -18pt;">Please note: if you have LDAP integration, the
LTPA token is a different login session maintained by Websphere and is not
related to this. LTPA token will expire after a fixed duration from logging in
regardless of whether the user is active or not. Thus, I usually set LTPA token
timeout to a very high value (e.g. 10-15 hours)</span><span style="text-indent: -18pt;"><br /></span><span style="text-indent: -18pt;"><span style="background: white; color: #323232;"><br /></span></span></span></div><div><span style="font-family: inherit;"><span style="text-indent: -18pt;"><span style="background: white; color: #323232;">- <b>Property mxe.int.webappurl</b>: </span></span><span style="background: white; color: #323232; text-indent: -18pt;">if
Maximo is only accessible via the LB’s URL, we should update the few webappurl
properties pointing to that URL. It could cause incorrect URL return in
integration responses, and BMXAA5798E when deploying Web Services or generating
XML schemas.</span><span style="text-indent: -18pt;">We can set the </span><span style="color: #323232; text-indent: -18pt;">mxe.int.verifywebappurl </span><span style="background-color: white; color: #323232; text-indent: -18pt;">to
0</span><span style="color: #323232; text-indent: -18pt;"> </span><span style="background-color: white; color: #323232; text-indent: -18pt;">to avoid the issue generating XML Schema</span></span></div><div><span style="font-family: inherit;"><span style="text-indent: -18pt;"><span style="color: #323232;"><br /></span></span><span style="color: #172b4d; text-indent: -18pt;">- <b>Websphere
rewriting port 80/443 to application server ports 9080/9443</b></span><span style="color: #172b4d; text-indent: -18pt;">: if Load
Balancer is setup to distribute load directly to the application servers
without the HTTP Webserver sitting in between, you can get this issue.</span></span></div><div><span style="background-color: white; color: #172b4d; font-family: inherit;"><br /></span></div><div><span style="background-color: white; color: #172b4d; font-family: inherit;">To fix this, for each app
server, add two Web Container custom properties below and set them to </span><strong style="background-color: white; color: #172b4d; font-family: inherit;">true:</strong><span style="background-color: white; color: #172b4d; font-family: inherit;"> <br /></span></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"></blockquote><ul style="text-align: left;"><li></li><li>com.ibm.ws.webcontainer.extractHostHeaderPort</li><li>Trusthostheaderport</li></ul><div><span style="color: #172b4d;"><b><span style="font-style: italic;">- </span>Missing message<span style="font-style: italic;"> </span>due Load Balancer's Maintenance Page: </b>during server patching, if Load Balancer is set to display a maintenance page, and if the HTTP Status of the page is 2xx, any integration message Maximo sent to the server via the LB shall be marked as successful while the target system never receives them. This can cause missing data in the target system</span></div><div><ol start="4" style="text-align: left;" type="1"><ol start="1" type="a">
</ol>
</ol></div>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-79754308682419311902022-05-20T11:25:00.003+10:002022-05-20T11:25:27.123+10:00Messaging engine cannot be started as there is no runtime initialized for it yet<p>I sometimes have issues with message engine not running. Usually I'll just try to restart the whole system and hope that it goes away.</p><p>If it doesn't work, in most cases, it is caused by a corrupted file store used by the message engine and the suggestion from the Internet is to delete these files, which seems to work fine.</p><p>Sometimes, with the message engine uses a database store, I had a very similar issue. I find it quite hard to find out the exact root cause. So I chose the easier path by simply deleting the whole message engine, create a new one, giving a new schema name for the data store. This ensures it creates new tables when message engine is initialized the first time. </p><p>Creating a new message engine and re-assigning bus destinations usually take less than 5 minutes, and it seems a lot easier than troubleshooting and finding the root cause of the issue.</p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-12454030563627854572022-05-20T11:03:00.007+10:002022-05-20T11:03:37.167+10:00JDBC Connection fails due to SQL Server allows TLS1.2 only<p>In a recent upgrade, I had to get Maximo 7.6.1.2 to work with SQL Server 2019 (15.0.4198.2 - Jan/2022). </p><p>Initially, I thought I needed to replace the JDBC driver that comes with Maximo with the latest JDBC driver version (10.2). However it doesn't solve the issue.</p><p>After searching the Web, I came across this <a href="https://techcommunity.microsoft.com/t5/sql-server-support-blog/jdbc-connections-fails-with-quot-the-driver-could-not-establish/ba-p/414157">page</a> which suggests the problem is due to SSL protocol TLS 1.2 is required.</p><p>Thus, I managed to fix the issue by adding this parameter to the end of the jdbc connections string: <b>sslProtocol=TLSv1.2;</b></p><p>The full connection string will look as follows:</p><p>mxe.db.url=jdbc:sqlserver://;serverName=[SERVERNAME];databaseName=[DBNAME];portNumber=1433;integratedSecurity=false;sendStringParametersAsUnicode=false;<b>sslProtocol=TLSv1.2</b>;</p><p><br /></p><p>The other method that seems to work is adding this parameter: <b>-Dcom.ibm.jsse2.overrideDefaultTLS=true</b> to the JVM argument of the Application server, or execution command of any tools running java. For example, for the integrityui.bat tool, I edit the file and update it as below:</p><p>@..\java\jre\bin\java -Dcom.ibm.jsse2.overrideDefaultTLS=true -Dswing.handleTopLevelPaint=false -classpath %MAXIMO_CLASSPATH% psdi.configure.UpgradeUI -i</p><p></p><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-30591542975750920672022-01-10T15:06:00.007+11:002022-08-19T12:45:47.880+10:00Redeploy a single web.xml file<p>Most Maximo settings or java code can be deployed by copy/pasting the file directly to the installed folder in Websphere without having to rebuild and redeploy the application. However, with <b>web.xml,</b> it doesn't work that way. Sometimes, we need to update this file to increase timeout setting or enable/disable integrated security mode</p><p>Sure, we can directly modify the file in Websphere without redeployment, but we will also have update the file in a few temporary folders for which, I find the process quite tedious.</p><p>Recently, my colleague told me we can just deploy the single web.xml file instead. Below is the process:</p><p>- Update the <b>web.xml</b> file with new settings</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEigkPzqF-7FbirF0Kq-4t0mV1dtY7q1lqpBworY6IanPm-_NssodQZozWa-rpNM_tcjSD7CPY4IO1SXbBkC42KXinTKzyyxo6p-gywK0_tu9ptYzRJPvU8Zuk2jG56bBe6UIga6CGmLPds7vK5Y2ZacJXcC1uGcKV8_vcy_ZaFVF_dcEjJxVjERVBpw=s1919" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="719" data-original-width="1919" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEigkPzqF-7FbirF0Kq-4t0mV1dtY7q1lqpBworY6IanPm-_NssodQZozWa-rpNM_tcjSD7CPY4IO1SXbBkC42KXinTKzyyxo6p-gywK0_tu9ptYzRJPvU8Zuk2jG56bBe6UIga6CGmLPds7vK5Y2ZacJXcC1uGcKV8_vcy_ZaFVF_dcEjJxVjERVBpw=w640-h240" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><span><a name='more'></a></span><p><br /></p><p>- Log in to Websphere console, open <b>Applications </b>> <b>Application Types > WebSphere enterprise applications: </b>select "MAXIMO" application by ticking on the checkbox next to it, click on <b>Update</b> button</p><p>- In "<b>Application update options</b>", select "<b>Replace or add a single file</b>" option</p><p>- In the textbox below "<b>Specify the relative path....</b>", specify: <span face=""Segoe UI", system-ui, "Apple Color Emoji", "Segoe UI Emoji", sans-serif" style="background-color: #e9eaf6; color: #242424; font-size: 14px; font-weight: bolder;">maximouiweb.war/WEB-INF/web.xml</span></p><p>- In the "<b>Specify the path to the file</b>", choose "<b>Local file system</b>", and click on "<b>Choose file</b>" to browse and select the updated <b>web.xml</b> file, click <b>Next. </b></p><p><b>- </b>Click OK on the next screen to deploy. Click Save when the deployment process is completed. Wait for a minute for the new settings to be propagated, then restart Maximo.</p><p><br /></p><span><!--more--></span><span><!--more--></span><span><!--more--></span><span><!--more--></span>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-20405334148634944032021-09-23T16:25:00.002+10:002021-09-23T16:32:15.346+10:00Post HTTP request using Automation script<p>I want to post a simple JSON message to an external system and do not want to add any external library to Maximo as it would require a restart. </p><p>In the past, I used the java HTTPClient library that comes with Maximo, but it would require half a page of boilerplate Jython code. Recently, I found a simpler solution below.</p><p></p><ul style="text-align: left;"><li>First I use WebHook as a mock service for testing. Go to webhook.site, it will give us a unique URL to send request to:</li></ul><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHPA6-i7cwebkPAncpFnSdVnod_tFb_2aLlqWwdTjKTsDSdbG1hIj_I9m-VbUd_ULNTcGVqbHPbf8Ao1PwPrKGMy21XSAdzN-2rliVztmPmbMWylEEMC9bGWiiyLps6ozAnZBTWa5ueqA/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="454" data-original-width="803" height="226" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHPA6-i7cwebkPAncpFnSdVnod_tFb_2aLlqWwdTjKTsDSdbG1hIj_I9m-VbUd_ULNTcGVqbHPbf8Ao1PwPrKGMy21XSAdzN-2rliVztmPmbMWylEEMC9bGWiiyLps6ozAnZBTWa5ueqA/w400-h226/image.png" width="400" /></a></div><br /><br /></div><span><a name='more'></a></span><ul style="text-align: left;"><li>Go to the "End Point" application in Maximo to create a HTTP Endpoint</li><ul><li>End Point Name: <b>ZZWEBHOOK</b></li><li>Handler: <b>HTTP</b></li><li>Headers<b>: Content-Type: application/json</b></li><li>URL: <copy/paste the Unique URL from webhook></li><li>HttpMethod: <b>POST</b></li></ul></ul><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxLOGIQd3a6-Yv0TbhwxijQpo_tLQjhNCMyTRM-YTrTVZRQKgOhuNDy8Aui30BVogpKy6Sx60ruZxDLDkcXhU-4sSs3PEJsgdGazF_bdApa85flzaxervOwM-_NgaQgvsGtZIGe5xIJxs/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="616" data-original-width="1174" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxLOGIQd3a6-Yv0TbhwxijQpo_tLQjhNCMyTRM-YTrTVZRQKgOhuNDy8Aui30BVogpKy6Sx60ruZxDLDkcXhU-4sSs3PEJsgdGazF_bdApa85flzaxervOwM-_NgaQgvsGtZIGe5xIJxs/w400-h210/image.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><ul><li style="text-align: left;">To ensure it's working, Use the Test button, write some simple text, then click on "TEST". Maximo should show a successful result. In Webhook, it should also show a new request received. (Note: if it gives an error related to SSL, it's because Websphere doesn't trust the target website. You'll need to add the certificate of the target website to Websphere trust store)</li></ul><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivmcnPkMEIOrNnjyC1ur2vJ0V1eVSdvMjFAAFbMkHYIKUiR14-WJtKpwxAKsEJnKO2goGTbZztlUl2g0GPiL8o5tT7Fp87LneG6c2rBZCSPTowOxY87ccgoDvw5-ao13ErQZbu7a6hunY/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="408" data-original-width="1111" height="148" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivmcnPkMEIOrNnjyC1ur2vJ0V1eVSdvMjFAAFbMkHYIKUiR14-WJtKpwxAKsEJnKO2goGTbZztlUl2g0GPiL8o5tT7Fp87LneG6c2rBZCSPTowOxY87ccgoDvw5-ao13ErQZbu7a6hunY/w400-h148/image.png" width="400" /></a><br /><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><ul><li style="text-align: left;">To send a request, just need two lines of code as follows:</li></ul><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUontLANWB9SYm_u4_yUOcwD9OXQF-Fih08u0WrnXZDi8N7P0bq9wGfO1K4oSXP6E7Q79kRT7NiAP0lREDXPva5F4pZO-KGOs5mivPJexWHkvW4-4PmdqdAsDxVAQEN61DRrwPYoehje8/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="60" data-original-width="493" height="39" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUontLANWB9SYm_u4_yUOcwD9OXQF-Fih08u0WrnXZDi8N7P0bq9wGfO1K4oSXP6E7Q79kRT7NiAP0lREDXPva5F4pZO-KGOs5mivPJexWHkvW4-4PmdqdAsDxVAQEN61DRrwPYoehje8/" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Happy coding!</div><br /><br /></div><br /><br /></div></div><br /><br /></div><div style="text-align: left;"><br /></div></div><br /><br /></div><br /><br /></div><br /><p></p><p><br /></p><p><br /></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com7tag:blogger.com,1999:blog-2386943267680155685.post-64961885907809599512021-09-03T12:59:00.004+10:002022-07-31T08:02:24.311+10:00Node Agent cannot start after configuring LDAP<p>Just to document a weird issue I had today. I was attempting to configure LDAP (Microsoft AD) for Maximo/Websphere. After it is configured (and I've tested that it can query AD data), Application Server security was enabled. Then the server is rebooted to refresh the new configuration.</p><span><a name='more'></a></span><p><br /></p><p>After a restart, ctgNode01 (node agent) service cannot start. Node Agent log shows the error below:</p><p><span style="background-color: #cccccc;">WSVR0100W: An error occurred initializing, nodeagent [class com.ibm.ws.runtime.component.ServerImpl]</span></p><p><span style="background-color: #cccccc;">com.ibm.ws.exception.ConfigurationError: com.ibm.websphere.ssl.SSLException: CWPKI0316E: Cannot get a security object from the configuration. This can indicate that the security.xml file for the cell is corrupt and you must validate the integrity of the file.</span></p><p><br /></p><p>After some googling around, there's some suggestion about a corrupted <b>security.xml</b> file. So I checked and found that the security.xml file in <b>ctgAppSrv01 </b>profile is completely empty. I looked at other Maximo servers and found this file has an exact same content with the security.xml file from <b>ctgDmgr01 </b>profile. I copied that file over to AppSrv profile, restarted Websphere and it is able to start again.</p><p><br /></p><p>Not sure why configuring LDAP would completely wipe out the content of this file.</p><p><br /></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-84112760720230414372021-08-06T16:29:00.006+10:002021-08-06T16:29:52.583+10:00 Enabling up HTTP Compression for Maximo<p>To enable HTTP compression for Maximo, follow the steps below:</p><p></p><ul style="text-align: left;"><li>Stop HTTP server</li><li>Make a backup copy of <b>\IBM\HTTPServer\conf\httpd.conf</b></li><li>Edit httpd.conf:</li><ul><li>Enable the following two lines:<span><a name='more'></a></span></li></ul></ul><div><span> </span><span> </span><span> </span>LoadModule deflate_module modules/mod_deflate.so</div><div><span> </span><span> </span><span> </span>LoadModule filter_module modules/mod_filter.so</div><div><br /></div><div><ul style="text-align: left;"><ul><li>Add the following lines to the bottom of the file:</li></ul></ul><div><div><span> </span><span> </span><span> </span>AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript</div><div><br /></div><div><span> </span><span> </span><span> </span>BrowserMatch ^Mozilla/4 gzip-only-text/html</div><div><span> </span><span> </span><span> </span>BrowserMatch ^Mozilla/4\.0[678] no-gzip</div><div><span> </span><span> </span><span> </span>BrowserMatch \bMSIE !no-gzip !gzip-only-text/html</div><div><span> </span><span> </span><span> </span>#Highest 9 - Lowest 1</div><div><span> </span><span> </span><span> </span>DeflateCompressionLevel 4</div></div></div><div><ul style="text-align: left;"><li>Start HTTP server</li></ul></div><p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-50409985857554043122021-04-15T23:56:00.004+10:002021-04-15T23:58:57.512+10:00Implement "Sleep" or "Wait" in WebMethods flow<p>I needed to send an external system a file import request. The external system would take some time to process the file before the import result can be queried. Making a status query immediately after the import request would always return an "import process is still running". It's best to wait for a few seconds before making the first attempt to query the import status.</p><p>It took quite a bit of time to look up the web for a "wait" or "sleep" function. Some posts suggested using Java flow, some recommended complex processes or involved an external library.</p><p></p><p>The easiest method I finally settled with is to use Repeat as follows:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrB4iNsk9qId3T7K3PB-FOjZQIZ1bTB16C0KNDJ999-DioGo-P3QiJqpcF2mJHDY14KXzXgLQF40rmwWeXN_fZxAiYp44Wywj86AOz_fIQEv2bC2DCAJK-8OIP5u3n9AQP0XWmYCTJz_U/s1044/Sleep.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="381" data-original-width="1044" height="234" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrB4iNsk9qId3T7K3PB-FOjZQIZ1bTB16C0KNDJ999-DioGo-P3QiJqpcF2mJHDY14KXzXgLQF40rmwWeXN_fZxAiYp44Wywj86AOz_fIQEv2bC2DCAJK-8OIP5u3n9AQP0XWmYCTJz_U/w640-h234/Sleep.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: left;">Essentially, the flow would repeat 1 time in 5 seconds before getting to the next step (Main Mapping). The repeat loop does nothing other than just writing a line in the server log to make troubleshooting a bit easier.</div><span></span>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-30675724960483337772021-04-04T22:37:00.004+10:002021-04-04T22:37:42.986+10:00The fun (and pain) of Kronos Integration<p><span style="text-align: justify;">One of our clients undertook a
massive IT transformation program which involved switching to a new financial
management system, upgrading and rebuilding a plethora of interfaces among
several systems, both internal and external to the business. Kronos was chosen
to replace an old timesheet software and there was the need to integrate it
with other systems such as Maximo and TechnologyOne. WebMethods was used as the
integration tool for this IT ecosystem. This is my first experience with
Kronos. The project took almost two years to finish. As always, when dealing
with something new, I had quite a bit of fun (and pain) during this project. As
it is approaching the final stage now, I think I should write down what I’ve
learnt. Hopefully, it will be useful for people out there who’re doing a
similar task.</span></p><p class="MsoNormal" style="text-align: justify;"><o:p></o:p></p>
<p class="MsoNormal"><b>REST API</b>: Kronos provides a pretty good reference
source for the REST API at this <a href="https://secure.workforceready.com.au/ta/docs/rest/public/">Link</a>. REST
API theoretically offers the advantage of supporting real-time integration and
enables seamless workflow. However, we don’t have such a requirement in this
project. On the other hand, this has two major limitations.<span></span></p><a name='more'></a> One is the API throttling
limit, which essentially restricts the number of calls you can make based on the
license purchased. The other limitation is, it is obvious to me that the API
was built for internal use of the application. It is not meant to be used for
external integration. No one told us about this when we first started. As a
result, we hit several major obstacles along the way. For example, most API
calls will need to be method-specific. Cost Center requests need to be either
Create New, Update, or Move; there is no Sync or Merge operation. The Update
and Move requests will accept Kronos’ internal ID only. When sending an update or move
request, we need to send another request first to retrieve the internal ID of a
record. Cost Center is a simple master data structure with a few fields,
however, to get it to work, we had to build some complex logic to query Kronos
to determine whether the record exists (and whether the parent exists) to send
in the appropriate create new/update/move calls. The complexity is taken to
another level as we needed to build caching mechanism to pre-load and refresh
the data at a suitable time so that the number of requests sent to Kronos is
kept at minimal. For a more complex data structure such as the Employee master
data, if we use the REST API, it is impossible to build an interface that is
robust enough for a large scale, high volume environment. I felt like we had to
build the whole application layer in WebMethods to handle all sort logic,
scenarios, and possible exceptions that could occur. The process to create a
new employee record can result in more than a dozen different requests to check
existing data and lookup internal Id of different profiles (security, schedule,
timesheet, holiday, pay calculation profiles etc.), then send in the Create New/Update
requests in the correct order, ensure proper handling of exception and roll
back if one request fails due to various reasons.<o:p></o:p><p></p><p class="MsoNormal"><br /></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNHZdKxbUs9DVYvZLvA_Lupn3y8bV-Wy02MesvJy889oUcl0RjU83A9-t2Er6WRenV2iXMdteRghN3sKgCnCOE6xRf-wVai3kmEQixn75gkiMLjqcmoWZXPaUDn3_2M_VaLrKl75onCxU/s1128/Kronos+API+Limit.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="907" data-original-width="1128" height="514" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNHZdKxbUs9DVYvZLvA_Lupn3y8bV-Wy02MesvJy889oUcl0RjU83A9-t2Er6WRenV2iXMdteRghN3sKgCnCOE6xRf-wVai3kmEQixn75gkiMLjqcmoWZXPaUDn3_2M_VaLrKl75onCxU/w640-h514/Kronos+API+Limit.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">REST API reference guide (This page shows the API Throttling limits)</td></tr></tbody></table><br /><p class="MsoNormal"><b>Report API</b>: Kronos has a REST API to execute reports
(both out-of-the-box and customized). This is useful to alleviate some of the
problems with the API throttling limit. For example, we have an interface to
send organisation hierarchy (departments and job positions) to Kronos as Cost Centers.
The source system would periodically export its whole data set to a CSV file,
and we’ll need to query Kronos to determine if the record exists to either send
a Create or an Update request, and if the record has new changes to send in update/move
requests. We used the Report API to retrieve the whole set of Cost Center data
in one single call rather than having to make thousands of individual cost centre
detail requests.</p><p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal"><b>Import API</b>: This turned out to the best way to send
data to Kronos. We learnt it the hard way. The import API still has a few minor
limitations such as some APIs uses description to identify a record instead of
an ID, or documentation sometimes is not accurate. Overall, this provides the
capability to bulk upload, auto translating ID, and runs on “merge” operation
(i.e. automatically decide to create new or update depends on whether a record already
exists or not). Since this is an asynchronous operation, and the time it takes
to process inbound data depends on the volume, there is a need to build a custom
response handler to request (and retry) Kronos later to retrieve the status of
the import job and handle success/failure. This custom response handling takes
some extra effort to build, but it can be reused for different import
endpoints. With the Employee interface mentioned earlier, at a point, it became
way too complex and a maintenance nightmare. We had to rebuild it from scratch
using the Import API and we’re glad that we did. It was greatly simplified, and
we are now very confident of its robustness.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p><p class="MsoNormal"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS5K2lEPM7T5itZaBLy0AzIgX58eZYcJ24x7nzXmpzQDm4tNWKrQUGL0LTXQiWtIjd_h6FXM8EbKtb7o2j92MzlWISToBlGcDtGDbqpwKURqAOR-uW1YXGAZQpOFZZLvOkgnSE0FfoMbU/s1343/Kronos+Import+API.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="792" data-original-width="1343" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS5K2lEPM7T5itZaBLy0AzIgX58eZYcJ24x7nzXmpzQDm4tNWKrQUGL0LTXQiWtIjd_h6FXM8EbKtb7o2j92MzlWISToBlGcDtGDbqpwKURqAOR-uW1YXGAZQpOFZZLvOkgnSE0FfoMbU/w640-h378/Kronos+Import+API.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">List of Import APIs which can be seen after logged in to Kronos</td></tr></tbody></table><br /><o:p><br /></o:p></p>
<p class="MsoNormal">To conclude, if I have to build a new Kronos interface now,
for retrieving data from Kronos and sending it to an external system, I will
start with using reports to identify new changes, then use the REST API to
retrieve details of individual records if necessary. To send data to Kronos, I
would look at the Import API first, and will only go for the REST API if the
Import API cannot do what I want to do and only if the request is very simple
and low volume.<span style="mso-spacerun: yes;"> </span><span style="mso-spacerun: yes;"> </span><o:p></o:p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-82075484534506987412021-03-14T11:43:00.005+11:002021-03-14T11:44:47.596+11:00WebMethods: Evaluate String IN and CONTAINS operator<p>In WebMethods, the most basic way to write a string “IN”
operator is to use Branch as follows:</p><p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal"></p><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp4HIgzNmNwICVcLo5s_15Pf-SurgTrSKPmq96oNbsm4EsE5OO32MBJ2yg4fPSlJCI4nlwE2P1DXCaiwiBJ3J-XLH_jdJ9VaFEdMyYPJ-ymREaeziwIiMpeZT302aCTlbIdYS7F2p10m8/s363/Basic+1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="98" data-original-width="363" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp4HIgzNmNwICVcLo5s_15Pf-SurgTrSKPmq96oNbsm4EsE5OO32MBJ2yg4fPSlJCI4nlwE2P1DXCaiwiBJ3J-XLH_jdJ9VaFEdMyYPJ-ymREaeziwIiMpeZT302aCTlbIdYS7F2p10m8/s16000/Basic+1.png" /></a></div><br /></div>Another way to reduce the number of lines of code is by combining
the conditions using “OR”:<o:p></o:p><p></p>
<p class="MsoNormal"></p><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh29TnyF39RudCsFBrVpZ-xBpTioZGui5Pc6aTp8BlIXtDIefEMX6zm9_yyaZ3uxzjgh3U-CV50DBcAnX6UzdgjOkaFzBnQtpy4d7RWF5W5RwNmCvc2yggWx1oeP0sI60xtoI1VH7Conus/s424/Basic+2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="64" data-original-width="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh29TnyF39RudCsFBrVpZ-xBpTioZGui5Pc6aTp8BlIXtDIefEMX6zm9_yyaZ3uxzjgh3U-CV50DBcAnX6UzdgjOkaFzBnQtpy4d7RWF5W5RwNmCvc2yggWx1oeP0sI60xtoI1VH7Conus/s16000/Basic+2.png" /></a><span><a name='more'></a></span></div><div style="text-align: justify;">These approaches works well if the number of options is
small or if the variable name is short. If there are more than a few options,
or in the case of a very long variable name, the code will look very messy, and thus, difficult to maintain. For example: </div><div style="text-align: justify;">$work_order.maximo/ns:PublishZZWORKORDER/ns:ZZWORKORDERSet/ns:WORKORDER[0]/ns:STATUS/*body</div><div style="text-align: justify;"><span style="text-align: left;"><br /></span></div><div style="text-align: justify;"><span style="text-align: left;">Using regular expression can simplify the code:</span></div><p></p><p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal">To implement the string “<b>IN</b>” logic, we can use <b>/^value$/</b>.
For example, the code below would evaluate to true if $input is exactly <b>one</b>
or <b>two</b> or <b>three</b></p><p class="MsoNormal"></p><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2KDHFNXZVAaJhxr5qQysVXZxv11mbuiJtGmKFpOOuw-flH6bUb5DrXdm-GzFztseutmmf2tvibaNhNTc-IlUvX8HE0u-q8CbCDTt3FJy1GPRBRIbQq7UY8tLCejnxGLHO0u89ssFzqeQ/s446/string+IN.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="61" data-original-width="446" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2KDHFNXZVAaJhxr5qQysVXZxv11mbuiJtGmKFpOOuw-flH6bUb5DrXdm-GzFztseutmmf2tvibaNhNTc-IlUvX8HE0u-q8CbCDTt3FJy1GPRBRIbQq7UY8tLCejnxGLHO0u89ssFzqeQ/s16000/string+IN.png" /></a></div><b><br /></b><o:p></o:p><p></p>
<p class="MsoNormal" style="text-align: justify;">To implement the string “<b>CONTAINS</b>” logic, we can use <b>/value/</b>.
For example, the below would return <b>true</b> if $input string contains <b>one:</b></p><p class="MsoNormal"></p><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOXZ8MvjV4VPYceNeaYlNcejLxgPQra4wHdAxnd_cirXJArVjVtD7hR9jWjrrJzoSZVJZr1oN5H8D-XUX3dd4e-rIGdcj5eGZNtT6RQ_7nlsb0z7tDptzFNjx7dbvNcNKjDFHYr0cTE0E/s352/string+CONTAINS.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="59" data-original-width="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOXZ8MvjV4VPYceNeaYlNcejLxgPQra4wHdAxnd_cirXJArVjVtD7hR9jWjrrJzoSZVJZr1oN5H8D-XUX3dd4e-rIGdcj5eGZNtT6RQ_7nlsb0z7tDptzFNjx7dbvNcNKjDFHYr0cTE0E/s16000/string+CONTAINS.png" /></a></div><p></p>
<p class="MsoNormal">With Regex, it is also simple to check if the variable
contains one of several values:</p><p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal"></p><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTtkZ8XjrV2EDAxA1FuoS3cJd1XZZf1sGo4RUdfBmuXo9jEYJXx2l9EBam77EZyy71NgO-Rp_3CMeFzq4JoSnsWJ_HBjotyBZFFtiQMVI9w158hz7LWMVlJa_qWhK9Jx9Shi2znRSHy4E/s492/Contains+one+of+several+values.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="64" data-original-width="492" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTtkZ8XjrV2EDAxA1FuoS3cJd1XZZf1sGo4RUdfBmuXo9jEYJXx2l9EBam77EZyy71NgO-Rp_3CMeFzq4JoSnsWJ_HBjotyBZFFtiQMVI9w158hz7LWMVlJa_qWhK9Jx9Shi2znRSHy4E/s16000/Contains+one+of+several+values.png" /></a></div><p></p><p class="MsoNormal"><br /></p><div class="separator" style="clear: both; text-align: justify;"><span style="text-align: left;">My favourite Martin Fowler’s quote is</span><span face="Arial, sans-serif" style="background: white; color: #202124; text-align: left;"> “</span><span style="text-align: left;">Any fool can
write code that a computer can understand</span><span style="text-align: left;">. Good programmers </span><span style="text-align: left;">write code</span><span style="text-align: left;"> that humans </span><span style="text-align: left;">can understand</span><span style="text-align: left;">”</span><span style="text-align: left;">. This trick helps me to keep most of my code fits in
a single screen.</span></div><o:p></o:p><p></p>
<p class="MsoNormal">Happy coding. <o:p></o:p></p><br /><br /><br />Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-40096087797377435222021-02-09T23:28:00.001+11:002021-02-09T23:28:35.500+11:00Troubleshoot integration issues when Maximo stops publishing data to external system.<p>I had to deal with this quite often. Most of the times, I
got it right and able to identify the problem quickly. In a few cases, it took
some time, and usually very stressful as it mostly occurs in Production. (It occurs
in DEV and PRE-PROD all the time, it’s just that people usually don’t care, and
it goes unnoticed)</p><p>Today I had to deal with it again and it took me some time.
The cause was something I dealt with before, was told by a colleague on how to
fix it (the easy way), but I forgot. This time around, under panic mode, I
restarted a few JVMs before I remembered I should ask around and was reminded
by my colleague again that it could be fixed with much less damage. I told
myself I should write it down for the next time, so here is the sum of what I
learned:<span></span></p><a name='more'></a><p></p><p class="MsoNoSpacing"><o:p></o:p></p>
<p class="MsoNoSpacing"></p><ul style="text-align: left;"><li><span style="text-indent: -18pt;">Publish channel in Maximo uses Sequential Queue
by default. When a message failed, it will stop processing other messages.</span></li></ul><ul style="text-align: left;"><li><span style="text-indent: -18pt;">I saw configured in some systems, the behaviour
(Sequential processing) can be disabled by simply change the error output of <b>SQOUTBD
</b>bus destination from “<b>none</b>” to “<b>system</b>” or an error queue.</span></li></ul><ul style="text-align: left;"><li><span style="text-indent: -18pt;">Check Message Tracking to see if there are
messages stuck in “<b>RECEIVED</b>” status, if there are many of those messages. it
means <b>JMSConsumer </b>doesn’t run, or it does run but one message failed (has <b>ERROR
</b>status in Message Reprocessing), and thus everything else got stuck. If there
is no message in “<b>RECEIVED</b>” status, it is either because Publish Channel is
not enabled, or because Message Tracking is not enabled.</span></li></ul><ul style="text-align: left;"><li><span style="text-indent: -18pt;">For Publish Channel to publish messages, we need
both the External System and the Publish Channel to be enabled. Event Listener
on the Publish Channel should be enabled (unless it is triggered by something
else like an Autoscript)</span></li></ul><ul style="text-align: left;"><li><span style="text-indent: -18pt;">If Message Tracking is not enabled for the
Publish Channel, we should enable it (now), unless the interface is extremely
unimportant, and no one cares.</span></li></ul><ul style="text-align: left;"><li><span style="text-indent: -18pt;">If there are a ton of “<b>RECEIVED</b>” messages in
Message Tracking, it’s likely due to two reasons noted below. Messages that get
published successfully have “<b>PROCESSED</b>” status.</span></li></ul><ul style="text-align: left;"><li><span style="text-indent: -18pt;">If there’s an error in <b>Message Reprocessing</b> that
blocks the queue, try to re-process, or delete it to clear the blockage.</span></li></ul><ul style="text-align: left;"><li><span style="text-indent: -18pt;">If there’s no blockage in Message Reprocessing, it’s
likely due to <b>JmsConsumer </b>cron task doesn’t run. Try re-activate/re-load the
cron instance. Make sure to enable “<b>Keep History?</b>” flag. After re-activating
and reloading the cron instance. If it shows “<b>START</b>”, but doesn’t show
“<b>ACTION</b>”, it means the Cron instance doesn’t run. It’s likely there’s a hang scheduler
task. It can be resolved by restarting the concerning JVM/server. This is the bad approach
used today. The easy way is to query and delete the task entry in “<b>TASKSCHEDULER</b>”
table. Don’t worry, once deleted, the cron task instance will create a new task
entry on the next run.</span></li></ul><ul style="text-align: left;"><li><span style="text-indent: -18pt;">For blockage on sequential queue, on non-prod
environment, we can see queue depth and clear all of those messages in the
queue to clear blockage using two methods below:</span></li></ul><ul style="text-align: left;"><ul><li>In
Maximo, go to <b>External Systems</b> > Action <b>Add/Modify Queues</b> > Select “<b>sqout</b>”
> choose <b>View </b>(or <b>Delete Queue Data</b>)</li></ul></ul><ul style="text-align: left;"><ul><li>In
<b>Websphere</b>, go to <b>Service Integration</b> > <b>Buses</b> > <b>Destinations </b>> <b>SQOUTBD
</b>> <b>Queue Points</b>. It will show<b> Queue Depth</b> which is the number of messages in
the queue. Click on the link to open > <b>Runtime </b>tab > <b>Messages </b>> <b>Delete
</b>or <b>Delete All</b></li></ul></ul><p></p><p class="MsoNoSpacing" style="margin-left: 72.0pt; mso-list: l0 level2 lfo1; text-indent: -18.0pt;"><o:p></o:p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-83406110758379530842021-02-01T23:01:00.001+11:002021-02-01T23:03:36.641+11:00Setting up alarms for integration<p> <span style="text-align: justify;">When writing a piece of software,
we are in total control of the quality of the product. With integration, many
elements are not under our control. Network and firewall are usually managed by
IT. With external systems, we usually don’t know how they work, or many times, not
given access. Yet, any changes to these elements can cause our interfaces to
fail.</span></p><p class="MsoNormal" style="text-align: justify;"><o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">For synchronous interfaces, the
user would receive instant feedback after each action is taken (e.g. Maximo
- GIS integration), thus, we don’t usually need to setup alarms. For
asynchronous interfaces, which usually run in the background, and don’t give instant
feedback, when failure occurs, it usually goes unnoticed. In many cases, we
only find out about failures after it has caused some major damage.<o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">A good interface must provide
adequate mechanism to handle failures, and in the case of async integration,
proper alarms and reports should be setup so that failures are captured and
handled proactively by IT and application administrators.<span></span></p><a name='more'></a><o:p></o:p><p></p>
<p class="MsoNormal" style="text-align: justify;">On the one hand, it is bad to
have no monitoring. On the other hand, it
is even worse to have too many alarms to a point that people completely ignore
everything including the critical issues. This is usually seen in larger organisations. Many of readers of this blog probably
won’t be surprised when they open the Message Reprocessing app of the Maximo system they manage and find thousands of unprocessed errors in there. It’s likely that those issues
have been accumulated and not dealt with for years.</p><p class="MsoNormal" style="text-align: justify;"><o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">It is hard to create a perfect
design from day one and build an interface that works smoothly after the first
release. There are many different kinds of problem an external system can throw at us and it is
not easy to envision all possible failure modes. As such, we should expect and plan for an intensive
monitoring and stabilizing period of a few days to one or two weeks after the first release.
<o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">As a rule of thumb, an interface
should always be monitored and raise alarms when a failure occurs. It
should also provide a mechanism to resubmit/reprocess a failed message. More
importantly, there shouldn’t be more than a few alarms raised per day on
average from each interface, no matter how critical and high volume the
integration is. More than that, it will become too noisy and people will start
ignoring those alarms. If an interface raises more than a few alarms a day, there
must be some recurring patterns and each of them must be treated as a systemic
issue. The software should be rebuilt or updated to handle these recurring
issues.<o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">It is easier said than done, and
every interface is a continuous learning and improvement process to me. Below
are some examples of the interfaces I built or dealt with recently. I hope you
find it entertaining to read.<o:p></o:p></p><p class="MsoNormal" style="text-align: justify;"><br /></p>
<p class="MsoNormal" style="text-align: justify;"><b>Case #1: Integration of Toll
Point equipment and maintenance to Maximo<o:p></o:p></b></p>
<p class="MsoNormal" style="text-align: justify;">An infrastructure construction company
built and is now operating a freeway in Sydney. Maximo is used to manage
maintenance activities, mainly on civil infrastructure. Toll point equipment
and traffic monitoring system was provided by an external provider (Kapsch). Device
status and maintenance work from this system are exported daily as CSV files
and sent to Maximo via SFTP. <span style="mso-spacerun: yes;"> </span>On Maximo
side, the CSV files are imported using a few automation scripts triggered by a
cron task.<o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">The main goal of the interface is
to maintain a consolidated database of all assets and maintenance activities in
Maximo. It is a non-critical integration because even if it stopped working for
a day or two, it won’t cause a business disruption. However, occasionally,
Kapsch would stop exporting CSV files for various reasons. The problem will
only be found out after a while, like when end-of-month report is produced or
when someone tries to lookup for the status of certain work order which was not
created via the interface. Since we don’t have any access or visibility to the
traffic monitoring system managed by Kaspch, we’ll need to build the monitoring
and alarms in Maximo. <o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">The difficulty is, when the
interface on Kapsch’s side fails, it doesn’t send Maximo anything, there would
be no import, and thus no errors or faults seen by Maximo to raise any alarm.
The solution we came up with is having a custom logging table that we write
each import as an entry with some basic statistics including import start time,
end time, total records processed and the number of records that failed. The
statistics are displayed on Start Center. <o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">For alarm, since this integration
is non-critical, an escalation is set to monitor whether there has been no new
import within the last 24 hours, Maximo will send out an email to me and the people
involved. There are actually a few different interfaces in this integration,
such as for device list and preventive maintenance work coming from TrafficCom,
or corrective work on faults coming from JIRA. Thus, sometimes, when a system
stopped running for various planned or unplanned reasons, I would receive
multiple emails for a couple of days in a row, which is too much. So, I tweaked
it even further by <span style="mso-spacerun: yes;"> </span>sending only one
email on the first day if one or more interfaces stopped working, and another
email reminding me a week later if the issue has not been rectified. After the
initial fine-tuning period, support team on Kapsch and Maximo side is added to
the recipient list, and after almost two years now, the integration has been
running satisfactorily. In other words, there has been a few times files are
not received on Maximo side and the support people involved were always
informed and able to take corrective action in a timely manner before the end-users
can notice.<o:p></o:p></p><p class="MsoNormal" style="text-align: justify;"><br /></p>
<p class="MsoNormal" style="text-align: justify;"><b>Case #2: Integration of CRM
and Maximo<o:p></o:p></b></p>
<p class="MsoNormal" style="text-align: justify;">A water utility in Queensland
uses Maximo for managing infrastructure asset, tracking, and dispatching work
to field crews. When a customer calls up requesting a new connection or
reporting a problem, the details are entered to a CRM system by the company’s call
centre. The request will then be sent to Maximo as a new SR, then turned into
work orders. When the work order is scheduled and a crew has been dispatched,
these status updates are sent back to CRM. At any time, if the customer calls
up to check on the status of the request, call centre should be able to provide
an answer by looking up the details of the ticket in CRM only. Certain types of
problem have high priority such as major leak or water quality issues. Some
issues have SLA with response time in minutes. As such, this integration is
highly critical. <o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">WebMethods is used as a middleware
to handle this integration, and as part of the steps sending new SR from CRM to
Maximo, service address will also need to be cross-checked with ArcGIS for
verification and standardization. As you can see, there are multiple points of
failure with this integration.<o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">This integration was built
several years ago and there has been some level of alarms setup in CRM on a few
points where there is high risk of failure such as when a Service Order is
created but not picked up by WebMehods or picked-up but not sent to Maximo.
Despite this, the interface would have some issues every few weeks, and thus,
needed to be rebuilt. In addition to existing alarms coming from CRM, several
new alarm points were added in Maximo and Webmethods:</p><p class="MsoNormal" style="text-align: justify;"></p><ul><li><span style="text-indent: -18pt;">When WM couldn’t talk with CRM to retrieve new
Service Order</span></li><li>When WM couldn’t send status update back to CRM</li><li>When WM couldn’t talk to Maximo</li><li>When Maximo couldn’t publish messages to WM</li></ul><p></p><p class="MsoListParagraphCxSpLast" style="mso-list: l0 level1 lfo1; text-align: justify; text-indent: -18pt;"><o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">These apply to individual
messages coming in and out of Maximo and CRM and any failure would result in an
email sent to the developer and the support team. <o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">On the first few days after this
new interface was released to Production, the team received a few hundred
alarms each day. My capacity to troubleshoot was about a dozen of those alarms
a day. Thus, instead of trying to solve them. We tried to identify all
recurring patterns of issues and address them by modifying the interface
design, business process, or fixing bad data. A great dealt of time was also
spent on trying to improve the alarms, such as for each type of issue, detailed
error messages, or in many cases, the content of the XML message itself is
attached to the email alarm. A new “fix patch” was released to Production about
two weeks after the first release, and after that, the integration only
produced a few alarms per month. In most cases, the support person can
immediately tell what is the cause of the problem by just looking at the email
before even log in to the client’s environment. After almost a year now, all of
the possible failure points that we envision, no matter how low of a chance it can
occur, has failed and raised alarms at least once, and the support team has
always been on top of it. I’m glad that we had put in all of those monitoring
in the first place. And as a result, I haven’t heard of any issues that not
been fixed before the end-users become aware of it.<o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;"><o:p> </o:p></p>
<p class="MsoNormal" style="text-align: justify;"><b>Case #3</b>: <b>Interface with
medium criticality/frequency<o:p></o:p></b></p>
<p class="MsoNormal" style="text-align: justify;">Of the two examples above, one is
low frequency/low criticality; the other is high frequency and highly critical.
Most interfaces are somewhere in the middle. Those interfaces that are highly
critical but don’t run frequently or don’t need short response time can also be
put into this category. In such cases, we might not need to send individual
alarms in real-time. Although I like to think of myself as pretty good at troubleshooting
issues, I don’t think I can handle more than a few issues per day. As such, my
rule of thumb is, if I receive more than a few alarms per day, it is too much. As
developers, if we don’t think we can handle more than a few alarms a day, I
think we shouldn’t to that to support team (giving them alarms all day long).
For the utility company mentioned above, when WebMethods was first deployed,
the WM developer has configured a bi-daily report that lists all failed
transactions occurred in the last 12 hours. Thus, for most interfaces, we don’t
need to setup any specific alarms. If there were a few failures, they will show
up in the report and will be looked at by technical support at noon or at the
end of the day. This appears to work really well, even for some of the very
critical interface such as bank transfer order or invoice payment.<o:p></o:p></p><p class="MsoNormal" style="text-align: justify;"><br /></p>
<p class="MsoNormal" style="text-align: justify;"><b>Case #4: Recurring failure
resulting in too much alarm<o:p></o:p></b></p>
<p class="MsoNormal" style="text-align: justify;">For the integration mentioned in
#1 and #2, the key to get them work satisfactorily is to spend some time after
the first release to monitor the interfaces and fine-tune both the interface
itself and the alarms. It is important to have alarms raised when failure
occurs, but it is also important to ensure there aren’t too many alarms raised.
Not only that people will ignore it if they receive too much alarm, <span style="mso-spacerun: yes;"> </span>it also makes it hard to tell the critical
issues apart from other noisy less important ones. From my experience, dealing
with those noisy alarms are usually quite easy. Most of the time, the alarms
come from a few recurring failures that are ignored. When people first look at
it, they can easily be overwhelmed by the high number of issues they see
initially, and thus, feel reluctant from dealing with it. In many cases, I
simply deal with each alarm/failure one by one, and carefully document the
error message or symptom, and the solution for each problem on an Excel
spreadsheet. Usually, after a few I’ve gone through a few issues, they would
all comeback to some recurring patterns that can easily be dealt with. Below is
an example:<o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;">A water utility uses an external
asset register system, and the asset data is synchronized to Maximo in a near
real-time frequency. The interface produces almost 1GB of SystemOut.log file
each day causing the logging system to become useless. I looked each error and
document them one by one. After about two hours, it was clear that 80% of the
errors come from missing locations which were not synchronized. When the
integration tries to create a new asset under these locations, it will write a
bunch errors to SystemOut.log. I did a quick scan and wrote down all of the
missing locations and quickly add them to Maximo using MXLoader. After that the
amount of error was greatly reduced. By doing occasion check on the log files
on the following few days, I was able to list all of the missing 30+ locations
and able to remove all of the related errors. The remaining errors found in the
log files were easily handled separately, some were quite critical that only be
made aware to the business after that.<o:p></o:p></p>
<p class="MsoNormal" style="text-align: justify;"><o:p> </o:p></p>
<p class="MsoNormal" style="text-align: justify;"><o:p> </o:p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-24252297102469603572020-12-09T22:24:00.006+11:002020-12-15T09:06:21.676+11:00Avoiding recursion filter on Publish Channel<p>The standard way to send a message from Maximo to an
external system is by setting up a Publish Channel and enabling Event Listener.
By default, Integration Framework doesn’t re-publish a change if it comes from
another inbound interface to prevent recursion on a bi-directional interface. Although
I don’t agree with this logic as one-way integration is much more common, but
anyway, IBM said it is easy to override that by extending the <a href="https://www.ibm.com/support/pages/integration-framework-recurrence-inbound-transactions-using-event-filter-class" target="_blank">Event Filter javaclass</a>.</p><p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal">The problem is, with the rise of automation script, no one wants java customization anymore. Of course, for massive systems where performance is critical, it is still the best choice. But, for
most medium-sized clients I work with, they’re all moving away from java
customization.<o:p></o:p></p>
<p class="MsoNormal">Anyway, an approach we can deal with this is not to use
Event Listener at all. Instead, we can trigger a publish from an Object Save
launch point from automation script using the example python code below:<o:p></o:p></p>
<blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="background-attachment: initial; background-clip: initial; background-color: #f3f3f3; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; background: rgb(243, 243, 243);">from
psdi.iface.mic import PublishChannelCache</span></div><div style="text-align: left;"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="background-color: #f3f3f3;">PublishChannelCache.getInstance().getPublishChannel("PUBLISH_CHANNEL_NAME>").publish(mbo,
True)</span></span></div><div style="text-align: left;"><span style="mso-spacerun: yes;"> </span></div></blockquote>
<p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal"><o:p> Happy coding!</o:p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com5tag:blogger.com,1999:blog-2386943267680155685.post-1239294566841429832020-12-07T22:33:00.002+11:002020-12-07T22:33:48.121+11:00Check Network/Firewall Status using PowerShellWorking with Maximo, we have to deal with network/firewall all the time. I can spend a whole day telling you fun stories (or extremely frustrating experience) I had with IT/Network guys. But that's not the point. If you end up here, I guess you're having an issue with firewall. So below is a quick recap of my tricks:<div><br /></div><div>- <b>Ping</b>: the first thing we try when we want to see if a server is accessible from our current machine. But don't jump to a conclusion too early if it shows the server is unreachable. Sometimes, the Network Admin has disabled ping response to enhance security. </div><div><br /></div><div>- <b>Telnet</b>: to check if a port is opened, we can use telnet from Windows Command console (e.g. <b style="background-color: #cccccc;">telnet google.com 80</b>). If it can be connected, means the target server is accessible on the specified port. But if it doesn't, first, make sure the server is listening on that port, before jumping to a conclusion that it's a Firewall issue. I made this mistake a few times, blaming the network, then it turned out it's Websphere/Maximo service is not running on the port that I assumed it should be running on</div><div><br /></div><div>- <b>PowerShell</b>: in many cases, the server is not connected to the Internet, and Telnet is not installed, (and yes, you don't have permission to install new software either). We can use PowerShell to check network connectivity using the two commands below:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><span style="background-color: #cccccc;"><b>$connection = New-Object System.Net.Sockets.TcpClient("google.com", 80)</b></span></div><div><span style="background-color: #cccccc;"><b>Write-Host $connection.Connected</b></span></div></blockquote><div><br /></div><div>The first line will return some errors if it cannot connect to the server, like the screenshot below: </div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAhznRBVq7q57AEID76CX1bbbSAe4ACozXIpirlDd7X6o9Sq7kvd3Mja1mbvhb5iXq1vk1M8JmuzG8zWmXLuKaBaOV-_ADQddzKjkAlQtv53ypZM7Fh6bFrSkAgAeaw79qgvlGr4GqXgo/s863/PowerShell2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="190" data-original-width="863" height="140" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAhznRBVq7q57AEID76CX1bbbSAe4ACozXIpirlDd7X6o9Sq7kvd3Mja1mbvhb5iXq1vk1M8JmuzG8zWmXLuKaBaOV-_ADQddzKjkAlQtv53ypZM7Fh6bFrSkAgAeaw79qgvlGr4GqXgo/w640-h140/PowerShell2.png" width="640" /></a></div><br /> If the server is accessible via the provided IP and port, the 2nd line will return the status = Connected</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi467iRrsw1fm6L95RCJsIB1RNJNIMyTiOGTBoGVD07f7Au0Gxf8VKzG76yMhxPHjABqABC2Yuw3Crpd4CPrIY5bO3Zun-k6M1zh0R43vPFXn2SviPhxj1uISZ-VQL3y8rVaZ2bG9P38tM/s773/PowerShell1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="76" data-original-width="773" height="62" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi467iRrsw1fm6L95RCJsIB1RNJNIMyTiOGTBoGVD07f7Au0Gxf8VKzG76yMhxPHjABqABC2Yuw3Crpd4CPrIY5bO3Zun-k6M1zh0R43vPFXn2SviPhxj1uISZ-VQL3y8rVaZ2bG9P38tM/w640-h62/PowerShell1.png" width="640" /></a></div><br /><div><br /></div>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-90032156817264691682020-08-08T12:13:00.008+10:002020-08-14T13:30:59.322+10:00Common use cases for EAM mobile solutions<p>In some organisations, when starting a mobile project, the stakeholders
may not have prior experience with mobility solutions for EAM, as such, we are sometimes
asked to implement features that do not add value to the business. As consultants,
it is satisfying to see something we implement being used and helps the end-users
on the field. And nothing can be more frustrating
than spending time building features that are not going to be used. In this
post, I will list out based on my experience some commonly used and not used
functions of a mobile app:</p><p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal"><b>Work Execution</b>: despite its name is Asset Management
software, 80% of activities in Maximo happen around the work management
process. It is not a surprise work execution is the number one use-case for a
mobile app. However, work management is a big process with several major stages
and many different activities. Only certain activities need to be recorded in
the field with a mobile device. Below are some common ones:<o:p></o:p></p><span><a name='more'></a></span><p class="MsoNormal"></p><ul style="text-align: left;"><li><span style="text-indent: -18pt;">Change Work Order status: start work, put it on
hold, or complete the work</span></li><li>Record actual costs: travel time, work time,
material consumption</li><li>Capture and attach photos</li><li>Enter work log</li><li><span style="text-indent: -18pt;">Capture operational parameter (meter reading)</span></li></ul><p></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: 0cm; mso-add-space: auto;">By
having a mobile device to capture these data on the field as it happens,
organizations benefit from having much more accurate data. The last one is a key enabler for a comprehensive Condition Monitoring program which is an important topic and I’ll cover in a separate post.<o:p></o:p></p><p class="MsoListParagraphCxSpMiddle" style="margin-left: 0cm; mso-add-space: auto;"><br /></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: 0cm; mso-add-space: auto;"><b>Physical
signature</b>: the ability to physically sign on a mobile device sounds great. Most mobile apps has this function. I have implemented this a few times. The user can physically sign on a mobile device. The signature is printed at the bottom of some BIRT reports (e.g. Work Completion form or Risk Assessment form etc.). It looked great and the customers were excited about it. But in all honesty, I find it's not really that important in
many cases.</p><p class="MsoListParagraphCxSpMiddle" style="margin-left: 0cm; mso-add-space: auto;"><o:p></o:p></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: 0cm; mso-add-space: auto;"><br /></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: 0cm; mso-add-space: auto;"><b>Work
Planning & Approval</b>: these are the activities that can and often be
done in an office setting with the user accessing Maximo on a browser. Planning
process can involve 3<sup>rd</sup> party scheduling tools like Akwire,
Primavera, Visual Planner etc. Who would want to go to the field to schedule
and assign people to some work orders on a tiny mobile screen? Some mobile apps
do have the capability to do that, but it doesn’t mean we have to use it. These
features are available usually for the field user to see the planned
information, and possibly edit some incorrect details.<o:p></o:p></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: 0cm; mso-add-space: auto;"><br /></p>
<p class="MsoListParagraphCxSpLast" style="margin-left: 0cm; mso-add-space: auto;"><b>Inspection
Form: </b>added from Maximo 7.6.0.8 as part of the Work Center module,<b> </b>and
progressively improved in the last few releases.<b> </b>This is the best
feature among those added to Maximo for the last 10 years. However, the Work
Center function is still too limited to be practical (UI is too slow, requires good
and fast connectivity, and not easy to customise). EzMaxMobile does the job
very well by providing responsive online and offline UI and works well with the
out-of-the-box backend in Maximo. From my limited knowledge, Datasplice had
Inspection app since at least a few years back, long before Maximo. That is
because Datasplice is an independent system with its own database. There are inspection
mobile solutions provided by industrial vendors too (e.g. Honeywell). I don’t
know how well these solutions work. My point is this is a common requirement
and haven’t been addressed by Maximo until recently. By filling out inspection
forms on a mobile device, data is fed directly to meter reading tables and the condition
monitoring app in Maximo which raises alarms or PM work orders automatically. <o:p></o:p></p><p class="MsoListParagraphCxSpLast" style="margin-left: 0cm; mso-add-space: auto;"><br /></p>
<p class="MsoNormal"><b>Inventory Stocktake</b>: my first three mobile projects
were to address this requirement so it must be a good use-case. The task is simple:
the user scans a barcode, enters a balance value, then saves. Extended feature
could be adding a photo of the item, adding a new bin, or updating item specification which later can be used for cataloguing and de-duplication
process. The key to getting it to work is a really smooth and efficient user
experience as these steps need to be repeated a thousand times in one shift. Fast
and simple UI running in full offline mode is a must. <strike>Interestingly, IBM doesn’t
make offline mode available in the Anywhere Physical Count app, which makes it
worthless in my opinion. It does work in some cases but imagine how frustrating
it would be for the end users when getting to a corner of a warehouse with poor signal. In a Work Execution app, users only need to change work order status
before starting work and entering labour time after finishing work. If there
is no connectivity, it can wait until they get to a place with better connectivity.
With physical count, the user needs to constantly scan and update data. Any
disruption due to poor or no connectivity is unacceptable</strike>. (Updated: Anywhere Physical count does work with Offline now)<o:p></o:p></p>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIGJVMV6vg92pJX_Vs8q9Em4hjwoQqGZuq_hM_mQW6tLKW2EzbAdYe6CfqB7uw5COryqxOsPVgAv93ZiAXQjVWbJR_WlwGYvBo47JOX6lz8JwG5HtrHD06gPXFSE-tick21v9UhGiM3pE/s1795/Abestos.jpg" style="display: block; margin-left: auto; margin-right: auto; padding: 1em 0px;"><img border="0" data-original-height="1274" data-original-width="1795" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIGJVMV6vg92pJX_Vs8q9Em4hjwoQqGZuq_hM_mQW6tLKW2EzbAdYe6CfqB7uw5COryqxOsPVgAv93ZiAXQjVWbJR_WlwGYvBo47JOX6lz8JwG5HtrHD06gPXFSE-tick21v9UhGiM3pE/w410-h290/Abestos.jpg" width="410" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><span style="font-size: small;"><i><b>Me counting and capturing item spec at a time mobile solution wasn't available. <br /> All data needed to be manually entered into Maximo by some interns later on<br /></b></i></span></td></tr></tbody></table><p class="MsoNormal"><b>Inventory Issue/Transfer/Receipt</b>: I’ve seen some projects
involved these apps. In general, how often it is used and how much value it brings
to the organization is not known to me. I think it does add value, but of course, not at the level of the Point-of-Sales solutions we often see in a supermarket.<o:p></o:p></p>
<p class="MsoNormal"><b>Purchase Requisition/Purchase Order</b>: Although PR and
PO apps are available on some mobile solutions, I haven’t seen them used
anywhere. There is some value for the field worker to be able to create Requisition.
That function belongs to the Work Order module though. The data then flows to
the PR application and handled by a back-office staff with a computer and a browser.<o:p></o:p></p>
<p class="MsoNormal"><b>Risk Assessment/Toolbox Talk</b>: this is a great use
case for a mobile app. It is something that must be done on the field, it adds a lot
of value in term of safety improvement and compliance. In many cases, it has
some legal implication. Unfortunately, even the Oil & Gas (HSE) module in
Maximo does not meet all of the requirement and usually needs a lot of customization.
We had implemented this feature in EzMaxMobile for some clients. But the assessment
forms and the compliance requirements are different in each industry and from
company to company. Thus, I don’t think we can see a standard risk assessment mobile
app for Maximo anytime soon. If your company needs it, you may have to pay for
the customization effort.<o:p></o:p></p>
<div class="separator" style="clear: both;"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqN_2aOudyks0qtZPSZkTphsdIhfLdXvs1vt2-k6-AVpCKgahenwPRnY4Ow6CI9gpM2uUl2Vni4BX694xIJzuaJfeLbJjYQvS7Co4YIc3xRh1HBOILcH3uyqvJqEALGcgZ39IxhGsABHQ/s512/RiskAssessment.png" style="display: block; margin-left: auto; margin-right: auto; padding: 1em 0px;"><img border="0" data-original-height="379" data-original-width="512" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqN_2aOudyks0qtZPSZkTphsdIhfLdXvs1vt2-k6-AVpCKgahenwPRnY4Ow6CI9gpM2uUl2Vni4BX694xIJzuaJfeLbJjYQvS7Co4YIc3xRh1HBOILcH3uyqvJqEALGcgZ39IxhGsABHQ/s0/RiskAssessment.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><span style="font-size: small;"><b><i>A risk assessment form on iPad screen</i></b></span><br /></td></tr></tbody></table>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com0tag:blogger.com,1999:blog-2386943267680155685.post-46014036326415930882020-08-01T12:32:00.069+10:002020-08-07T09:55:47.480+10:00A case for EzMaxMobile<p class="MsoNormal">I spend a
large portion of my time working with mobile solutions, but I haven’t talked
much on this topic. In this post, I will give a bit of praise to EzMaxMobile.</p>
<p class="MsoNormal"><b><span lang="" style="mso-ansi-language: EN-US;">Why should
you listen to me</span></b><span lang="" style="mso-ansi-language: EN-US;">: I
know a bit about mobile solutions for Maximo. I’ve done several (failed) pilots
with the Maximo mobile suite (of the olden days). I’ve built a <a href="https://youtu.be/kGtJXQ22HcE" target="_blank">mobile app</a> which
is pretty successful and is being used by some large Oil & Gas operators. I
have a bit of experience on a few small Anywhere projects, with Datasplice, and
a few smaller home-grown apps. <o:p></o:p></span></p>
<p class="MsoNormal"><b><span lang="" style="mso-ansi-language: EN-US;">Why
shouldn’t you listen to me</span></b><span lang="" style="mso-ansi-language: EN-US;">: the settings and the level of my involvement for each of the above
projects/deployments are vastly different, as such, my opinion on this matter
is heavily biased (toward EzMaxMobile).<o:p></o:p></span></p><p class="MsoNormal"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhseU6SQGDXHHTbCBWuQGV8yIiCMHUamhjL9Ab-LO1WkzBT62T8x0Lwm6At1nG3sPlWLIBlm4tokkixWqXn6jdJ-P3yMI1YWYYAAHgzaLt3B8T3RqxNLfRJujGhwalXctUDzlNFvpnTv-o/s755/ZebraDevices.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="755" data-original-width="710" height="410" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhseU6SQGDXHHTbCBWuQGV8yIiCMHUamhjL9Ab-LO1WkzBT62T8x0Lwm6At1nG3sPlWLIBlm4tokkixWqXn6jdJ-P3yMI1YWYYAAHgzaLt3B8T3RqxNLfRJujGhwalXctUDzlNFvpnTv-o/w386-h410/ZebraDevices.jpg" width="386" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><p></p><p class="MsoNormal"><span></span></p><a name='more'></a><p></p><p class="MsoNormal"></p><p class="MsoNormal" style="mso-margin-bottom-alt: auto; mso-margin-top-alt: auto;"><span style="color: black; line-height: 107%;">Back to the topic, as of
today (2020), I think EzMaxMobile is by far the best mobile solution for
Maximo. There are many things that InterPro (and Chon Neth, the original author of
the popular Maximo Times blog) have done right with this product. Below are the
3 things I love the most:</span><o:p></o:p></p>
<ul style="-webkit-text-stroke-width: 0px; font-variant-caps: normal; font-variant-ligatures: normal; orphans: 2; text-decoration-color: initial; text-decoration-style: initial; widows: 2; word-spacing: 0px;" type="disc">
<li class="MsoNormal" style="color: black; line-height: normal; mso-list: l2 level1 lfo1; mso-margin-bottom-alt: auto; mso-margin-top-alt: auto; tab-stops: list .5in;"><b><span lang=""><span>Consistency with Maximo</span></span></b><span lang=""><span>: EzMax uses the same business layer with Maximo and inherits all MBO business logic. It means, 90% of all logic customization done in Maximo automatically flows through to the mobile app. It includes integration processing rules, MBO java extension,
automation scripts, security settings, and error messages. If you’ve heard of the phrase “the best code is no code at all”, you will appreciate to know that they’ve achieved exactly that with this product. On this point,
EzMax is the clear winner when compared with Datasplice because EzMax is built for Maximo, while Datasplice is built as an independent work management mobile app that works with both SAP and Maximo.</span><o:p></o:p></span></li>
</ul>
<ul style="-webkit-text-stroke-width: 0px; font-variant-caps: normal; font-variant-ligatures: normal; orphans: 2; text-decoration-color: initial; text-decoration-style: initial; widows: 2; word-spacing: 0px;" type="disc">
<li class="MsoNormal" style="color: black; line-height: normal; mso-list: l1 level1 lfo2; mso-margin-bottom-alt: auto; mso-margin-top-alt: auto; tab-stops: list .5in;"><b><span lang=""><span>A great open framework</span></span></b><span lang=""><span>: in certain cases, there are still rules that need to be configured on the Mobile app only. For that, EzMax has a great framework that is very similar to Maximo architecture which consists of a
core library (which we can decompile to understand the logic), and MVC
components of which the source code is provided. Third-party implementers like myself are free to modify and extend any logic to meet user requirements. The business layer is written in Java, as such, we don’t
need to know native mobile development skills such as Android or iOS to
customize the app; definitely don’t need Android Studio and/or Xcode
(which needs a MacBook), that’s another big plus. Having experience building mobile app before, I can tell it’s easy to build and deliver an app with a predefined specification. Most developers of home-grown apps will be stuck at that, building and implementing their own app. Ability to build an open framework and enable 3<sup>rd</sup> party implementers to build and extend on top of that is a totally different level. So just for this point alone, my hat’s off to you, Mr. Chon.</span><o:p></o:p></span></li>
</ul>
<ul style="-webkit-text-stroke-width: 0px; font-variant-caps: normal; font-variant-ligatures: normal; orphans: 2; text-decoration-color: initial; text-decoration-style: initial; widows: 2; word-spacing: 0px;" type="disc">
<li class="MsoNormal" style="color: black; line-height: normal; mso-list: l0 level1 lfo3; mso-margin-bottom-alt: auto; mso-margin-top-alt: auto; tab-stops: list .5in;"><b><span lang=""><span>Instant development loop and ease of deployment</span></span></b><span lang=""><span>: this is probably what I love the most when working with EzMax. Instant development loop means immediately after updating the code, I can see the result. On production, full redeployment takes 5-10 minutes. However, most small changes can be done on-the-fly
without downtime, and users see the change immediately. On other platforms
where compiling the code to native iOS/Android app is required, we usually
have to wait for 10-30 minutes to compile the code in
XCode/Android before you can run it. Then redeployment, then users have to re-install the app. Although it doesn’t sound that interesting to the business users, it will directly translate into the much higher overall productivity, and thus, lower cost for the customer. In a recent project I involved, it took me
less than 10 hours of dev work to cover minor screen changes which include
100+ fields in about 20 app screens.</span> For
Anywhere, it's likely to take me one full man-month or more, not to mention,
many of the changes wouldn’t be possible in Anywhere in the first place. <i>(And a small note:
when a developer loves something, it directly leads to a much higher
quality product and happy users; when a developer hates something, it
means glitches, frustration, and meeting room arguments)</i><o:p></o:p></span></li>
</ul>
<p class="MsoNormal" style="-webkit-text-stroke-width: 0px; font-variant-caps: normal; font-variant-ligatures: normal; mso-margin-bottom-alt: auto; mso-margin-top-alt: auto; orphans: 2; text-decoration-color: initial; text-decoration-style: initial; widows: 2; word-spacing: 0px;"><span style="color: black; line-height: 107%;"><span lang="" style="mso-ansi-language: EN-US;">To
be fair, there are certain things I don’t like about EzMaxMobile. For example,
Offline mode has a different code structure, i.e. any screen changes need to be
done twice for the online and offline screens. Besides, Offline functionality
is also more limited than Online. This is the inherent problem with any offline
mobile app. Think about it: an offline app will need all 3 layers: database,
business, and UI in one mobile device. In other words, full capability means
you need the whole Maximo system running on a mobile device, which is
impossible. However, on this aspect, Anywhere does provide smoother transition
between Online and Offline. Anyway, it doesn’t really matter much when
Anywhere’s capability is too limited in the first place.<o:p><o:p></o:p></o:p></span></span></p>
<p class="MsoNormal" style="-webkit-text-stroke-width: 0px; font-variant-caps: normal; font-variant-ligatures: normal; mso-margin-bottom-alt: auto; mso-margin-top-alt: auto; orphans: 2; text-decoration-color: initial; text-decoration-style: initial; widows: 2; word-spacing: 0px;"><span lang="" style="mso-ansi-language: EN-US;"><span style="color: black; line-height: 107%;">Again, this is my
personal and very biased view. I currently work for a company which provides
EzMaxMobile implementation service (means I spent a lot more time working with
EzMax). However, I don’t have any involvement in business development, thus I
don’t have any obligation or receive any benefit for promoting it. In fact, we
do provide implementation services for both EzMaxMobile and Anywhere. However,
I think compared with Anywhere, EzMax is far more practical. In my opinion, IBM
would do the Maximo world a favour by ditching Anywhere and acquire Interpro</span>.<o:p></o:p></span></p>
<p class="MsoNormal" style="-webkit-text-stroke-width: 0px; font-variant-caps: normal; font-variant-ligatures: normal; mso-margin-bottom-alt: auto; mso-margin-top-alt: auto; orphans: 2; text-decoration-color: initial; text-decoration-style: initial; widows: 2; word-spacing: 0px;"><span lang="" style="mso-ansi-language: EN-US;"><span style="color: black; line-height: 107%;">As for the Maximo users,
I cannot say in general which mobile solution is the best for you as there are many
other options out there (e.g. EAM360, Informer). It also largely depends on
what local providers are available in certain countries and the products they
use. Most of the time, the success of an implementation depends on the
consultant, not the product. Thus, my suggestion is, if you are thinking about
implementing a mobile solution, don’t just blindly go for a “default”,
brand-name solution. Do your homework by getting quotes and demo on a list of
specific requirements. For mobile solutions, the requirements are usually simple, focusing on a few tasks that add value to a worker if done on the
field. Thus, it is not difficult to define specific requirements during the RFx
processes. If you don’t know what are the requirements yet, do a small pilot
with an out-of-the-box installation first. License, server, and installation
service are cheap. It’s the customization that costs the most. How about
starting with a trial license or 5 named-user licenses, no customization, then
get some feedback from the field guys? It’s not a bad idea to do a dual pilot,
then comparing products side-by-side. I’ve seen one or two clients who did just
that, and it saved them a lot of money and frustration over the long run.<o:p><o:p></o:p></o:p></span></span></p><br /><p></p>Viet Tranhttp://www.blogger.com/profile/08353095557070775609noreply@blogger.com2