I am attempting to use bbv.common.EventBroker with Ninject and am running into a few problems getting things wired up correctly.
I currently am using Ninject factories to create an item and then, since I subscribe to events, use the eventbroker to do the subscription. However when the register method is called I get an error that I can not find any information for at all. I am not even sure what the error means.
Error:
bbv.Common.EventBroker.Exceptions.RepeatedSubscriptionException occurred
Message=Cannot add more than one subscription handler method of the same subscriber one topic: 'Method1'.
Source=bbv.Common.EventBroker
StackTrace:
at bbv.Common.EventBroker.Internals.EventTopic.ThrowIfRepeatedSubscription(Object subscriber, String handlerMethodName)
at bbv.Common.EventBroker.Internals.EventTopic.AddSubscription(Object subscriber, MethodInfo handlerMethod, IHandler handler, IList`1 subscriptionMatchers)
at bbv.Common.EventBroker.Internals.EventInspector.HandleSubscriber(Object subscriber, Boolean register, MethodInfo methodInfo, EventSubscriptionAttribute attr, IEventTopicHost eventTopicHost)
at bbv.Common.EventBroker.Internals.EventInspector.ProcessSubscriber(Object subscriber, Boolean register, IEventTopicHost eventTopicHost)
at bbv.Common.EventBroker.EventBroker.Register(Object item)
InnerException:
Code:
public const string Topic1 = "Topic1";
public const string Topic2 = "Topic2";
public const string Topic3 = "Topic3";
public const string Topic4 = "Topic4";
public ItemHelper(IItem item, IEventBroker eventBroker)
{
_item = item;
eventBroker.Register(this);
}
[EventSubscription(Topic1, typeof(bbv.Common.EventBroker.Handlers.Publisher))]
public void Method1(object sender, SomeArgs1 args)
{
...
}
[EventSubscription(Topic2, typeof(bbv.Common.EventBroker.Handlers.Publisher))]
public void Method2(object sender, SomeArgs2 args)
{
...
}
[EventSubscription(Topic3, typeof(bbv.Common.EventBroker.Handlers.Publisher))]
public void Method3(object sender, SomeArgs3 args)
{
...
}
[EventSubscription(Topic4, typeof(bbv.Common.EventBroker.Handlers.Publisher))]
public void Method4(object sender, SomeArgs4 args)
{
...
}
It turned out that the class had an interface which had the [EventSubscription] attributes on its methods. The error message does make a lot more sense now that I know this.
Related
I created a biometric authentication service that starts an activity and registers a callback to a static EventHandler for the result.
The handler:
public class BiometricHandler : IBiometricHandler
{
private TaskCompletionSource<byte[]> taskCompletionSource;
public Task<byte[]> StartBiometricAuth()
{
Intent intent = new Intent(Android.App.Application.Context, typeof(BiometricActivity));
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
taskCompletionSource = new TaskCompletionSource<byte[]>();
BiometricActivity.BiometricEventHandler += BiometricCompleted;
return taskCompletionSource.Task;
}
private void BiometricCompleted(object sender, BiometricEventArgs e)
{
taskCompletionSource.SetResult(e.Success ? e.Payload : new byte[] { });
BiometricActivity.BiometricEventHandler -= BiometricCompleted;
}
}
And the activity (not the actual code obviously):
public class BiometricActivity : Activity
{
public static event EventHandler<BiometricEventArgs> BiometricEventHandler;
private readonly int BIOMETRIC_REQUEST = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Intent intent = new Intent(Application.Context, typeof(BiometricAuth));
StartActivityForResult(intent, BIOMETRIC_REQUEST);
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
BiometricEventHandler?.Invoke(
this,
new BiometricEventArgs(
true, new byte[] {1, 2, 3}
);
Finish();
}
}
The above code throws a NullReferenceException:
0xFFFFFFFFFFFFFFFF in System.Diagnostics.Debugger.Mono_UnhandledException_internal
0x1 in System.Diagnostics.Debugger.Mono_UnhandledException at /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/corlib/System.Diagnostics/Debugger.cs:125,4
0x20 in Android.Runtime.DynamicMethodNameCounter.57
0x6 in Xamarin.Forms.Platform.Android.PlatformConfigurationExtensions.OnThisPlatform<Xamarin.Forms.Application> at D:\a\_work\1\s\Xamarin.Forms.Platform.Android\PlatformConfigurationExtensions.cs:8,4
0xC in Xamarin.Forms.Platform.Android.AppCompat.FragmentContainer.OnResume at D:\a\_work\1\s\Xamarin.Forms.Platform.Android\AppCompat\FragmentContainer.cs:126,4
0x8 in AndroidX.Fragment.App.Fragment.n_OnResume at C:\a\_work\1\s\generated\androidx.fragment.fragment\obj\Release\monoandroid12.0\generated\src\AndroidX.Fragment.App.Fragment.cs:2570,4
0x11 in Android.Runtime.DynamicMethodNameCounter.57
And the console:
**System.NullReferenceException:** 'Object reference not set to an instance of an object.'
I narrowed it down to the Finish() method.
There is no exception when it is not called.
I tried calling Finish() from the handler using BroadcastReceiver, same result.
Why does Finish() throw this exception?
Well, it seems like the issue is an asynchronous race condition.
Finish() takes a bit longer to complete than the service to return the data.
Then the content page tries to perform a UI event, and since Finish() hasn't finished yet, the activity is still "visible" and the content page is not yet responsible for the UI thread. So the UI event fails with the exception.
Since there doesn't seem to be an easy way to await the Finish() call, I used Task.Await to delay the UI event for a bit and to Finish() complete.
Not ideal, but at least NullReferenceException is gone
I tried this:
void onShutdown(#Observes final ShutdownEvent event) throws InterruptedException {
log.infof("ShutdownEvent received, waiting for %s seconds before shutting down", shutdownWaitSeconds);
TimeUnit.SECONDS.sleep(shutdownWaitSeconds);
log.info("Continue shutting down");
}
But after receiving ShutdownEvent Quarkus already responds with 503 to http requests. Looks like this could be done with ShutdownListener in preShutdown method. I have implemented this listener but it does not get called yet. How do I register ShutdownListener?
Use case here is OpenShift sending requests to terminating pod.
Option 1: Create Quarkus extension
Instructions are here. ShutdownController is my own class implementing ShutdownListener where I have a sleep in preShutdown method.
class ShutdownControllerProcessor {
#BuildStep
FeatureBuildItem feature() {
return new FeatureBuildItem("shutdown-controller");
}
#BuildStep
ShutdownListenerBuildItem shutdownListener() {
// Called at build time. Default constructor will be called at runtime.
// Getting MethodNotFoundException when calling default constructor here.
return new ShutdownListenerBuildItem(new ShutdownController(10));
}
}
Option 2: Modify ShutdownRecorder private static final field
New shutdown listener can be added using reflection. This is a bit ugly solution.
registerIfNeeded() need to be called after Quarkus startup, for example with timer 1 second after #PostConstruct.
#ApplicationScoped
public class ListenerRegisterer {
public void registerIfNeeded() {
try {
tryToRegister();
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
private void tryToRegister() throws NoSuchFieldException, IllegalAccessException {
final var field = ShutdownRecorder.class.getDeclaredField("shutdownListeners");
field.setAccessible(true);
final var listeners = (List<ShutdownListener>) field.get(null);
if (listeners != null && !listeners.toString().contains("ShutdownController")) {
listeners.add(new ShutdownController(10));
setFinalStatic(field, listeners);
}
}
private static void setFinalStatic(final Field field, final Object newValue) throws NoSuchFieldException, IllegalAccessException {
field.setAccessible(true);
final var modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
}
I'm writing a Python script in IronPython that uses a C# DLL implementing the observer/observable pattern, the C# library has the observable class and I want to implement the observer in Python.
I'm registering my Python function as the observer method, but when it gets called, I get an "System.MissingMemberException: 'PeripheralDiscoveredEventArgs' object has no attribute 'ChangeType' exception thrown.
I can't find any documentation on how to use custom arguments with C# events when using IronPython, looked here, here, here and here.
My Python code is:
central = None
def MyCallback(sender, event_args):
print event_args.ChangeType, event_args.Name
def main():
global central
central = objFactory.GetCentral();
d = System.EventHandler[CustomEventArgs](MyCallback)
central.myHandler += d
(do something)
if __name__ == "__main__":
main()
I also tried to do just this:
central.myHandler += MyCallback
But I got the same exception.
The central variable as a Central instantance and the central.myHandler property in the is defined in the Central class as:
public event EventHandler<CustomEventArgs> myHandler = delegate { };
The CustomEventArgs class is defined as:
public class CustomEventArgs: EventArgs
{
public CustomEventArgs(Class1 obj1, int i, Class2 obj2, bool tf);
public Class1 Obj1 { get; }
public int I{ get; }
public Class2 Obj2t { get; }
public bool Tf{ get; }
}
The observable class method that calls my callback is:
internal abstract class EventHelper
{
internal static void TriggerEvent<T>(EventHandler<T> eventHandler, object source, T args)
{
// event handle no used ?
if (eventHandler == null) return;
try
{
// call event handler
eventHandler(source, args);
}
catch (Exception ex)
{
var target = eventHandler.Target != null ? eventHandler.Target : "unknown";
var methodName = (eventHandler.Method != null && eventHandler.Method.Name != null) ? eventHandler.Method.Name : "unknown";
Log.Error(string.Format("Exception in event handler '{0}' in '{1}'", methodName, target), ex);
}
}
The whole exception is:
"System.MissingMemberException: 'CustomEventArgs' object has no attribute 'ChangeType'
at IronPython.Runtime.Binding.PythonGetMemberBinder.FastErrorGet`1.GetError(CallSite site, TSelfType target, CodeContext context)
at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
at __main__$1.MyCallback$74(PythonFunction $function, Object sender, Object event_args) in C:\\code\\Launcher.py:line 51
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
at _Scripting_(Object[] , Object , CustomEventArgs )
at System.EventHandler`1.Invoke(Object sender, TEventArgs e)
at EventHelper.TriggerEvent[T](EventHandler`1 eventHandler, Object source, T args) in C:\\code\\Utility\\EventHelper.cs:line 27"
Any pointers on how to use custom event arguments in C# with IronPython? Thanks!!
Environment:
Windows 7 Enterprise 64 bits
IronPython 2.7.8 (2.7.8.0) on .NET 4.0.30319.42000 (32-bit)
.NET Framework 4.5.2
Nevermind, it was my mistake (as usual). Trying to access a non-existent member of a class usually leads to errors/exceptions...
Just changing the observer code to :
def centralOnPeripheralDiscovered_callback(sender, event_args):
print sender
print event_args
Solves the problem. Thanks for the help!
On GWTP I am sending a UpdateDiagramBoxEvent with the code below, but the handler is executed twice. In other words, I can see that the sendUpdateDiagramBoxEvent() is executed only once, but it is received twice. The same is happening with many other events on my code. Any ideas of what is wrong, and how can I avoid this behaviour? THANKS.
Receive event
UpdateDiagramBoxHandler updateDiagramBoxHandler = new UpdateDiagramBoxHandler() {
#Override
public void onUpdateDiagramBox(UpdateDiagramBoxEvent event) {
doSomething();
}
};
Send event
EventUtil.sendUpdateDiagramBoxEvent(CellTableManager.this.eventBus,
BasicConstants.EventSubscriptors.VIEW, 0,
BasicConstants.EditableTableFields.DIAGRAMTYPE,
ClientState.getCurrentDiagramType().name());
public static void sendUpdateDiagramBoxEvent(final EventBus eventBus,
final BasicConstants.EventSubscriptors recipient,
final int index, final BasicConstants.EditableTableFields field,
final String value){
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
#Override
public void execute() {
UpdateDiagramBoxEvent updateDiagramBoxEvent =
new UpdateDiagramBoxEvent(transactionNumber, recipient,
field.toString(), value, index);
eventBus.fireEvent(updateDiagramBoxEvent);
}
});
}
Register event handler (from MyProjectPresenter.java)
#Inject PlaceManager placeManager;
#Override
protected void onBind() {
[...]
registerHandler(getEventBus().addHandler(UpdateDiagramBoxEvent.getType(),
updateDiagramBoxHandler));
}
It generally means that you simply registered your event handlers twice.
Is this GWTP and if so how are you registering your events/handlers? I seem to recall there is a pitfall that you can use either #ProxyEvent or addRegisteredHandler() but not both, or you will receive the events twice.
Hope that helps.
Cheers,
Or the bean in question might not be singleton.
This is quite straight forward(ish) to do is the event is 'real' as in now created by DynamicProxy, but I can't work anything out for a mocked event.
The best way to explain what I'm trying to achieve is with code, please see the comment lines in the test method:
using System;
using Moq;
using NUnit.Framework;
namespace MOQTest
{
[TestFixture]
public class EventsMoqTest
{
[Test]
public void DetachTest()
{
var hasEventMock = new Mock<IHasEvent>();
using (var observer = new Observer(hasEventMock.Object))
{
//Assert that hasEventMock.Object has handler attached
}
//Assert that hasEventMock.Object DOES NOT have handler attached
}
}
public interface IHasEvent
{
event EventHandler AnEvent;
}
public class Observer : IDisposable
{
private readonly IHasEvent _hasEvent;
private readonly EventHandler _hasEventOnAnEvent;
public Observer(IHasEvent hasEvent)
{
_hasEvent = hasEvent;
_hasEventOnAnEvent = _hasEvent_AnEvent;
_hasEvent.AnEvent += _hasEventOnAnEvent;
}
void _hasEvent_AnEvent(object sender, EventArgs e)
{}
public void Dispose()
{
_hasEvent.AnEvent -= _hasEventOnAnEvent;
}
}
}
Unfortunately, you can't. This isn't really a moq issue, but the way the C# event keyword works with delegates. See this SO answer for more information.