Maximo Event Filter Java class not being picked up by Publish Channel - maximo

I have written a Java class for event filtering on one of the Publish channels, and rebuilt and deployed it. I have referenced it on the Publish channel too. However, Maximo behaves as if the class was never there.
package com.sof.iface.eventfilter;
import java.rmi.RemoteException;
import psdi.iface.mic.MaximoEventFilter;
import psdi.iface.mic.PublishInfo;
import psdi.mbo.MboRemote;
import psdi.util.MXException;
import psdi.util.logging.MXLogger;
import psdi.util.logging.MXLoggerFactory;
public class VSPPWOCOMPEventFilter extends MaximoEventFilter {
private static final String SILMX_ATTRIBUTE_STATUS = "STATUS";
private MXLogger log = MXLoggerFactory.getLogger("maximo.application.EVENTFILTER");
/**
* Constructor
*
* #param pubInfo Publish Channel Information
* #throws MXException Maximo Exception
*/
public VSPPWOCOMPEventFilter(PublishInfo pubInfo) throws MXException {
super(pubInfo);
} // end constructor.
/**
* Decide whether to filter out the event before it triggers the
* Publish Channel or not.
*/
public boolean filterEvent(MboRemote mbo) throws MXException, RemoteException {
log.debug("######## com.sof.iface.eventfilter.VSPPWOCOMPEventFilter::filterEvent() - Start of Method");
boolean filter = false;
// try {
String status = mbo.getString(SILMX_ATTRIBUTE_STATUS);
log.debug("######## com.sof.iface.eventfilter.VSPPWOCOMPEventFilter::filterEvent() - WO Status " + status);
if(mbo.isModified("STATUS") && status == "COMP") {
log.debug("######## com.sof.iface.eventfilter.VSPPWOCOMPEventFilter::filterEvent() - Skipping MBO");
filter = true;
} else {
filter = super.filterEvent(mbo);
}
log.debug("######## com.sof.iface.eventfilter.VSPPWOCOMPEventFilter::filterEvent() - End of Method");
return filter;
// }
} // end filterEvent.
} // end class.
Please ignore the below text :)
A good logging (tracing) is always a lifesaver when you have problems in a production environment. I will never stop telling to my fellow programmers how much is important to fill code with meaningful log calls.Maximo has a good and flexible logging subsystem. This IBM TechNote describes in detail how logging works in Maximo. Let’s now see hot to use Maximo logging in your custom Java code.

It looks like you need to skip the outbound message when the Work Order is completed. When the event doesn't seem to occur, make sure to check for these flags:
External System is active
Publish Channel is active
Publish Channel listener is enabled
I think you could easily achieve the same result with a SKIP action processing rule. See details here:
https://www.ibm.com/support/knowledgecenter/en/SSLKT6_7.6.0/com.ibm.mt.doc/gp_intfrmwk/c_message_proc_actions.html
Also worth mentioning: IBM added automation script support for Event Filtering in version 7.6 so no more build/redeploy required.

Related

Spring Integration Jms InboundGateway Dynamic Reply Queue

