Запускаем фсё из LDN

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
470
я тут думал-подумал...
очередной раз упёршись рогом..., имеем программу/код, имеем домину/нотес, хотим запустить код
НО платформа LDN, в силу многих причин, сделать этого не может (полноценно) в моём случае - это java
Проблемы с жвм домины я пересказывать здесь не буду
Ряд ограничений:
- никаких аппсерверов
- получить данные по результатам работы кода в домину (например в агент)

Решение созрело неожиданно ;) - программа может выдавать результаты в консоль. С т.з. обмена данными - это ничем не отличается от того же http запроса.
Т.е. мы пишем в консоль, а домина забирает из неё результат, дело за малым - контролируемый запуск и парсинг результатов
решать будем... в джава (доминошной)
важный кусок про кодировку!
нам расколоть его поможет киножурнал кусок
Java:
       String launch=javaApp;
       launch=javaApp + jvmparams +" -jar " + classPath +jar;
       System.out.println("launch:"+launch);
 
       /* call the Hello class */
       try
       {
           theProcess = Runtime.getRuntime().exec(launch);
           /*OutputStream strmOut=theProcess.getOutputStream();*/
       }
       catch(IOException e)
       {
          System.err.println("Error on exec() method");
          e.printStackTrace();
       }
       return theProcess;
хы - скажут мне - удивил запуском программы
тут есть тонкость - получение stdout процесса theProcess.getInputStream() и опять возможен скепсис - как нам это контролировать?
забегая вперед - из ЛС "тоже хачу"
придётся напрячь программную мысль и
Java:
    static void printProcessInput(InputStream is, boolean bOutSuppress){
        Scanner scan = getScanner(is);
        System.out.println("! *Console output:" + !bOutSuppress);
        for (ScanResult res;(res=intercept(scan,".*",bOutSuppress)).isHasNext();){

        }
/*
        while(scan.hasNextLine()){
            String line = scan.nextLine();
              if (!bOutSuppress)
                  System.out.println(line);
        }
 
*/    }
в каментах вариант без разрыва цикла и он в ЛС не подойдёт - "мыто хочем" не массив (который может быть немерянным) получить, а поток и здесь его и получаем, а итерацию вынесем на уровень ЛС
мы уже почти получили желаемое вот она (подготовка)
Java:
    public static ScanResult intercept(Scanner scan, String regex, boolean bOutSuppress){
        boolean hasNext=scan.hasNextLine();
        ScanResult res=new ScanResult("",hasNext);
        if(hasNext){
            String s = scan.nextLine();
            res.setText(s);
            if (!bOutSuppress)
                System.out.println(s);
            Pattern p= Pattern.compile(regex,Pattern.CASE_INSENSITIVE );
            Matcher matcher = p.matcher(s);
            if (matcher.find())res.setRes(s);
        }
        return res;
    }
Потенциально (при больших перерывах в получении из потока) - рискуем схватить таймаут в приложении (а именно его мы запустили выше)
Но не будем доводить до абсурда ;)
Можно заметить - regex - именно оно позволит фильтровать "нужное" из потока (для отладки - нам и весь поток нужен)
ответный кусок в ЛС
Visual Basic:
    Function testProc()
        On Error GoTo ErrH
        GoTo Begin
ErrH:
        Error Err, RaiseError
Begin:
        Dim theProcess As JavaObject, scanRes As JavaObject, scan As Javaobject, errScan As Javaobject
        Set theProcess = getRuntime(me.userJDK, me.classPath, me.jar, me.jvmparams)

        Set scan=rtuObj.getScanner(theProcess.getInputStream())
        Set errScan=rtuObj.getScanner(theProcess.getErrorStream())

        Dim res() As String, cnt As Long
        'scanning program consiole output
        Do
            'set filter for lines to be accepted, true for skiping stdout from java
            Set scanRes=rtuObj.intercept(scan, regexTag, Not me.isDebug)
            'print programm output here
            DebugInfo scanRes.getText()
            'fillout array by filter
            If Len(scanRes.getRes())>0 Then
                Stop
                ReDim Preserve res(cnt) As String
                res(cnt)=StrRight(scanRes.getRes(),stripTag)
                cnt=cnt+1
            End If
            'break when no lines
            If Not scanRes.isHasNext() Then Exit Do
            'Stop
        Loop
        Stop
        'print out error stream (stderr) if exists
        Dim bErrHeader As Boolean
        bErrHeader=True
        Do
            Set scanRes=rtuObj.intercept(errScan, {.+}, Not me.isDebug)
            Dim txt As String
            txt=scanRes.getText()
            If (Len(txt)>0) And bErrHeader Then
                DebugInfo {*** Errors ***}
                bErrHeader=False
            End If
            DebugInfo txt
            If Not scanRes.isHasNext() Then Exit Do
            Stop
        Loop
     
        testProc=(res)
    End Function
типа УРА
бонусом мы получили замену неработающей регулярно Java Debug Console (при запущенном дизигнере)

по регэкспам - пригодится "отрицание" ;)
хавалук
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
470
продолжу свой опус....
выше - это тупо зауск проги, но без детализации, что ещё возможно:
- передача инфы в потоке - типа пишем в выходной поток проги, она плюёт во входной для кода (в коде выше InputStream). Тонкость - надо коммутировать эти потоки через очередь, примитивный случай (без коммутации) может дать проблемы (типа записали - надо сразу считывать)
- пакетная обработка - проге скармливаем файл с набором строк, каждая строка - набор аргументов

щас опишу рапер для http...
для чего он нужен - блаблабла старая домина/жвм/асинхронная обработка запросов, библиотеки для жвм новее домирношной
кусок основного кода джава (внешней)
Java:
    private static String getStringFromMethod(HttpEntityEnclosingRequestBase method, String bodyJSON, String authToken) throws IOException {
        method.addHeader("content-type", "application/json;charset=UTF-8");
        if (authToken != null) method.addHeader("X-Auth-Token", authToken);

        StringEntity body = new StringEntity(bodyJSON);
        method.setEntity(body);

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            try (CloseableHttpResponse response = httpClient.execute(method)) {
                int status = response.getStatusLine().getStatusCode();
                if (status != STATUS_OK) {
                    LOG.error("\n->error:" + status + ";" + response.getStatusLine().getReasonPhrase());
                } else {
                    return resultToFile(EntityUtils.toString(response.getEntity()));
                }
            }
        }
        return null;
    }
а точку входа оформим так
Java:
    public static void main(String[] args) throws IOException {
        String token = null;
        String data = null;
        String method = null;
        String uri = null;
        for (String s : args) {
            if (s.toUpperCase().startsWith(String.valueOf(ARGS.FILE))) {
                data = new BufferedReader(
                        new InputStreamReader(new FileInputStream(s.split("=")[1]), StandardCharsets.UTF_8))
                        .lines()
                        .collect(Collectors.joining("\n"));
            } else if (s.toUpperCase().startsWith(String.valueOf(ARGS.DATA))) {
                data = s.split("=")[1];
            } else if (s.toUpperCase().startsWith(String.valueOf(ARGS.METHOD))) {
                method = s.split("=")[1];
            } else if (s.toUpperCase().startsWith(String.valueOf(ARGS.URI))) {
                uri = s.split("=")[1];
            } else if (s.toUpperCase().startsWith(String.valueOf(ARGS.TOKEN))) {
                String[] arr = s.split("=");
                if(arr.length>1)token=arr[1];
            }
        }
        if (uri != null && method != null) {
            switch (method) {
                case "deleteRequestWithAuthToken":
                    if (token == null) token = getToken();
                    if (token != null)
                        deleteRequestWithAuthToken(uri, token);
                    break;
                case "getRequest":
                    if (token == null) token = getToken();
                    if (token != null)
                        getRequest(uri, token);
                    break;
                case "postRequest":
                    if (data != null)
                        postRequest(uri, data);
                    break;
                case "postRequestWithAuthToken":
                    if (token == null) token = getToken();
                    if (token != null && data != null)
                        postRequestWithAuthToken(uri, token, data);
                    break;
                case "PutRequestWithAuthToken":
                    if (token == null) token = getToken();
                    if (token != null && data != null)
                        putRequestWithAuthToken(uri, token, data);
                    break;
                case "getFileRequest":
                    if (token == null) token = getToken();
                    if (token != null && data != null)
                        getFileRequest(uri, token, data);
            }
        }
    }
как видно - идет вызов методов класса
наворачивать рефлекшн и контроль параметров и имен я тупо не стал - считаю избыточным
теперь к джава в домине (кусок)
Java:
    private enum ARGS {FILE, METHOD, URI, DATA, TOKEN, JSON};
    private enum METHODS {deleteRequestWithAuthToken,getRequest,getFileRequest,postRequest,postRequestWithAuthToken,putRequestWithAuthToken};
    public String fileTag="->file";
    public String tokenTag="->token";
    public String txtTag="->text:";
    public String fileRegexp="^-\\>text:.+";
    public String tokenRegexp="^-\\>text:.+";
    public String txtRegexp="^-\\>text:.+";

//.........
       private String json2File(String bodyJSON) throws IOException{
        String res=null;
        Path tmp = Files.createTempFile("res", ".txt");
        PrintWriter pw = new PrintWriter(tmp.toFile());
        pw.println(bodyJSON);
        pw.close();
        //tmp.toFile().deleteOnExit();
        res=tmp.toFile().getAbsolutePath();
        return res;
    }
 
    private String intercept(String args, String regexp){
        Process proc=RuntimeUtils.getRuntime(userJDK,classPath, jar+ args,"");
        Scanner scan=RuntimeUtils.getScanner(proc.getInputStream());
        String ret=null;
        for (ScanResult res;(res=RuntimeUtils.intercept(scan,regexp,true)).isHasNext();){
            String s=res.getRes();
//last by regex
            if (s.length()>0)ret=s;
        }
        RuntimeUtils.printProcessInput(proc.getErrorStream(), false);
        return ret;
    }

    public String getBody(String txt, String regex){
        String res=null;
        Pattern p= Pattern.compile(regex,Pattern.CASE_INSENSITIVE );
        Matcher matcher = p.matcher(txt);
        if (matcher.find()){
            res=txt.substring(txt.lastIndexOf(txtTag)+txtTag.length());
        }
        return res;
    }
//......
    public String PostRequestWithAuthToken(String url, String bodyJSON, String authToken) throws IOException{
        //HttpPost post = new HttpPost("https://api.infologistics.ru/api4/v4/authenticate");
        String args=String.valueOf(ARGS.URI)+"="+url+" "
        +ARGS.TOKEN+"="+authToken + " "
        +ARGS.METHOD+"="+METHODS.postRequestWithAuthToken +" "
        +ARGS.FILE+"="+json2File(bodyJSON);

        String ret=intercept(args,this.txtRegexp);
        if(ret!=null)ret=ret.substring(ret.lastIndexOf(txtTag)+txtTag.length());
 
        return ret==null?"error":ret;
    }
из кусков видно - можно передавать как файл с контентом, так и строку, разумеется для "сложных" данных файл лучше (чтобы при запуске в коммандной строке не произошёл УПС)
момент - регэкспы для разных случаев разные, во внешней "программе" логгер выдает дублируя - текст ответа и в файл
1637229264403.png

их я фильтрую и ищу
ещё логгер надо настраивать на локаль (чтобы было предсказуемо), например для slf4j property будет так
1637229398531.png

из LS2J может отправить так
Visual Basic:
Class TestToken As ErrorHandlerWJ
    rtuCl As JavaClass
    rtuObj As JavaObject
    SystemObj As Javaobject
    fssep As String
    userJDK As String
    jar As String
    ClassPath As String
    '" -XX:+PrintInlining -Xms512m -Xmx1G -Xquickstart -Xverify:none ";//-Xaggressive -Xnoagent -Xzero:j9zip ";
    jvmparams As String
    isDebug As Boolean
    stripTag As String
    regexTag As String
 
    Sub New(xUserJDK As String, xClassPath As String, xJar As String)
        me.regexTag=regexTag
        me.stripTag=stripTag
        me.IsDebug=True
        me.userJDK=xUserJDK
        me.ClassPath=xClassPath
        me.jar=xJar
        Set rtuCl=jSession.Getclass(WRAPPERCLASS)
        Set rtuObj=rtuCl.Createobject
        Dim JavaSystem As JavaClass
        Set JavaSystem=me.jSession.Getclass(SYSTEM_CLASS)
        Set SystemObj=JavaSystem.Createobject()
        me.fssep=me.SystemObj.getProperty({file.separator})
    End Sub
 
    %REM
        Function testAuth
        Description: Comments for Function
    %END REM
    Function testAuth
        On Error GoTo ErrH
        GoTo Begin
