001/*
002 * Copyright (c) 2025 Tada AB and other contributors, as listed below.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the The BSD 3-Clause License
006 * which accompanies this distribution, and is available at
007 * http://opensource.org/licenses/BSD-3-Clause
008 *
009 * Contributors:
010 *   Chapman Flack
011 */
012package org.postgresql.pljava.example.annotation;
013
014import java.lang.module.ModuleDescriptor;
015
016import java.sql.ResultSet;
017import java.sql.SQLException;
018
019import java.util.Iterator;
020import java.util.Objects;
021
022import java.util.stream.Stream;
023
024import org.postgresql.pljava.ResultSetProvider;
025import org.postgresql.pljava.annotation.Function;
026import static org.postgresql.pljava.annotation.Function.Effects.STABLE;
027
028/**
029 * Example code to support querying for the modules in Java's boot layer.
030 */
031public class Modules implements ResultSetProvider.Large {
032    /**
033     * Returns information on the named modules in Java's boot module layer.
034     */
035    @Function(
036        effects = STABLE,
037        out = {
038                               "name  pg_catalog.text",
039            "any_unqualified_exports  boolean",
040              "any_unqualified_opens  boolean"
041        }
042    )
043    public static ResultSetProvider java_modules()
044    {
045        return new Modules(
046            ModuleLayer.boot().modules().stream().map(Module::getDescriptor)
047                .filter(Objects::nonNull));
048    }
049
050    private final Iterator<ModuleDescriptor> iterator;
051    private final Runnable closer;
052
053    private Modules(Stream<ModuleDescriptor> s)
054    {
055        iterator = s.iterator();
056        closer = s::close;
057    }
058
059    @Override
060    public boolean assignRowValues(ResultSet receiver, long currentRow)
061    throws SQLException
062    {
063        if ( ! iterator.hasNext() )
064            return false;
065
066        ModuleDescriptor md = iterator.next();
067
068        receiver.updateString(1, md.name());
069
070        receiver.updateBoolean(2,
071            md.exports().stream().anyMatch(e -> ! e.isQualified()));
072
073        receiver.updateBoolean(3,
074            md.isOpen() ||
075            md.opens().stream().anyMatch(o -> ! o.isQualified()));
076
077        return true;
078    }
079
080    @Override
081    public void close()
082    {
083        closer.run();
084    }
085}