Is it possible to have a dynamic reply queue with Jms OutboundGateway via DSL?
Jms.inboundGateway(jmsListenerContainer)
.defaultReplyQueueName("queue1 or queue2")
Working Solution using ThreadLocal and DestinationResolver:
private static final ThreadLocal<String> REPLY_QUEUE = new ThreadLocal<>();
IntegrationFlows.from(Jms.inboundGateway(listenerContainer)
.defaultReplyQueueName("defaultQueue1")
.destinationResolver(destinationResolver())
.transform(p -> {
// on some condition, else "defaultQueue1"
REPLY_QUEUE.set("changedToQueue2");
return p;
})
#Bean
public DestinationResolver destinationResolver() {
return (session, destinationName, pubSubDomain) -> session.createQueue(REPLY_QUEUE.get());
}
It is not clear from where you'd like to take that dynamic reply queue name, but there is another option:
/**
* #param destinationResolver the destinationResolver.
* #return the spec.
* #see ChannelPublishingJmsMessageListener#setDestinationResolver(DestinationResolver)
*/
public S destinationResolver(DestinationResolver destinationResolver) {
By default this one is a DynamicDestinationResolver which does only this: return session.createQueue(queueName);. Probably here you can play somehow with your different names to determine.
Another way is to have a JMSReplyTo property set in the request message from the publisher.
UPDATE
Since you cannot rely on a default Reply-To JMS message property, I suggest you to look into a ThreadLocal in your downstream flow where you can place your custom header. Then a custom DestinationResolver can take a look into that ThreadLocal variable for a name to delegate to the same mentioned DynamicDestinationResolver.

Records are not consumed when adding checkpointer

I have following configuration for KinesisMessageDrivenChannelAdapter, when I remove dynamoDbMetaDataStore as checkpointer, messages are received correctly, but when I add it back records are always empty.
I debugged the code and KinesisMessageDrivenChannelAdapter.processTask() line 776 (version 2.0.0.M2) returns empty records.
UPDATE:
public DynamoDbMetaDataStore dynamoDbMetaDataStore() {
String url = consumerClientProperties.getDynamoDB().getUrl();
final AmazonDynamoDBAsync amazonDynamoDB = AmazonDynamoDBAsyncClientBuilder.standard()
.withEndpointConfiguration(new EndpointConfiguration(
url,
Regions.fromName(awsRegion).getName()))
.withClientConfiguration(new ClientConfiguration()
.withMaxErrorRetry(consumerClientProperties.getDynamoDB().getRetries())
.withConnectionTimeout(consumerClientProperties.getDynamoDB().getConnectionTimeout())).build();
DynamoDbMetaDataStore dynamoDbMetaDataStore = new DynamoDbMetaDataStore(amazonDynamoDB, "consumer-test");
return dynamoDbMetaDataStore;
}
public KinesisMessageDrivenChannelAdapter kinesisInboundChannel(
AmazonKinesis amazonKinesis, String[] streamNames) {
KinesisMessageDrivenChannelAdapter adapter =
new KinesisMessageDrivenChannelAdapter(amazonKinesis, streamNames);
adapter.setConverter(null);
adapter.setOutputChannel(kinesisReceiveChannel());
adapter.setCheckpointStore(dynamoDbMetaDataStore());
adapter.setConsumerGroup(consumerClientProperties.getName());
adapter.setCheckpointMode(CheckpointMode.manual);
adapter.setListenerMode(ListenerMode.record);
adapter.setStartTimeout(10000);
adapter.setDescribeStreamRetries(1);
adapter.setConcurrency(10);
return adapter;
}
Thank you
I recommend you to test your solution with the latest 2.0.0.BUILD-SNAPSHOT.
There is already an option like:
/**
* Specify a {#link LockRegistry} for an exclusive access to provided streams.
* This is not used when shards-based configuration is provided.
* #param lockRegistry the {#link LockRegistry} to use.
* #since 2.0
*/
public void setLockRegistry(LockRegistry lockRegistry) {
where you would need to inject a DynamoDbLockRegistry for better checkpoint management.
For that purpose you would also need to add this dependency:
compile("com.amazonaws:dynamodb-lock-client:1.0.0")
There indeed might be some issues with filtering in that M2 yet...

How to move issues from Google Code to Phabricator

Google Code is shutting down so I want to move my 2500 issues to Phabricator (hosted at Phoreplay).
While there are moving procedure for Github and others, I did not manage to find similar tools for Phabricator.
How to move issues from Google Code to Phabricator?
Only issues, not wiki/code/downloads/etc.
Note: I use Phabricator instead of Github because it fits my requirements better.
Preliminary note if you wish to keep tasks IDs
Migrations project could be facilitated if we can temporarily edit the Maniphest application code, so as you aren't in control of your installation, this is difficult to offer a clean solution to get consistent ID. So, first, you should be in control of your installation.
Such migration code has been written by the Blender project: here their repository at import moment.
The steps
Export Google code tasks in CSV or JSON format
Run a Phabricator script to import them, or, call the API conduit
Export
Google provides some tools to perform the migration. These tools include a issues.py script to parse issues on Google code.
With that, you can dump your issues in a workable format, for example JSON to store an array of comments.
Import through API (best for smallest tasks, without comments)
You could use the API and call through conduit maniphest.createtask. But this is not really convenient, as it's not the easiest way to add comments, closes the issue, etc.
Import through a script
This is probably the most interesting way to import the tasks, and this is the solution offering the maximal flexibility.
Here the skeleton of such script I drafting from the Blender code and some of my internal codes:
#!/usr/bin/env php
<?php
$root = dirname(dirname(__FILE__));
require_once $root . '/scripts/__init_script__.php';
/**
* Represents a task importable into Phabricator
*/
class LegacyTask {
private $id;
private $title;
//... other tasks properties, depending the Google Code fields
public function importIntoPhabricator () {
$projects = ......... // we need an array with one or more PHIDs, according what you created
$task = ManiphestTask::initializeNewTask($reporter);
$task->setTitle($title);
$task->attachProjectPHIDs($projects);
$task->setDescription($this->description);
$task->setPriority($this->priority);
$task->setOverrideID($this->id); //This is the method you want to borrow to the Blender migration code
$task->save();
//Initial transaction
$changes = [
ManiphestTransaction::TYPE_STATUS => ManiphestTaskStatus::STATUS_OPEN,
PhabricatorTransactions::TYPE_VIEW_POLICY => 'public'
];
self::applyTransactionsForChanges($task, $changes);
//Closes task
if ($this->closed) {
$status = ... //ManiphestTaskStatus::STATUS_CLOSED_RESOLVED
self::closeTask($task, $status);
}
//Project transaction
self::associateTaskToProject($task, $projects);
//Adds comments
//...
}
static public function getTransactions ($changes) {
$transactions = [];
$template = new ManiphestTransaction();
foreach ($changes as $type => $value) {
$transaction = clone $template;
$transaction->setTransactionType($type);
if ($type == PhabricatorTransactions::TYPE_EDGE) {
$transaction->setMetadataValue('edge:type', PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
}
$transaction->setNewValue($value);
$transactions[] = $transaction;
}
return $transactions;
}
static public function applyTransactionsForChanges ($task, $changes) {
$transactions = self::getTransactions($changes);
self::applyTransactions($task, $transactions);
}
static public function applyTransactions ($task, $transactions) {
$editor = id(new ManiphestTransactionEditor())
->setActor(self::getReporterUser())
->setContentSource(self::getContentSource())
->setContinueOnNoEffect(true)
->applyTransactions($task, $transactions);
}
static function associateTaskToProject($task, $projects) {
$project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$transactions = [
id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $project_type)
->setNewValue([
'=' => array_fuse($projects)
])
];
self::applyTransactions($task, $transactions);
}
/**
* Closes the task
*/
static public function closeTask ($task, $status) {
$changes = [
ManiphestTransaction::TYPE_STATUS => $status
];
self::applyTransactionsForChanges($task, $changes);
}
}
Close status are documented here.
It works best.
Ask your core developers and top reporters if any to create an account, try to map their users, and for everyone else, attribute issues and comments to a bot account created for the migration.

griffon javafx-groovy and fxml?

I tried the samples given in github griffon-master, also I tried the samples of the guide.
I would like to use javafx and groovy.
I would like to use fxml - thought of a scenario as that: fxml to set the stage, and for changes, use the groovy (set adjustment)
It seems that is not possible. I can use ("make it run"): javafx-java, read an fxml (with loadFromFXML), and the bindings are working. If using javafx-groovy, I can read an fxml, but with the javafx-class Loader (load), and bindings are not working (or it seems so).
Is it not possible at this moment, to use javafx-groovy and read-in fxml (via loadfromfxml)?
Could you post some sample code? Here's one example that makes use of the fxml node form GroovyFX
package org.example
import griffon.core.artifact.GriffonView
import griffon.metadata.ArtifactProviderFor
import javafx.scene.control.Tab
import org.codehaus.griffon.runtime.javafx.artifact.AbstractJavaFXGriffonView
#ArtifactProviderFor(GriffonView)
class Tab4View extends AbstractJavaFXGriffonView {
FactoryBuilderSupport builder
SampleController controller
SampleModel model
private AppView parentView
void initUI() {
builder.with {
content = builder.fxml(resource('/org/example/tab4.fxml')) {
inputLabel.text = application.messageSource.getMessage('name.label')
bean(input, text: bind(model.inputProperty()))
bean(output, text: bind(model.outputProperty()))
}
}
connectActions(builder.content, controller)
Tab tab = new Tab('Hybrid')
tab.content = builder.content
parentView.tabPane.tabs.add(tab)
}
}
This can be done. The trick is to make your Controller actions adhere to a stringent set of rules. The tldr is to make sure they return void.
Good:
def void save() {
Bad:
def save() {
The reason is found in the reflective analysis the Griffon framework uses to create its list of action targets. This list is generated in DefaultGriffonControllerClass.getActionNames(), which requires that:
Actions are subject to the following rules in order to be considered as such:
must have public (Java) or default (Groovy) visibility modifier.
name does not match an event handler, i.e, it does not begin with on.
must pass {code GriffonClassUtils.isPlainMethod()} if it's a method.
must have void as return type if it's a method.
value must be a closure (including curried method pointers) if it's a property.
The criteria defined in GriffonClassUtils.isPlainMethod() are as follows:
isInstanceMethod(method)
! isBasicMethod(method)
! isGroovyInjectedMethod(method)
! isThreadingMethod(method)
! isArtifactMethod(method)
! isMvcMethod(method)
! isServiceMethod(method)
! isEventPublisherMethod(method)
! isObservableMethod(method)
! isResourceHandlerMethod(method)
! isGetterMethod(method)
! isSetterMethod(method)
! isContributionMethod(method)
The list of action target names is subsequently used by AbstractActionManager:
#Nullable
private static Method findActionAsMethod(#Nonnull GriffonController controller, #Nonnull String actionName) {
for (Method method : controller.getClass().getMethods()) {
if (actionName.equals(method.getName()) &&
isPublic(method.getModifiers()) &&
!isStatic(method.getModifiers()) &&
method.getReturnType() == Void.TYPE) {
return method;
}
}
return null;
}

Log4net EventLogAppender Log Event ID

Is there a way to log an event into the windows event log with a specified eventid per message? I am using log4net v 1.2.10.
Based on what I see in the EventLogAppender source code the following should do the trick:
log4net.ThreadContext.Properties["EventID"] = 5;
Just call this before you write your log messages (if you do not set it for all messages you should remove the "EventID" again from the Properties.
N.B the property key is case sensitive.
When one uses the native .net Event Log APIs in System.Diagnostics, the WriteEntry methods allow setting the eventID and category. In these APIs:
eventID is a 32 bit int, but its value must be between 0 and 65535
category is a 16 bit int, but its value must be positive. If the
event source includes a category resource file, the event viewer will
use the integer category value to lookup a localized “Task category”
string. Otherwise, the integer value is displayed.
The categories must be numbered consecutively, beginning with the
number 1
Log4net supports writing an EventID and a Category, but it isn’t straight forward. When log4net’s EventLogAppender logs an event, it looks at a dictionary of properties. The named properties "EventID" and "Category" are automatically mapped by the EventLogAppender to the corresponding values in the event log. I’ve seen a few good suggested ways to use log4net’s EventLogAppender and set the EventID and Category in the Windows event log.
a. Using log4net’s appender filtering, a filter may be registered that can add the EventID and Category properties. This method has a nice benefit that the standard log4net wrappers are used and so this can be implemented without changing existing logging code. The difficulty in this method is some mechanism has to be created to calculate the EventID and Category from the logged information. For instance, the filter could look at the exception source and map that source to a Category value.
b. Log4net may be extended so custom logging wrappers can be used that can include EventID and Category parameters. Adding EventID is demonstrated in the log4net sample “Extensibility – EventIDLogApp” which is included in the log4net source. In the extension sample a new interface (IEventIDLog) is used that extends the standard ILog interface used by applications to log. This provides new logging methods that include an eventId parameter. The new logging methods add the eventId to the Properties dictionary before logging the event.
public void Info(int eventId, object message, System.Exception t)
{
if (this.IsInfoEnabled)
{
LoggingEvent loggingEvent = new LoggingEvent(ThisDeclaringType, Logger.Repository, Logger.Name, Level.Info, message, t);
loggingEvent.Properties["EventID"] = eventId;
Logger.Log(loggingEvent);
}
}
c. Log4net supports a ThreadContext object that contains a Properties dictionary. An application could set the EventID and Category properties in this dictionary and then when the thread calls a logging method, the values will be used by the EventLogAppender.
log4net.ThreadContext.Properties["EventID"] = 5;
Some helpful references:
Log4net home page
Log4net SDK reference
Log4net samples
Log4net source
Enhancing log4net exception logging
Log4Net Tutorial pt 6: Log Event Context
Customizing Event Log Categories
EventSourceCreationData.CategoryResourceFile Property
Event Logging Elements
EventLog.WriteEntry Method
Well, the solution was to build the extension project "log4net.Ext.EventID" and to use its types: IEventIDLog, EventIDLogImpl and EventIDLogManager.
Another solution is to add a custom Filter as described here: Enhancing log4net exception logging (direct link to the Gist just in case).
As the author points out:
... EventLogAppender uses inline consts to check them. Once they are added they will be used by the mentioned EventLogAppender to mark the given entries with EventId and Category.
The filter implementation will look like the code below (stripped down gist) with the added benefit that if you make GetEventId method public, you can write some tests against it
public class ExceptionBasedLogEnhancer : FilterSkeleton
{
private const string EventLogKeyEventId = "EventID";
public override FilterDecision Decide(LoggingEvent loggingEvent)
{
var ex = loggingEvent.ExceptionObject;
if (ex != null)
{
loggingEvent.Properties[EventLogKeyEventId] = GetEventId(ex);
}
return FilterDecision.Neutral;
}
private static short GetEventId(Exception ex)
{
// more fancy implementation, like getting hash of ex properties
// can be provided, or mapping types of exceptions to eventids
// return no more than short.MaxValue, otherwise the EventLog will throw
return 0;
}
}
Extend ILog.Info() to take an event ID:
public static class LogUtils
{
public static void Info(this ILog logger, int eventId, object message)
{
log4net.ThreadContext.Properties["EventID"] = eventId;
logger.Info(message);
log4net.ThreadContext.Properties["EventID"] = 0; // back to default
}
}
Then call it like this:
using LogUtils;
private static readonly log4net.ILog _logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
_logger.Info(3, "First shalt thou take out the Holy Pin, then shalt thou count to three.");

Resources