ErrH:
        Error Err, RaiseError
Begin:
        Dim s As String
        s=rtuObj.PostRequest(uri, requestBody)
        MsgBox s
    End Function
End Class
отмечу - вызов из нотусни даст преобразование в 1251 (ибо шинда) и текст на русском будет кракозябрами (если логгер в UTF-8)
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
470
тут можно отмесить ещё +
возможность "деплооймента" в простом варианте - тупо выгружать на нотусёвые темпы (без контроля на md5 и прочих усложненй)
Visual Basic:
Function Deploy(extSource As ExtSource)
    On Error GoTo ErrH
    GoTo Begin
ErrH:
    Error Err, RaiseError
Begin:
    Dim ses As New NotesSession, doc As NotesDocument
    Set doc=ses.Currentdatabase.Getview(OPTIONS_VIEW).Getdocumentbykey(OPT_DIRS, True)
    Dim path As String
    path=doc.Getitemvalue(OPT_FLD)(0)
    If Instr(path,{ })>0 Then path=StrLeft(doc.Getitemvalue(OPT_FLD)(0),{ })
    path=doc.Getitemvalue(PATH_FLD)(0) &path
   
    Dim rtu As New RuntimeUtils(doc.Getitemvalue(APP_FLD)(0), doc.Getitemvalue(PATH_FLD)(0),_
    doc.Getitemvalue(OPT_FLD)(0) _
    &{ -j } &extSource.sJson &{ -t } &extSource.sTemplate)
   
    On Error 76 Resume Next
    If Dir$(path , ATTR_ARCHIVE) = "" Then
        Dim destDir As String
        Dim    eo As NotesEmbeddedObject, rt As NotesRichTextItem
        Set rt=doc.Getfirstitem({body})
        destDir=GetNotesTempDirectory()
        ForAll e In rt.Embeddedobjects
            Set eo=e
            path=destDir &{\} &eo.Name
            eo.Extractfile(path)
            Exit ForAll
        End ForAll
        path = rtu.unzip(path, destdir)
        Print {ZIP has been deloyed to path: } path
        Delete rtu
        Set rtu = New RuntimeUtils(doc.Getitemvalue(APP_FLD)(0), path &{\},_
        doc.Getitemvalue(OPT_FLD)(0) _
        &{ -j } &extSource.sJson &{ -t } &extSource.sTemplate)
    Else
        Print "Found: " path
    End If
    Set Deploy=rtu
End Function
это со считыванием кофига из БД.
В джава классе:
Java:
    public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
        File destFile = new File(destinationDir, zipEntry.getName());

        String destDirPath = destinationDir.getCanonicalPath();
        String destFilePath = destFile.getCanonicalPath();

        if (!destFilePath.startsWith(destDirPath + File.separator)) {
            throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
        }

        return destFile;
    }
   
    @SuppressWarnings("resource")
    public static String unzip(String fileZip, String sDestDir) throws IOException {
        /*String fileZip = "src/main/resources/unzipTest/compressed.zip";
        File destDir = new File("src/main/resources/unzipTest");
*/
        String res="";
        byte[] buffer = new byte[1024];
        ZipInputStream zis;
        zis = new ZipInputStream(
                new FileInputStream(fileZip));
        ZipEntry zipEntry =
                zis.getNextEntry();
        File destDir=new File(sDestDir);
        boolean bLatch=false;
        while (zipEntry != null) {
            File newFile = newFile(destDir, zipEntry);
            if (zipEntry.isDirectory()) {
                if (!newFile.isDirectory() && !newFile.mkdirs()) {
                    throw new IOException("Failed to create directory " + newFile);
                }
            } else {
                // fix for Windows-created archives
                File parent = newFile.getParentFile();
                if (!parent.isDirectory() && !parent.mkdirs()) {
                    throw new IOException("Failed to create directory " + parent);
                }

                // write file content
                FileOutputStream fos = new FileOutputStream(newFile);
                int len;
                while ((len = zis.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                fos.close();
            }
            //*will return first entry
            if (res.length()==0 && !bLatch){
                res=newFile.getParent().toString();
                System.out.println("first entry" + newFile.getAbsolutePath());
           }

            bLatch=true;
            zipEntry = zis.getNextEntry();
        }
        zis.closeEntry();
        zis.close();
/*        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
*/
        return res;
    }
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!