/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.nifi.processors.windows.event.log; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Kernel32; import com.sun.jna.platform.win32.W32Errors; import com.sun.jna.platform.win32.WinDef; import com.sun.jna.platform.win32.WinError; import com.sun.jna.platform.win32.WinNT; import org.apache.commons.io.Charsets; import org.apache.nifi.annotation.lifecycle.OnScheduled; import org.apache.nifi.annotation.lifecycle.OnStopped; import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processors.windows.event.log.jna.EventSubscribeXmlRenderingCallback; import org.apache.nifi.processors.windows.event.log.jna.WEvtApi; import org.apache.nifi.util.MockFlowFile; import org.apache.nifi.util.MockProcessSession; import org.apache.nifi.util.MockSessionFactory; import org.apache.nifi.util.ReflectionUtils; import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunners; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(JNAJUnitRunner.class) public class ConsumeWindowsEventLogTest { @Mock Kernel32 kernel32; @Mock WEvtApi wEvtApi; @Mock WinNT.HANDLE subscriptionHandle; @Mock Pointer subscriptionPointer; private ConsumeWindowsEventLog evtSubscribe; private TestRunner testRunner; public static List<WinNT.HANDLE> mockEventHandles(WEvtApi wEvtApi, Kernel32 kernel32, List<String> eventXmls) { List<WinNT.HANDLE> eventHandles = new ArrayList<>(); for (String eventXml : eventXmls) { WinNT.HANDLE eventHandle = mock(WinNT.HANDLE.class); when(wEvtApi.EvtRender(isNull(WinNT.HANDLE.class), eq(eventHandle), eq(WEvtApi.EvtRenderFlags.EVENT_XML), anyInt(), any(Pointer.class), any(Pointer.class), any(Pointer.class))).thenAnswer(invocation -> { Object[] arguments = invocation.getArguments(); Pointer bufferUsed = (Pointer) arguments[5]; byte[] array = Charsets.UTF_16LE.encode(eventXml).array(); if (array.length > (int) arguments[3]) { when(kernel32.GetLastError()).thenReturn(W32Errors.ERROR_INSUFFICIENT_BUFFER).thenReturn(W32Errors.ERROR_SUCCESS); } else { ((Pointer) arguments[4]).write(0, array, 0, array.length); } bufferUsed.setInt(0, array.length); return false; }); eventHandles.add(eventHandle); } return eventHandles; } @Before public void setup() { evtSubscribe = new ConsumeWindowsEventLog(wEvtApi, kernel32); when(subscriptionHandle.getPointer()).thenReturn(subscriptionPointer); when(wEvtApi.EvtSubscribe(isNull(WinNT.HANDLE.class), isNull(WinNT.HANDLE.class), eq(ConsumeWindowsEventLog.DEFAULT_CHANNEL), eq(ConsumeWindowsEventLog.DEFAULT_XPATH), isNull(WinNT.HANDLE.class), isNull(WinDef.PVOID.class), isA(EventSubscribeXmlRenderingCallback.class), eq(WEvtApi.EvtSubscribeFlags.SUBSCRIBE_TO_FUTURE | WEvtApi.EvtSubscribeFlags.EVT_SUBSCRIBE_STRICT))) .thenReturn(subscriptionHandle); testRunner = TestRunners.newTestRunner(evtSubscribe); } @Test(timeout = 10 * 1000) public void testProcessesBlockedEvents() throws UnsupportedEncodingException { testRunner.setProperty(ConsumeWindowsEventLog.MAX_EVENT_QUEUE_SIZE, "1"); testRunner.run(1, false, true); EventSubscribeXmlRenderingCallback renderingCallback = getRenderingCallback(); List<String> eventXmls = Arrays.asList("one", "two", "three", "four", "five", "six"); List<WinNT.HANDLE> eventHandles = mockEventHandles(wEvtApi, kernel32, eventXmls); AtomicBoolean done = new AtomicBoolean(false); new Thread(() -> { for (WinNT.HANDLE eventHandle : eventHandles) { renderingCallback.onEvent(WEvtApi.EvtSubscribeNotifyAction.DELIVER, null, eventHandle); } done.set(true); }).start(); // Wait until the thread has really started while (testRunner.getFlowFilesForRelationship(ConsumeWindowsEventLog.REL_SUCCESS).size() == 0) { testRunner.run(1, false, false); } // Process rest of events while (!done.get()) { testRunner.run(1, false, false); } testRunner.run(1, true, false); List<MockFlowFile> flowFilesForRelationship = testRunner.getFlowFilesForRelationship(ConsumeWindowsEventLog.REL_SUCCESS); assertEquals(eventXmls.size(), flowFilesForRelationship.size()); for (int i = 0; i < eventXmls.size(); i++) { flowFilesForRelationship.get(i).assertContentEquals(eventXmls.get(i)); } } @Test public void testStopProcessesQueue() throws InvocationTargetException, IllegalAccessException { testRunner.run(1, false); List<String> eventXmls = Arrays.asList("one", "two", "three"); for (WinNT.HANDLE eventHandle : mockEventHandles(wEvtApi, kernel32, eventXmls)) { getRenderingCallback().onEvent(WEvtApi.EvtSubscribeNotifyAction.DELIVER, null, eventHandle); } ReflectionUtils.invokeMethodsWithAnnotation(OnStopped.class, evtSubscribe, testRunner.getProcessContext()); List<MockFlowFile> flowFilesForRelationship = testRunner.getFlowFilesForRelationship(ConsumeWindowsEventLog.REL_SUCCESS); assertEquals(eventXmls.size(), flowFilesForRelationship.size()); for (int i = 0; i < eventXmls.size(); i++) { flowFilesForRelationship.get(i).assertContentEquals(eventXmls.get(i)); } } @Test public void testScheduleErrorThenTriggerSubscribe() throws InvocationTargetException, IllegalAccessException { evtSubscribe = new ConsumeWindowsEventLog(wEvtApi, kernel32); when(subscriptionHandle.getPointer()).thenReturn(subscriptionPointer); when(wEvtApi.EvtSubscribe(isNull(WinNT.HANDLE.class), isNull(WinNT.HANDLE.class), eq(ConsumeWindowsEventLog.DEFAULT_CHANNEL), eq(ConsumeWindowsEventLog.DEFAULT_XPATH), isNull(WinNT.HANDLE.class), isNull(WinDef.PVOID.class), isA(EventSubscribeXmlRenderingCallback.class), eq(WEvtApi.EvtSubscribeFlags.SUBSCRIBE_TO_FUTURE | WEvtApi.EvtSubscribeFlags.EVT_SUBSCRIBE_STRICT))) .thenReturn(null).thenReturn(subscriptionHandle); testRunner = TestRunners.newTestRunner(evtSubscribe); testRunner.run(1, false, true); WinNT.HANDLE handle = mockEventHandles(wEvtApi, kernel32, Arrays.asList("test")).get(0); List<EventSubscribeXmlRenderingCallback> renderingCallbacks = getRenderingCallbacks(2); EventSubscribeXmlRenderingCallback subscribeRenderingCallback = renderingCallbacks.get(0); EventSubscribeXmlRenderingCallback renderingCallback = renderingCallbacks.get(1); renderingCallback.onEvent(WEvtApi.EvtSubscribeNotifyAction.DELIVER, null, handle); testRunner.run(1, true, false); assertNotEquals(subscribeRenderingCallback, renderingCallback); verify(wEvtApi).EvtClose(subscriptionHandle); } @Test public void testScheduleError() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { evtSubscribe = new ConsumeWindowsEventLog(wEvtApi, kernel32); when(wEvtApi.EvtSubscribe(isNull(WinNT.HANDLE.class), isNull(WinNT.HANDLE.class), eq(ConsumeWindowsEventLog.DEFAULT_CHANNEL), eq(ConsumeWindowsEventLog.DEFAULT_XPATH), isNull(WinNT.HANDLE.class), isNull(WinDef.PVOID.class), isA(EventSubscribeXmlRenderingCallback.class), eq(WEvtApi.EvtSubscribeFlags.SUBSCRIBE_TO_FUTURE | WEvtApi.EvtSubscribeFlags.EVT_SUBSCRIBE_STRICT))) .thenReturn(null); when(kernel32.GetLastError()).thenReturn(WinError.ERROR_ACCESS_DENIED); testRunner = TestRunners.newTestRunner(evtSubscribe); testRunner.run(1); assertEquals(0, getCreatedSessions(testRunner).size()); verify(wEvtApi, never()).EvtClose(any(WinNT.HANDLE.class)); } @Test public void testStopClosesHandle() { testRunner.run(1); verify(wEvtApi).EvtClose(subscriptionHandle); } @Test(expected = ProcessException.class) public void testScheduleQueueStopThrowsException() throws Throwable { ReflectionUtils.invokeMethodsWithAnnotation(OnScheduled.class, evtSubscribe, testRunner.getProcessContext()); WinNT.HANDLE handle = mockEventHandles(wEvtApi, kernel32, Arrays.asList("test")).get(0); getRenderingCallback().onEvent(WEvtApi.EvtSubscribeNotifyAction.DELIVER, null, handle); try { ReflectionUtils.invokeMethodsWithAnnotation(OnStopped.class, evtSubscribe, testRunner.getProcessContext()); } catch (InvocationTargetException e) { throw e.getCause(); } } public EventSubscribeXmlRenderingCallback getRenderingCallback() { return getRenderingCallbacks(1).get(0); } public List<EventSubscribeXmlRenderingCallback> getRenderingCallbacks(int times) { ArgumentCaptor<EventSubscribeXmlRenderingCallback> callbackArgumentCaptor = ArgumentCaptor.forClass(EventSubscribeXmlRenderingCallback.class); verify(wEvtApi, times(times)).EvtSubscribe(isNull(WinNT.HANDLE.class), isNull(WinNT.HANDLE.class), eq(ConsumeWindowsEventLog.DEFAULT_CHANNEL), eq(ConsumeWindowsEventLog.DEFAULT_XPATH), isNull(WinNT.HANDLE.class), isNull(WinDef.PVOID.class), callbackArgumentCaptor.capture(), eq(WEvtApi.EvtSubscribeFlags.SUBSCRIBE_TO_FUTURE | WEvtApi.EvtSubscribeFlags.EVT_SUBSCRIBE_STRICT)); return callbackArgumentCaptor.getAllValues(); } @Test public void testGetSupportedPropertyDescriptors() { assertEquals(ConsumeWindowsEventLog.PROPERTY_DESCRIPTORS, evtSubscribe.getSupportedPropertyDescriptors()); } @Test public void testGetRelationships() { assertEquals(ConsumeWindowsEventLog.RELATIONSHIPS, evtSubscribe.getRelationships()); } private static Set<MockProcessSession> getCreatedSessions(TestRunner testRunner) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { MockSessionFactory processSessionFactory = (MockSessionFactory) testRunner.getProcessSessionFactory(); Method getCreatedSessions = processSessionFactory.getClass().getDeclaredMethod("getCreatedSessions"); getCreatedSessions.setAccessible(true); return (Set<MockProcessSession>) getCreatedSessions.invoke(processSessionFactory); } }