• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / elemental / 982

29 Apr 2025 08:34PM UTC coverage: 56.409% (+0.007%) from 56.402%
982

push

circleci

adamretter
[feature] Improve README.md badges

28451 of 55847 branches covered (50.94%)

Branch coverage included in aggregate %.

77468 of 131924 relevant lines covered (58.72%)

0.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.95
/exist-core/src/main/java/org/exist/repo/ClasspathHelper.java
1
/*
2
 * Elemental
3
 * Copyright (C) 2024, Evolved Binary Ltd
4
 *
5
 * admin@evolvedbinary.com
6
 * https://www.evolvedbinary.com | https://www.elemental.xyz
7
 *
8
 * Use of this software is governed by the Business Source License 1.1
9
 * included in the LICENSE file and at www.mariadb.com/bsl11.
10
 *
11
 * Change Date: 2028-04-27
12
 *
13
 * On the date above, in accordance with the Business Source License, use
14
 * of this software will be governed by the Apache License, Version 2.0.
15
 *
16
 * Additional Use Grant: Production use of the Licensed Work for a permitted
17
 * purpose. A Permitted Purpose is any purpose other than a Competing Use.
18
 * A Competing Use means making the Software available to others in a commercial
19
 * product or service that: substitutes for the Software; substitutes for any
20
 * other product or service we offer using the Software that exists as of the
21
 * date we make the Software available; or offers the same or substantially
22
 * similar functionality as the Software.
23
 *
24
 * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
25
 *       The original license header is included below.
26
 *
27
 * =====================================================================
28
 *
29
 * eXist-db Open Source Native XML Database
30
 * Copyright (C) 2001 The eXist-db Authors
31
 *
32
 * info@exist-db.org
33
 * http://www.exist-db.org
34
 *
35
 * This library is free software; you can redistribute it and/or
36
 * modify it under the terms of the GNU Lesser General Public
37
 * License as published by the Free Software Foundation; either
38
 * version 2.1 of the License, or (at your option) any later version.
39
 *
40
 * This library is distributed in the hope that it will be useful,
41
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
42
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43
 * Lesser General Public License for more details.
44
 *
45
 * You should have received a copy of the GNU Lesser General Public
46
 * License along with this library; if not, write to the Free Software
47
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
48
 */
49
package org.exist.repo;
50

51
import org.apache.logging.log4j.LogManager;
52
import org.apache.logging.log4j.Logger;
53
import org.exist.SystemProperties;
54
import org.exist.start.Classpath;
55
import org.exist.start.EXistClassLoader;
56
import org.exist.storage.BrokerPool;
57
import org.exist.storage.BrokerPoolService;
58
import org.expath.pkg.repo.FileSystemStorage;
59
import org.expath.pkg.repo.Package;
60
import org.expath.pkg.repo.PackageException;
61
import org.expath.pkg.repo.Packages;
62
import org.expath.pkg.repo.deps.ProcessorDependency;
63

64
import java.io.BufferedReader;
65
import java.io.IOException;
66
import java.net.URI;
67
import java.nio.file.Files;
68
import java.nio.file.Path;
69
import java.nio.file.Paths;
70
import java.util.Collection;
71
import java.util.Optional;
72
import java.util.Set;
73

74
/**
75
 * Helper class to construct classpath for expath modules containing
76
 * jar files. Part of start.jar
77
 */
78
public class ClasspathHelper implements BrokerPoolService {
1✔
79

80
    private final static Logger LOG = LogManager.getLogger(ClasspathHelper.class);
1✔
81

82
    @Override
83
    public void prepare(final BrokerPool brokerPool) {
84
        final ClassLoader loader = brokerPool.getClassLoader();
1✔
85
        if (!(loader instanceof EXistClassLoader)) {
1✔
86
            return;
1✔
87
        }
88
        final Classpath cp = new Classpath();
1✔
89
        scanPackages(brokerPool, cp);
1✔
90
        ((EXistClassLoader)loader).addURLs(cp);
1✔
91
    }
1✔
92

93
    public static void updateClasspath(BrokerPool pool, org.expath.pkg.repo.Package pkg) throws PackageException {
94
        final ClassLoader loader = pool.getClassLoader();
×
95
        if (!(loader instanceof EXistClassLoader))
×
96
            {return;}
×
97
        if (!isCompatible(pkg)) {
×
98
            LOG.warn("Package {} is not compatible with this version of eXist. To avoid conflicts, Java libraries shipping with this package are not loaded.", pkg.getName());
×
99
            return;
×
100
        }
101
        final FileSystemStorage.FileSystemResolver resolver = (FileSystemStorage.FileSystemResolver) pkg.getResolver();
×
102
        final Path packageDir = resolver.resolveResourceAsFile(".");
×
103
        final Classpath cp = new Classpath();
×
104
        try {
105
            scanPackageDir(pkg, cp, packageDir);
×
106
            ((EXistClassLoader)loader).addURLs(cp);
×
107
        } catch (final IOException e) {
×
108
            LOG.warn("An error occurred while updating classpath for package {}", pkg.getName(), e);
×
109
        }
110
    }
×
111

112
    private static void scanPackages(BrokerPool pool, Classpath classpath) {
113
        try {
114
            final Optional<ExistRepository> repo = pool.getExpathRepo();
1✔
115
            if (repo.isPresent()) {
1!
116
            for (final Packages pkgs : repo.get().getParentRepo().listPackages()) {
1!
117
                final Package pkg = pkgs.latest();
×
118
                if (!isCompatible(pkg)) {
×
119
                    LOG.warn("Package {} is not compatible with this version of eXist. To avoid conflicts, Java libraries shipping with this package are not loaded.", pkg.getName());
×
120
                } else {
×
121
                    try {
122
                        final FileSystemStorage.FileSystemResolver resolver = (FileSystemStorage.FileSystemResolver) pkg.getResolver();
×
123
                        final Path packageDir = resolver.resolveResourceAsFile(".");
×
124
                        scanPackageDir(pkg, classpath, packageDir);
×
125
                    } catch (final IOException e) {
×
126
                        LOG.warn("An error occurred while updating classpath for package {}", pkg.getName(), e);
×
127
                    }
128
                }
129
            }
130
            }
131
        } catch (final Exception e) {
1✔
132
            LOG.warn("An error occurred while updating classpath for packages", e);
×
133
        }
134
    }
1✔
135

136
    private static boolean isCompatible(final Package pkg) throws PackageException {
137
        // determine the Elemental version this package is compatible with
138
        final Collection<ProcessorDependency> processorDeps = pkg.getProcessorDeps();
×
139
        final String procVersion = SystemProperties.getInstance().getSystemProperty("product-version", "1.0.0");
×
140
        final String eXistCompatibleProcVersion = SystemProperties.getInstance().getSystemProperty("exist-db-expath-pkg-compatible-version", "6.3.0");
×
141
        PackageLoader.Version requiresProcessorVersion = null;
×
142
        for (final ProcessorDependency dependency: processorDeps) {
×
143
            if (Deployment.PROCESSOR_NAME.equals(dependency.getProcessor()) || Deployment.EXIST_PROCESSOR_NAME.equals(dependency.getProcessor())) {
×
144
                if (dependency.getSemver() != null) {
×
145
                    requiresProcessorVersion = new PackageLoader.Version(dependency.getSemver(), true);
×
146
                } else if (dependency.getSemverMax() != null || dependency.getSemverMin() != null) {
×
147
                    requiresProcessorVersion = new PackageLoader.Version(dependency.getSemverMin(), dependency.getSemverMax());
×
148
                } else if (dependency.getVersions() != null) {
×
149
                    requiresProcessorVersion = new PackageLoader.Version(dependency.getVersions(), false);
×
150
                }
151
                break;
×
152
            }
153
        }
154
        if (requiresProcessorVersion == null) {
×
155

156
            // does the package contain XQuery Module(s) implemented in Java?
157
            final ExistPkgInfo existPkgInfo = (ExistPkgInfo) pkg.getInfo("exist");
×
158
            if (existPkgInfo == null) {
×
159
                // no Java modules
160
                return true;
×
161
            }
162

163
            final Set<URI> javaModules = existPkgInfo.getJavaModules();
×
164
            if (javaModules == null || javaModules.isEmpty()) {
×
165
                // no Java modules
166
                return true;
×
167
            }
168

169
            /*
170
                There are Elemental Java modules in the package,
171
                but the package does not declare which version
172
                of Elemental (the processor) that it depends upon,
173
                therefore we assume it is incompatible.
174

175
                NOTE - In older versions of Elemental, if the package
176
                did not declare a dependency on a specific processor
177
                version, we would check whether the version of
178
                Elemental was between 1.4.0 and 2.2.1
179
                (inclusive). As we are now past Elemental version
180
                5.2.0, that would always return false!
181
             */
182
            return false;
×
183

184
        } else {
185
            return requiresProcessorVersion.getDependencyVersion().isCompatible(procVersion)
×
186
                || requiresProcessorVersion.getDependencyVersion().isCompatible(eXistCompatibleProcVersion);
×
187
        }
188
    }
189

190
    private static void scanPackageDir(Package pkg, Classpath classpath, Path module) throws IOException {
191
        final Path dotExist =  module.resolve(".exist");
×
192
        if (Files.exists(dotExist)) {
×
193
            if (!Files.isDirectory(dotExist)) {
×
194
                throw new IOException("The .exist config dir is not a dir: " + dotExist);
×
195
            }
196

197
            final Path cp = dotExist.resolve("classpath.txt");
×
198
            if (Files.exists(cp)) {
×
199
                try (final BufferedReader reader = Files.newBufferedReader(cp)) {
×
200
                    String line;
201
                    while ((line = reader.readLine()) != null) {
×
202
                        Path p = Paths.get(line);
×
203
                        if (!p.isAbsolute()) {
×
204
                            final FileSystemStorage.FileSystemResolver res = (FileSystemStorage.FileSystemResolver) pkg.getResolver();
×
205
                            p = res.resolveComponentAsFile(line);
×
206
                        }
207
                        p = p.normalize().toAbsolutePath();
×
208

209
                        if (Files.exists(p)) {
×
210
                            classpath.addComponent(p.toString());
×
211
                        } else {
×
212
                            LOG.warn("Unable to add '" + p + "' to the classpath for EXPath package: " + pkg.getName() + ", as the file does not exist!");
×
213
                        }
214
                    }
215
                }
216
            }
217
        }
218
    }
×
219
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc