Унификация разработки

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 586
272
#1
Речь пойдет больше про java и xpages
Что хочется:
- писать код и тестировать его во внешних ИДЕ (дизайнер сильно убогий по разработке java/js...)
- при переносе в дизайнер минимально изменять код
- кроссплатформа

про Team Development знают (полагаю) все, там есть ODP (On Disk Project) т.е. создавая приложение можно ему назначить его отображение на папку, а уже её синхронить и с nsf и с git (как вариант)
Засада состоит в том, что классы домины привязаны к нативным либам, что создает массу нецдобств и отсутствие переносимости (если рарабаывать одновременно в дизайнере и ИДЕ, на др. платформе)

Вариант с IIOP ну очень горбатый. Предлагаю следующую модель:
- пишем REST для доступа в сущностям, возможно использовать
Для просмотра контента необходимо: Войти или зарегистрироваться

- делаем финт с авторизацией (уже описывал) - создавая LtpaToken, через страницу в публичным доступом и шифрованием
- обращение к сущностям получаем в виде JSON
- к внешней ИДЕ надо будет прикрутить
Для просмотра контента необходимо: Войти или зарегистрироваться
(в домине оно и так есть)
немного кода xAgent_token.xsp:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" rendered="false">
    <xp:this.beforePageLoad><![CDATA[#{javascript:try {
var url = context.getUrl();
var host = url.getHost();
var domain=host.match(/\..*/)[0];
var response=facesContext.getExternalContext().getResponse();

//var token=paramValues.get("token").toString();
//var rUrl=paramValues.get("url").toString();
var user_enc = context.getUrlParameter('id');
print("::xAgent_token::id:" + user_enc);
var user=decrypt(base64_decode(user_enc));
print("::xAgent_token::User:" + user);
/* var httpURL = database.getHttpURL();
var dbUrl= httpURL.replace("http://", "https://").match(/(.+)\?/)[1];
print("url:"+dbUrl);
*/
  var generated=LtpaGenerator(user);

//var header="LtpaToken=" + token + "; domain=" + domain + "; path=/";

//response.setHeader("Set-Cookie", header);
//facesContext.getExternalContext().redirect(rUrl);
  var externalContext:javax.faces.context.ExternalContext = facesContext.getExternalContext();
  var response:javax.servlet.http.HttpServletResponse = externalContext.getResponse();
  response.setHeader("Cache-Control", "no-cache");
  response.setDateHeader("Expires", -1);
  var externalContext:javax.faces.context.ExternalContext = facesContext.getExternalContext();
  var response:javax.servlet.http.HttpServletResponse = externalContext.getResponse();
  response.setHeader("Cache-Control", "no-cache");
  response.setDateHeader("Expires", -1);
  var gVars=getVars();
  print("::xAgent_token::token:"+generated);
  response.setHeader("Set-Cookie", "LtpaToken=" + generated + "; domain="+ gVars.domain + "path=/");
  //response.setContentType("text/html;charset=UTF-8");
  var writer:java.io.PrintWriter = response.getWriter();
  writer.write(generated);

} catch(e) {
        _dump(e);
}
}]]></xp:this.beforePageLoad>

    <xp:this.resources>
        <xp:script src="/SSJSUtils.jss" clientSide="false"></xp:script>
    </xp:this.resources>
</xp:view>
либы уже выкладывал, если нужно что-то довыложить - спрашивайте
суть проста - генерим токен при вызове страницы, ответом прописываем куки на запрашивающую сторону
следом можем уже вызывать REST или DAS
Java:
import com.ibm.commons.util.io.json.JsonException;
import com.ibm.commons.util.io.json.JsonJavaFactory;
import com.ibm.commons.util.io.json.JsonParser;
import org.apache.commons.codec.binary.Base64;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;

public class DAS {
    //... здесь поля, кот. зависят от приложения target - это адрес "...xAgent_token.xsp?id=" + encBase64 (см. authorize)
    private static final JWT jwt = new JWT();
    private static final String encrypted = jwt.encrypt(userName,key);
    private static final String encBase64=new String(Base64.encodeBase64(encrypted.getBytes()));
    static{TrustAllCertificates.install();}

    private static List<String> cookies;

    public static InputStream request2URL(String target) throws IOException {

        URL url=new URL(target);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        if (cookies!=null)
            for (String cookie : cookies) {
                conn.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
            }

        InputStream res = null;
        if (conn.getResponseCode() == 200) {
            System.out.println("OK");
            res= conn.getInputStream();
            if (cookies==null)cookies = conn.getHeaderFields().get("Set-Cookie");
            if (cookies!=null) {
                for (String el : cookies) {
                    System.out.println(el);
                }
            }

        }else{
            System.out.format("Request status %d -> %s\n",conn.getResponseCode(),conn.getResponseMessage());
        }
        return res;
    }
    public static String request2URLString(String target) throws IOException {
        String res="";
        URL url=new URL(target);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        if (cookies!=null)
            for (String cookie : cookies) {
                conn.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
            }

        if (conn.getResponseCode() == 200) {
            System.out.println("OK");
            res= result2String(conn);
            if (cookies==null)cookies = conn.getHeaderFields().get("Set-Cookie");
            if (cookies!=null) {
                for (String el : cookies) {
                    System.out.println(el);
                }
            }

        }else{
            System.out.format("Request status %d -> %s\n",conn.getResponseCode(),conn.getResponseMessage());
        }
        return res;
    }

    private static String result2String(HttpURLConnection conn) throws IOException {
        StringBuilder res = new StringBuilder();
        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String line = "";
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
            res.append(line);
        }
        reader.close();
        return res.toString();
    }

    public static void authorize() throws IOException {
        cookies=null;
        URL url=new URL(target+encBase64);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        if (conn.getResponseCode() == 200) {
            result2String(conn);
            cookies = conn.getHeaderFields().get("Set-Cookie");
            for (String el : cookies) {
                System.out.println(el);
            }
        }
    }
    //https://stackoverflow.com/questions/40917681/in-xpages-how-do-you-get-the-post-body-from-a-rest-call
    //jar: https://jar-download.com/?detail_search=g%3A%22com.ibm.sbt%22+AND+a%3A%22com.ibm.commons%22+AND+v%3A%229.0.0%22&a=com.ibm.commons
    public static void main(String[] args) throws IOException, JsonException {
        authorize();
        //String res=request2URLString(serverURL+dbPart+"/PDFTemplates"+viewJsonPart);
        String reqnames=request2URLString(serverURL+dbPart+"/restCustom.xsp/customJSONcat");
        List<Map<String, Object>> names = (List<Map<String, Object>>)JsonParser.fromJson(JsonJavaFactory.instanceEx, reqnames);;
        for(Map<String, Object> nameobj:names) {
            String nameconf = (String) nameobj.get("name");
            String req = request2URLString(serverURL + dbPart + "/restCustom.xsp/customJSONfilter?key="+nameconf);
            System.out.print("*********** "+nameconf+" ");
            //"https://mail1.castrolcis.com/templates/qpassport.nsf/restCustom.xsp/customJSONfilter?key=Neuhof,France"
            //InputStreamReader isR = new InputStreamReader(request2URL(serverURL+dbPart+"/restCustom.xsp/customJSONfilter?key=Neuhof,France"));
            //JsonJavaObject jsonObj = (JsonJavaObject) JsonParser.fromJson(JsonJavaFactory.instanceEx, isR);
            //List<Map<String, Object>> arr
            List<Map<String, Object>> result = (List<Map<String, Object>>) JsonParser.fromJson(JsonJavaFactory.instanceEx, req);
            for (Map<String, Object> map : result) {
                String rowmap = (String) map.get("rowmap");
                String name = (String) map.get("name");
                String region = (String) map.get("region");
                boolean isID = (Boolean) map.get("isID");
                System.out.format("********************\nname:%s\nregion:%s\nrowmap:\n", name, region);
                new PDFDBReadConfig.TestTable(region, isID, rowmap.split(";"));
                for (String r : rowmap.split(";")) {
                    System.out.format("\t%s\n", r);
                }
            }
        }
    }
}
ключ (key)для шифрования используется при отправке имени пользователя, он должен соответствовать таковому на сервере. Json парсер - это как-раз из com.ibm.commons.util.io.json
в main вызывается autorize() и после - запрос к домине (токен передается с куками)
В домине будет использован тот же JSON хоть из java хоть из JS
Т.е. формат и логика разработки будут одинаковы в ИДЕ и дизайнере
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 586
272
#2
еще момент - есть получение данных из вьюшки типа формата JSON
ниже кусок получения значений из вьюшки с отображением мультивэлью как несколько строк
Java:
private static final String viewJsonPart="?ReadViewEntries&OutputFormat=JSON";
....
        String reqnames=request2URLString(serverURL+dbPart+"/"+TMPL_VIEW+viewJsonPart);
        Map<String, Object> viewmap = (Map<String, Object>) JsonParser.fromJson(JsonJavaFactory.instanceEx, reqnames);
        List<JsonObject> entries= (List<JsonObject>) viewmap.get("viewentry");
        //List<Map<String, Object>> names = (List<Map<String, Object>>)JsonParser.fromJson(JsonJavaFactory.instanceEx, reqnames);
        for(JsonObject entry:entries) {
            System.out.println(entry.getJsonProperty("entrydata"));// +"\n"+ entry.getJsonProperty("entrydata").getClass().getName());
            List<JsonObject>list= (List<JsonObject>) entry.getJsonProperty("entrydata");
            System.out.println(list.get(0).getJsonProperty("text"));
            JsonObject jo=(JsonObject)list.get(2).getJsonProperty("textlist");
            if (jo!=null) {
                List<JsonJavaObject> arrJsonJavaObject = (List<JsonJavaObject>) jo.getJsonProperty("text");
                System.out.println(arrJsonJavaObject.get(0).get("0"));
/*
                //only one element
                for (JsonJavaObject jjo : arrJsonJavaObject) {
                    System.out.println(StringEscapeUtils.unescapeJava(jjo.toString()));
                }
*/
 
Последнее редактирование:

savl

Lotus team
28.10.2011
2 136
105
#3
@lmike а автосборку еще не рассматривал? Я вот смотрю в эту сторону, причем для классики...
есть такое:
Для просмотра контента необходимо: Войти или зарегистрироваться

Я даже просто пробовал делать командами через батник, но операция пока полуручная... И надо многое продумать...
Там еще был проект с участием Ant сборщика.
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 586
272
#4
Я вот смотрю в эту сторону, причем для классики...
пока нет и меня смущает необходимость нативных либ домины
по ссылке - это что-то экстримальное ;) - патч на класс
цель моих изысканий - получение рабочего варианта (без нотусевых сущностей), кот. я почти 1-1 перенесу в домину, и для теста нужен только доступный сервер домины
по сути этот подход очень напоминает ODA, но нет необходимости в доп. OSGi плаге на сервере
в тек. состоянии я уже спокойно обращаюсь БД, через http, на домине будет также. Накладные расходы будут нивелированы отсут. recycle для нотусевых объектов и необходимости их перетаскивания в память и "обратно"...
 
Последнее редактирование:

savl

Lotus team
28.10.2011
2 136
105
#6
ну это я как вариант, а вот автосборку можно запустить и простой командой. Но для этого нужен дизайнер где-то, да еще и старт без запроса пароля... а значит винда, которую ты не перевариваешь...
в целом, идея то хорошая, мне самому надо сделать что-то вроде CI/CD для Domino, да еще и с контролем версий.
Но на DAS + Web + xpage я даже не надеюсь, в компаниях с большим уровнем иерархии такие идеи просто застревают :(
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 586
272
#7
Но для этого нужен дизайнер где-то, да еще и старт без запроса пароля... а значит винда, которую ты не перевариваешь...
не то чтобы не перевариваю, она реально пропихивается везде, хотя присутствуют сложности в управлении и настройке, особенно для разработки
пример
Для просмотра контента необходимо: Войти или зарегистрироваться

хватануть это можно на ровном месте и подобное задалбывает... я не осущ. подобных масштабов разработок (даже близко) , но куча тупок и ненужной мне активности, винды, мешает сильно
Инструменты - я легко разверну ч-л, от виртуалки до контейнеров, в винде с этим все плохо (куча кастылей и стороннего софта)
CI/CD для Domino, да еще и с контролем версий.
очень близко и разаботку вести в сторонней ИДЕ (я в Idea делаю), сборку в nsf - придется дизайнер дергать...
Но на DAS + Web + xpage я даже не надеюсь
собрать простую демонстрацию и показать начальству, может подействует ;)
сделать демку из моего поста про интеграцию с ОнлиОфис - наглядное совместное редактирование доков (из домины)...
 

savl

Lotus team
28.10.2011
2 136
105
#9
@lmike да. Только по факту, это дизайнер на сервере просто.
То есть ставишь клиент на сервер и через командную строку на сервере запускаешь.
Нашел пару минусов:
1. Клиент на сервере должен запускаться без пароля, это важно.
2. Результирующий проект nsf/ntf кладется в папку Data клиента, всегда в корень !!!11 никаких вложенных папок.
3. Если файл nsf/ntf уже такой есть, то задача не отрабатывает.
4. Логи гавно, без комментариев.
5. Массовый билд не работает, либо я не смог его нормально запустить. Лучше на один проект - один файл и в цикле делать.
6. Имя шаблона для базы не задать... То есть Replace/Refresh потом делать все равно вручную.

По сути тот проект/плагин для развертывания или его вариант через Ant, реализован через этот же механизм.
Там по большей части эти минусы решены.