All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hcl.domino.jna.internal.capi.NotesCAPI Maven / Gradle / Ivy

There is a newer version: 1.44.0
Show newest version
/*
 * ==========================================================================
 * Copyright (C) 2019-2022 HCL America, Inc. ( http://www.hcl.com/ )
 *                            All rights reserved.
 * ==========================================================================
 * Licensed 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 .
 *
 * 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 com.hcl.domino.jna.internal.capi;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;

import com.hcl.domino.DominoException;
import com.hcl.domino.commons.util.DominoUtils;
import com.hcl.domino.commons.util.PlatformUtils;
import com.hcl.domino.exception.DominoInitException;
import com.hcl.domino.jna.JNADominoProcess;
import com.hcl.domino.jna.internal.capi.INotesCAPI.NativeFunctionName;
import com.sun.jna.Function;
import com.sun.jna.FunctionMapper;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Structure;

public class NotesCAPI {
	private static volatile INotesCAPI m_instance;
	private static volatile INotesCAPI m_instanceWithStackLogging;
	
	@SuppressWarnings("unused")
	private static Class m_nativeClazz;
	private static DominoException m_initError;
	private static int m_platformAlignment;

	static {
		if (PlatformUtils.isWindows()) {
			if (PlatformUtils.is64Bit()) {
				m_platformAlignment = Structure.ALIGN_DEFAULT;
			}
			else {
				m_platformAlignment = Structure.ALIGN_NONE;
			}
		}
		else if (PlatformUtils.isMac()) {
			if (PlatformUtils.is64Bit()) {
				m_platformAlignment = Structure.ALIGN_NONE;
			}
			else {
				m_platformAlignment = Structure.ALIGN_DEFAULT;
			}
		}
		else if (PlatformUtils.isLinux()) {
			m_platformAlignment = Structure.ALIGN_DEFAULT;
		}
		else {
			m_platformAlignment = -1;
		}
	}

	public static int getPlatformAlignment() {
		return m_platformAlignment;
	}

	 /**
   * Loads the Domino shared library and returns a Java Proxy object to map C functions to Java methods.
   * 
   * @return C API proxy object to call C API methods
   * @throws DominoInitException of Domino shared library cannot be found or the C API init failed
   */
  public static synchronized INotesCAPI get() {
    return get(false);
  }
  
	/**
	 * Loads the Domino shared library and returns a Java Proxy object to map C functions to Java methods.
	 * 
	 * @param skipThreadCheck true to not check if the current thread has been initialized for Domino
	 * @return C API proxy object to call C API methods
	 * @throws DominoInitException of Domino shared library cannot be found or the C API init failed
	 */
	public static synchronized INotesCAPI get(boolean skipThreadCheck) {
		if (m_instance==null && m_initError==null) {
			try {
				m_instance = createAPI();
			}
			catch (Throwable e) {
				m_initError = new DominoInitException("Error loading Notes/Domino shared library. Please make sure that the location of nnotes.dll (Windows), libnotes.so (Linux) or libnotes.dylib is added to the PATH.", e);
			}
		}
	
		if (m_initError!=null) {
			throw m_initError;
		}
		
		if (!skipThreadCheck) {
	    JNADominoProcess.checkThreadEnabledForDomino();
		}
		
		boolean useCallstackLogging = DominoUtils.checkBooleanProperty("jnx.callstacklog", "JNX_CALLSTACKLOG"); //$NON-NLS-1$ //$NON-NLS-2$
		
		if (useCallstackLogging) {
			if (m_instanceWithStackLogging==null) {
				m_instanceWithStackLogging = wrapWithCrashStackLogging(INotesCAPI.class, m_instance);
			}
			
			return m_instanceWithStackLogging;
		}
		else {
			return m_instance;
		}
	}
	
	private static INotesCAPI createAPI() {
		//keep reference to Native as described here: https://github.com/java-native-access/jna/blob/master/www/FrequentlyAskedQuestions.md#why-does-the-vm-sometimes-crash-in-my-shutdown-hook-on-windows
		m_nativeClazz = Native.class;

		//enforce using the extracted JNA .dll/.so file instead of what we find on the PATH
		DominoUtils.setJavaProperty("jna.nosys", "true"); //$NON-NLS-1$ //$NON-NLS-2$

		if (PlatformUtils.isWindows()) {
			if (PlatformUtils.is64Bit()) {
				m_platformAlignment = Structure.ALIGN_DEFAULT;
			}
			else {
				m_platformAlignment = Structure.ALIGN_NONE;
			}
		}
		else if (PlatformUtils.isMac()) {
			if (PlatformUtils.is64Bit()) {
				m_platformAlignment = Structure.ALIGN_NONE;
			}
			else {
				m_platformAlignment = Structure.ALIGN_DEFAULT;
			}
		}
		else if (PlatformUtils.isLinux()) {
			m_platformAlignment = Structure.ALIGN_DEFAULT;
		}
		else {
			String osName = DominoUtils.getJavaProperty("os.name", null); //$NON-NLS-1$
			m_initError = new DominoException(0, MessageFormat.format("Platform is unknown or not supported: {0}", osName));
			return null;
		}

		Map libraryOptions = new HashMap<>();
		libraryOptions.put(Library.OPTION_CLASSLOADER, NotesCAPI.class.getClassLoader());
		libraryOptions.put(Library.OPTION_FUNCTION_MAPPER, new FunctionNameAnnotationMapper());

		if (PlatformUtils.isWin32()) {
			libraryOptions.put(Library.OPTION_CALLING_CONVENTION, Function.ALT_CONVENTION); // set w32 stdcall convention
		}

		INotesCAPI api;
		if (PlatformUtils.isWindows()) {
			api = Native.load("nnotes", INotesCAPI.class, libraryOptions); //$NON-NLS-1$
		}
		else {
			api = Native.load("notes", INotesCAPI.class, libraryOptions); //$NON-NLS-1$
		}
		return api;
	}

	/**
	 * Wraps the specified API object to dump caller stacktraces right before invoking
	 * native methods
	 * 
	 * @param apiClazz API interface
	 * @return api API implementation
	 */
	@SuppressWarnings("unchecked")
	static  T wrapWithCrashStackLogging(final Class apiClazz, final T api) {
		try {
			return AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
				InvocationHandlerWithStacktraceLogging invocationHandler = new InvocationHandlerWithStacktraceLogging(api);
				return (T) Proxy.newProxyInstance(api.getClass().getClassLoader(), new Class[] {apiClazz}, invocationHandler);
			});
		} catch (PrivilegedActionException e) {
			e.printStackTrace();
			return api;
		}
	}

	/**
	 * Custom FunctionMapper to use {@link NativeFunctionName} annotations to
	 * define the name of the native methods instead of just using the method name.
	 */
	static class FunctionNameAnnotationMapper implements FunctionMapper {
	    @Override
	    public String getFunctionName(NativeLibrary nativeLibrary, Method method) {
	        NativeFunctionName annotation = method.getAnnotation(NativeFunctionName.class);
	        // just return the function's name if the annotation is not applied
	        if (annotation == null) {
	        	return method.getName();
	        }
	        else {
	        	return annotation.name();
	        }
	    }
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy