com.hcl.domino.jna.data.JNAAgent Maven / Gradle / Ivy
/*
* ==========================================================================
* 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.data;
import java.io.IOException;
import java.io.Writer;
import java.lang.ref.ReferenceQueue;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import com.hcl.domino.DominoException;
import com.hcl.domino.commons.errors.INotesErrorConstants;
import com.hcl.domino.commons.gc.APIObjectAllocations;
import com.hcl.domino.commons.gc.IAPIObject;
import com.hcl.domino.commons.gc.IGCDominoClient;
import com.hcl.domino.commons.util.NotesErrorUtils;
import com.hcl.domino.commons.util.StringTokenizerExt;
import com.hcl.domino.commons.util.StringUtil;
import com.hcl.domino.data.Agent;
import com.hcl.domino.data.Database;
import com.hcl.domino.data.Database.OpenDocumentMode;
import com.hcl.domino.data.Document;
import com.hcl.domino.data.IAdaptable;
import com.hcl.domino.exception.AgentTimeoutException;
import com.hcl.domino.exception.ObjectDisposedException;
import com.hcl.domino.jna.BaseJNAAPIObject;
import com.hcl.domino.jna.internal.Mem;
import com.hcl.domino.jna.internal.NotesNamingUtils;
import com.hcl.domino.jna.internal.NotesStringUtils;
import com.hcl.domino.jna.internal.capi.NotesCAPI;
import com.hcl.domino.jna.internal.gc.allocations.JNAAgentAllocations;
import com.hcl.domino.jna.internal.gc.allocations.JNADatabaseAllocations;
import com.hcl.domino.jna.internal.gc.allocations.JNADocumentAllocations;
import com.hcl.domino.jna.internal.gc.handles.DHANDLE;
import com.hcl.domino.jna.internal.gc.handles.LockUtil;
import com.hcl.domino.jna.internal.structs.NotesUniversalNoteIdStruct;
import com.hcl.domino.misc.NotesConstants;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
public class JNAAgent extends BaseJNAAPIObject implements Agent {
private int m_agentNoteId;
private JNADocument m_agentDoc;
private String m_agentNameAndAliases;
private String m_comment;
public JNAAgent(JNADatabase parentDb, int agentNoteId, IAdaptable adaptable) {
super(parentDb);
m_agentNoteId = agentNoteId;
DHANDLE handle = adaptable.getAdapter(DHANDLE.class);
if (handle==null) {
throw new DominoException(0, "Missing expected agent handle");
}
getAllocations().setAgentHandle(handle);
setInitialized();
}
@Override
public Database getParentDatabase() {
return (Database) getParent();
}
/**
* Opens the agent note and stores it in a variable for reuse
*
* @return agent note
*/
private Document getAgentDoc() {
if (m_agentDoc==null || m_agentDoc.isDisposed()) {
m_agentDoc = (JNADocument) getParentDatabase().getDocumentById(m_agentNoteId, EnumSet.noneOf(OpenDocumentMode.class)).get();
}
return m_agentDoc;
}
@SuppressWarnings("rawtypes")
@Override
protected JNAAgentAllocations createAllocations(IGCDominoClient> parentDominoClient, APIObjectAllocations parentAllocations,
ReferenceQueue super IAPIObject> queue) {
return new JNAAgentAllocations(parentDominoClient, parentAllocations, this, queue);
}
@Override
public String getName() {
if (m_agentNameAndAliases==null) {
m_agentNameAndAliases = getAgentDoc().get("$Title", String.class, ""); //$NON-NLS-1$ //$NON-NLS-2$
}
int iPos = m_agentNameAndAliases.indexOf('|');
return iPos!=-1 ? m_agentNameAndAliases.substring(0, iPos) : m_agentNameAndAliases;
}
@Override
public List getAliases() {
if (m_agentNameAndAliases==null) {
m_agentNameAndAliases = getAgentDoc().get("$Title", String.class, ""); //$NON-NLS-1$ //$NON-NLS-2$
}
int iPos = m_agentNameAndAliases.indexOf('|');
if (iPos==-1) {
return Collections.emptyList();
}
List aliases = new ArrayList<>();
StringTokenizerExt st = new StringTokenizerExt(m_agentNameAndAliases.substring(iPos+1), "|"); //$NON-NLS-1$
while (st.hasMoreTokens()) {
String currToken = st.nextToken().trim();
if (!StringUtil.isEmpty(currToken)) {
aliases.add(currToken);
}
}
return aliases;
}
@Override
public String getUNID() {
return getParentDatabase().toUNID(m_agentNoteId);
}
@Override
public int getNoteID() {
return m_agentNoteId;
}
@Override
public String getComment() {
if (m_comment==null) {
m_comment = getAgentDoc().get("$Comment", String.class, ""); //$NON-NLS-1$ //$NON-NLS-2$
}
return m_comment;
}
@Override
public boolean isEnabled() {
checkDisposed();
boolean enabled = LockUtil.lockHandle(getAllocations().getAgentHandle(), (hAgentByVal) -> {
return NotesCAPI.get().AgentIsEnabled(hAgentByVal);
});
return enabled;
}
@Override
public boolean isRunAsWebUser() {
checkDisposed();
boolean isRunAsWebUser = LockUtil.lockHandle(getAllocations().getAgentHandle(), (hAgentByVal) -> {
return NotesCAPI.get().IsRunAsWebUser(hAgentByVal);
});
return isRunAsWebUser;
}
@Override
public AgentRunContext createAgentContext() {
return new JNAAgentRunContext();
}
@Override
public void run(AgentRunContext runCtx) {
checkDisposed();
Document doc = runCtx.getDocumentContext().orElse(null);
if (doc instanceof JNADocument && ((JNADocument)doc).isDisposed()) {
throw new ObjectDisposedException(doc);
}
//always reopen the DB so that the agent runs in a consistent state; otherwise
//we would have Session.EffectiveUsername set to the signer and Evaluate("@Username")
//return the user specified via AgentSetUserName
int runFlags = NotesConstants.AGENT_REOPEN_DB;
int ctxFlags = 0;
boolean checkSecurity = runCtx.isCheckSecurity();
if (checkSecurity) {
ctxFlags = NotesConstants.AGENT_SECURITY_ON;
}
final int fCtxFlags = ctxFlags;
Optional stdOut = runCtx.getOutputWriter();
int timeoutSeconds = runCtx.getTimeoutSeconds();
int paramDocId = runCtx.getParamDocId();
LockUtil.lockHandle(getAllocations().getAgentHandle(), (hAgentByVal) -> {
DHANDLE.ByReference rethContext = DHANDLE.newInstanceByReference();
short result = NotesCAPI.get().AgentCreateRunContextExt(hAgentByVal, null, 0, fCtxFlags, rethContext);
NotesErrorUtils.checkResult(result);
LockUtil.lockHandle(rethContext, (hContextByVal) -> {
try {
if (stdOut.isPresent()) {
//redirect stdout to in memory buffer
short redirType = NotesConstants.AGENT_REDIR_MEMORY;
short resultSetOut = NotesCAPI.get().AgentRedirectStdout(hContextByVal, redirType);
NotesErrorUtils.checkResult(resultSetOut);
}
if (timeoutSeconds!=0) {
short timeOutResult = NotesCAPI.get().AgentSetTimeExecutionLimit(hContextByVal, timeoutSeconds);
NotesErrorUtils.checkResult(timeOutResult);
}
if (doc!=null) {
JNADocumentAllocations docAllocations = (JNADocumentAllocations) doc.getAdapter(APIObjectAllocations.class);
if (docAllocations!=null) {
LockUtil.lockHandle(docAllocations.getNoteHandle(), (hNoteByVal) -> {
NotesCAPI.get().AgentSetDocumentContext(hContextByVal, hNoteByVal);
return 0;
});
}
}
if (paramDocId!=0) {
NotesCAPI.get().SetParamNoteID(hContextByVal, paramDocId);
}
short runResult = NotesCAPI.get().AgentRun(hAgentByVal, hContextByVal, null, runFlags);
final short runResultMasked = (short) (runResult & NotesConstants.ERR_MASK);
if (runResultMasked==INotesErrorConstants.ERR_ASSISTANT_TIMEOUT) {
//fill placeholders for agent, database and signer in timeout error
//Execution time limit exceeded by Agent '%s' in database '%p'. Agent signer '%a'.
//e.g.
//Execution time limit exceeded by Agent 'testagent' in database 'Server1/TestOrg test\db.nsf'. Agent signer 'Karsten Lehmann/Mindoo'.
String errMsg = NotesErrorUtils.errToString(INotesErrorConstants.ERR_ASSISTANT_TIMEOUT);
errMsg = errMsg.replace("%s", getName()); //$NON-NLS-1$
errMsg = errMsg.replace("%p", NotesNamingUtils.toAbbreviatedName(getParentDatabase().getServer())+" "+getParentDatabase().getRelativeFilePath()); //$NON-NLS-1$
errMsg = errMsg.replace("%a", getSigner()); //$NON-NLS-1$
throw new AgentTimeoutException(runResult, errMsg);
}
NotesErrorUtils.checkResult(runResult);
if (stdOut.isPresent()) {
DHANDLE.ByReference retBufHandle = DHANDLE.newInstanceByReference();
IntByReference retSize = new IntByReference();
NotesCAPI.get().AgentQueryStdoutBuffer(hContextByVal, retBufHandle, retSize);
int iRetSize = retSize.getValue();
if (iRetSize!=0) {
LockUtil.lockHandle(retBufHandle, (hBufByVal) -> {
Pointer bufPtr = Mem.OSLockObject(hBufByVal);
try {
//decode std out buffer content
String bufContentUnicode = NotesStringUtils.fromLMBCS(bufPtr, iRetSize);
try {
stdOut.get().write(bufContentUnicode);
} catch (IOException e) {
throw new DominoException("Error writing agent output", e);
}
return 0;
}
finally {
Mem.OSUnlockObject(hBufByVal);
}
});
}
}
}
finally {
NotesCAPI.get().AgentDestroyRunContext(hContextByVal);
}
return 0;
});
return 0;
});
}
@Override
public void runOnServer(boolean suppressPrintToConsole) {
runOnServer(0, suppressPrintToConsole);
}
@Override
public void runOnServer(int noteIdParamDoc, boolean suppressPrintToConsole) {
checkDisposed();
boolean bForeignServer = false;
JNADatabaseAllocations dbAllocations = (JNADatabaseAllocations) getParentDatabase().getAdapter(APIObjectAllocations.class);
short result = LockUtil.lockHandles(dbAllocations.getDBHandle(), getAllocations().getAgentHandle(),
(hDbByVal, hAgentByVal) -> {
return NotesCAPI.get().ClientRunServerAgent(hDbByVal,
m_agentNoteId, noteIdParamDoc, bForeignServer ? 1 : 0, suppressPrintToConsole ? 1 : 0);
});
NotesErrorUtils.checkResult(result);
}
@Override
public String toStringLocal() {
Database parentDb = getParentDatabase();
return MessageFormat.format(
"JNAAgent [db={0}!!{1}, noteid={2}, name={3}]", //$NON-NLS-1$
parentDb.getServer(), parentDb.getRelativeFilePath(), m_agentNoteId, getName()
);
}
@Override
public Optional getAgentSavedData() {
checkDisposed();
Database db = getParentDatabase();
JNADatabaseAllocations dbAllocations = (JNADatabaseAllocations) db.getAdapter(APIObjectAllocations.class);
NotesUniversalNoteIdStruct.ByReference retUNID = NotesUniversalNoteIdStruct.newInstanceByReference();
short result = LockUtil.lockHandle(dbAllocations.getDBHandle(), (hDbByVal) -> {
return NotesCAPI.get().AssistantGetLSDataNote(hDbByVal, m_agentNoteId, retUNID);
});
NotesErrorUtils.checkResult(result);
String unid = retUNID.toString();
if (StringUtil.isEmpty(unid) || "00000000000000000000000000000000".equals(unid)) { //$NON-NLS-1$
return Optional.empty();
}
else {
return db.getDocumentByUNID(unid);
}
}
@Override
public String getSigner() {
return getAgentDoc().getSigner();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy