从Java以编程方式导入XML:导入项目但返回的列表为空

时间:2016-02-12 10:23:34

标签: java xml intersystems-cache

这是一个示例Java代码,它尝试使用%SYSTEM.OBJ's LoadStream method导入XML导出中可用的类;为简洁起见省略了import指令:

public final class Main2
{
    private static final String CACHEDB_HOST = "cachedb.host";
    private static final String CACHEDB_PORT = "cachedb.port";
    private static final String CACHEDB_USER = "cachedb.user";
    private static final String CACHEDB_PASSWORD = "cachedb.password";
    private static final String CACHEDB_NAMESPACE = "cachedb.namespace";
    private static final String LOADEDFILE = "loadedFile";

    private static final String CACHEDB_HOST_DEFAULT = "localhost";
    private static final String CACHEDB_PORT_DEFAULT = "1972";

    private static final String JDBC_URL_TEMPLATE = "jdbc:Cache://%s:%s/%s";

    private Main2()
    {
        throw new Error("instantiation not permitted");
    }

    public static void main(final String... args)
        throws IOException, CacheException
    {
        if (args.length == 0)
            throw new IllegalArgumentException("missing arguments");

        final Properties properties = new Properties();

        final Path path = Paths.get(args[0]).toRealPath();

        try (
            final Reader reader = Files.newBufferedReader(path);
        ) {
            properties.load(reader);
        }

        final String jdbcUrl = String.format(JDBC_URL_TEMPLATE,
            readProperty(properties, CACHEDB_HOST, CACHEDB_HOST_DEFAULT),
            readProperty(properties, CACHEDB_PORT, CACHEDB_PORT_DEFAULT),
            readProperty(properties, CACHEDB_NAMESPACE));

        final String user = readProperty(properties, CACHEDB_USER);
        final String password = readProperty(properties, CACHEDB_PASSWORD);

        final Path loadedFile = Paths.get(readProperty(properties, LOADEDFILE))
            .toRealPath();

        try (
            final CacheDb db = new CacheDb(jdbcUrl, user, password);
        ) {
            final GlobalCharacterStream stream
                = new GlobalCharacterStream(db.getDatabase());

            loadContent(stream, loadedFile);

            /*
             * Arguments for class "%SYSTEM.OBJ", class method "LoadStream"
             */
            final Dataholder[] arguments = new Dataholder[8];

            /*
             * Arguments ByRef
             *
             * Indices start at 1, not 0
             */
            final int[] byRefArgs = new int[2];

            // Arg 3: error log
            final StringHolder errorlog = new StringHolder("");
            byRefArgs[0] = 3;

            // Arg 4: list of loaded items
            final StringHolder loadedlist = new StringHolder("");
            byRefArgs[1] = 4;

            /*
             * Fill arguments
             */
            // arg 1: stream
            arguments[0] = Dataholder.create(stream);
            // arg 2: qspec; the default, therefore null
            arguments[1] = new Dataholder((String) null);
            // arg 3: errorlog
            arguments[2] = Dataholder.create(errorlog.value);
            // arg 4: loadedlist
            arguments[3] = Dataholder.create(loadedlist.value);
            // arg 5: listonly; we want true
            arguments[4] = Dataholder.create(Boolean.TRUE);
            // arg 6: selecteditems; nothing
            arguments[5] = Dataholder.create(null);
            // arg 7: displayname. For logging...
            arguments[6] = Dataholder.create("IMPORT");
            // arg 8: charset. Default is empty string, we'll assume UTF-8.
            arguments[7] = new Dataholder((String) null);

            // Now, make the call
            final Dataholder[] result = db.getDatabase().runClassMethod(
                "%SYSTEM.OBJ",
                "LoadStream",
                byRefArgs,
                arguments,
                Database.RET_PRIM
            );

            /*
             * The result normally has three members:
             *
             * - first is the status; and we need to do that:
             */
            db.getDatabase().parseStatus(result[0]);

            /*
             * - others are ByRef arguments
             */
            // FIXME: probably not ideal
            errorlog.set(result[1].getString());
            System.out.println("errorlog: " + errorlog.getValue());

            loadedlist.set(result[2].getString());
            System.out.println("loadedlist: " + loadedlist.getValue());
        }
    }

    private static void loadContent(final GlobalCharacterStream stream,
        final Path path)
        throws IOException, CacheException
    {
        final StringBuilder sb = new StringBuilder();

        try (
            final Reader reader = Files.newBufferedReader(path);
        ) {
            final char[] buf = new char[2048];
            int nrChars;

            while ((nrChars = reader.read(buf)) != -1)
                sb.append(buf, 0, nrChars);
        }

        stream._write(sb.toString());
    }

    private static String readProperty(final Properties properties,
        final String key)
    {
        final String ret = properties.getProperty(key);
        if (ret == null)
            throw new IllegalArgumentException("required property " + key
                + " is missing");
        return ret;
    }

    private static String readProperty(final Properties properties,
        final String key, final String defaultValue)
    {
        return properties.getProperty(key, defaultValue);
    }
}

现在,代码运行;在Studio中,我看到这些项目也已导入。

然而输出是这个(caché安装是法语,对不起):

Inventaire démarré le 02/12/2016 11:16:38
Classement du fichier IMPORT en tant que xml
Inventaire terminé.

errorlog: 
loadedlist: null

我无法看到哪些项目已导入。

我做错了什么?

1 个答案:

答案 0 :(得分:1)

Java bindings supports getting ByRef values, and you do it correct. But unfortunately there is one limitations, and you catch it here. With ByRef in Caché we can pass arrays, such like below

array("name1")="value1"
array("name2")="value2"

But in Java we can't get such value, only if array was have value in a "root". Load method in a meanwhile, has code which transform array to list of values, and such value we already can get. So, as a workaround I can recommend to replace %GlobalCharacterStream to %FileCharacterStream, with some temporary filename with extension xml. And then we can use this filename in a Load method. So, after few changes, code should looks like:

    final FileCharacterStream stream = new FileCharacterStream(db);

    Dataholder[] args = new Dataholder[]{new Dataholder("xml")};
    Dataholder res = ((SysDatabase) db).runClassMethod("%File", "TempFilename", args, 0);
    stream._filenameSet(res.getString());

    loadContent(stream, path);

    final String remoteFileName = stream._filenameGet();


    /*
     * Arguments for class "%SYSTEM.OBJ", class method "Load"
     */
    final Dataholder[] arguments = new Dataholder[9];

    /*
     * Arguments ByRef
     *
     * Indices start at 1, not 0
     */
    final int[] byRefArgs = new int[3];

    // Arg 3: error log
    final StringHolder errorlog = new StringHolder("");
    byRefArgs[0] = 3;

    // Arg 4: list of loaded items
    final StringHolder loadedlist = new StringHolder("");
    byRefArgs[1] = 4;

    // Arg 9: description (?)
    final StringHolder description = new StringHolder("");
    byRefArgs[2] = 9;

    /*
     * Fill arguments
     */
    // arg 1: file name
    arguments[0] = Dataholder.create(remoteFileName);
    // arg 2: qspec; we want to ensure that compile works, at least
    arguments[1] = new Dataholder("d");
    // arg 3: errorlog
    arguments[2] = Dataholder.create(errorlog.value);
    // arg 4: loadedlist
    arguments[3] = Dataholder.create(loadedlist.value);
    // arg 5: listonly; no
    arguments[4] = Dataholder.create(Boolean.FALSE);
    // arg 6: selecteditems; nothing
    arguments[5] = Dataholder.create(null);
    // arg 7: displayname. For logging...
    arguments[6] = Dataholder.create("IMPORT.xml");
    // arg 8: charset. Default is empty string, we'll assume UTF-8.
    arguments[7] = new Dataholder((String) null);
    // arg 9: description (?)
    arguments[8] = Dataholder.create(description.value);

    // Now, make the call
    final Dataholder[] result = db.runClassMethod(
        "%SYSTEM.OBJ",
        "Load",
        byRefArgs,
        arguments,
        Database.RET_PRIM
    );

    /*
     * The result normally has three members:
     *
     * - first is the status; and we need to do that:
     */
    db.parseStatus(result[0]);

    /*
     * - others are ByRef arguments
     */
    errorlog.set(result[1].getString());
    System.out.println("errorlog: " + errorlog.getValue());

    loadedlist.set(result[2].getString());
    System.out.println("loadedlist: " + loadedlist.getValue());

and as a result

 Load started on 02/12/2016 22:56:06
 Loading file IMPORT.xml as xml
 Imported class: Sample.Address
 Imported class: Sample.Person
 Load finished successfully.

 errorlog: 
 loadedlist: Sample.Address.cls,Sample.Person.cls