/*
* 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.jna;
import com.sun.jna.Memory;
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.WinNT;
import org.apache.commons.io.Charsets;
import org.apache.nifi.logging.ComponentLog;
import java.util.function.Consumer;
/**
* Callback that will render the XML representation of the event using native Windows API
*/
public class EventSubscribeXmlRenderingCallback implements WEvtApi.EVT_SUBSCRIBE_CALLBACK {
public static final String RECEIVED_THE_FOLLOWING_WIN32_ERROR = "Received the following Win32 error: ";
public static final int INITIAL_BUFFER_SIZE = 1024;
public static final String EVT_RENDER_RETURNED_THE_FOLLOWING_ERROR_CODE = "EvtRender returned the following error code ";
public static final String MISSING_EVENT_MESSAGE = "Received missing event notification. Consider triggering processor more frequently or increasing queue size.";
private final ComponentLog logger;
private final Consumer<String> consumer;
private final int maxBufferSize;
private final WEvtApi wEvtApi;
private final Kernel32 kernel32;
private final ErrorLookup errorLookup;
private int size;
private Memory buffer;
private Memory used;
private Memory propertyCount;
public EventSubscribeXmlRenderingCallback(ComponentLog logger, Consumer<String> consumer, int maxBufferSize, WEvtApi wEvtApi, Kernel32 kernel32, ErrorLookup errorLookup) {
this.logger = logger;
this.consumer = consumer;
this.maxBufferSize = maxBufferSize;
this.wEvtApi = wEvtApi;
this.kernel32 = kernel32;
this.size = Math.min(maxBufferSize, INITIAL_BUFFER_SIZE);
this.errorLookup = errorLookup;
this.buffer = new Memory(size);
this.used = new Memory(4);
this.propertyCount = new Memory(4);
}
@Override
public synchronized int onEvent(int evtSubscribeNotifyAction, WinDef.PVOID userContext, WinNT.HANDLE eventHandle) {
if (logger.isDebugEnabled()) {
logger.debug("onEvent(" + evtSubscribeNotifyAction + ", " + userContext + ", " + eventHandle);
}
if (evtSubscribeNotifyAction == WEvtApi.EvtSubscribeNotifyAction.ERROR) {
if (eventHandle.getPointer().getInt(0) == WEvtApi.EvtSubscribeErrors.ERROR_EVT_QUERY_RESULT_STALE) {
logger.error(MISSING_EVENT_MESSAGE);
} else {
logger.error(RECEIVED_THE_FOLLOWING_WIN32_ERROR + eventHandle.getPointer().getInt(0));
}
} else if (evtSubscribeNotifyAction == WEvtApi.EvtSubscribeNotifyAction.DELIVER) {
wEvtApi.EvtRender(null, eventHandle, WEvtApi.EvtRenderFlags.EVENT_XML, size, buffer, used, propertyCount);
// Not enough room in buffer, resize so it's big enough
if (kernel32.GetLastError() == W32Errors.ERROR_INSUFFICIENT_BUFFER) {
int newMaxSize = used.getInt(0);
// Check for overflow or too big
if (newMaxSize < size || newMaxSize > maxBufferSize) {
logger.error("Dropping event " + eventHandle + " because it couldn't be rendered within " + maxBufferSize + " bytes.");
// Ignored, see https://msdn.microsoft.com/en-us/library/windows/desktop/aa385577(v=vs.85).aspx
return 0;
}
size = newMaxSize;
buffer = new Memory(size);
wEvtApi.EvtRender(null, eventHandle, WEvtApi.EvtRenderFlags.EVENT_XML, size, buffer, used, propertyCount);
}
int lastError = kernel32.GetLastError();
if (lastError == W32Errors.ERROR_SUCCESS) {
int usedBytes = used.getInt(0);
String string = Charsets.UTF_16LE.decode(buffer.getByteBuffer(0, usedBytes)).toString();
if (string.endsWith("\u0000")) {
string = string.substring(0, string.length() - 1);
}
consumer.accept(string);
} else {
logger.error(EVT_RENDER_RETURNED_THE_FOLLOWING_ERROR_CODE + errorLookup.getLastError() + ".");
}
}
// Ignored, see https://msdn.microsoft.com/en-us/library/windows/desktop/aa385577(v=vs.85).aspx
return 0;
}
}