• 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

62.04
/extensions/webdav/src/main/java/org/exist/webdav/ExistResourceFactory.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.webdav;
50

51

52
import com.bradmcevoy.http.Resource;
53
import com.bradmcevoy.http.ResourceFactory;
54
import org.apache.logging.log4j.LogManager;
55
import org.apache.logging.log4j.Logger;
56
import org.exist.EXistException;
57
import org.exist.collections.Collection;
58
import org.exist.dom.persistent.LockedDocument;
59
import org.exist.storage.BrokerPool;
60
import org.exist.storage.DBBroker;
61
import org.exist.storage.lock.Lock.LockMode;
62
import org.exist.util.FileUtils;
63
import org.exist.xmldb.XmldbURI;
64

65
import java.io.InputStream;
66
import java.net.URISyntaxException;
67
import java.nio.file.Files;
68
import java.nio.file.Path;
69
import java.util.Optional;
70
import java.util.Properties;
71

72
/**
73
 * Class for constructing Milton WebDAV framework resource objects  .
74
 *
75
 * @author Dannes Wessels (dizzzz_at_exist-db.org)
76
 */
77
public class ExistResourceFactory implements ResourceFactory {
78

79
    private final static Logger LOG = LogManager.getLogger(ExistResourceFactory.class);
1✔
80
    private BrokerPool brokerPool = null;
1✔
81

82
    /**
83
     * XML serialization options
84
     */
85
    private final Properties webDavOptions = new Properties();
1✔
86

87
    /**
88
     * Default constructor. Get access to instance of exist-db broker pool.
89
     */
90
    public ExistResourceFactory() {
1✔
91

92
        try {
93
            brokerPool = BrokerPool.getInstance();
1✔
94

95
        } catch (EXistException e) {
×
96
            LOG.error("Unable to initialize WebDAV interface.", e);
×
97
        }
1✔
98

99
        // load specific options
100
        try {
101
            // 1) try and read default config from classpath
102
            try (final InputStream is = getClass().getResourceAsStream("webdav.properties")) {
1✔
103
                if (is != null) {
1!
104
                    LOG.info("Read default WebDAV configuration from classpath");
1✔
105
                    webDavOptions.load(is);
1✔
106
                } else {
107
                    LOG.warn("Unable to read default WebDAV configuration from the classpath.");
×
108
                }
109
            }
110
        } catch (final Throwable ex) {
×
111
            LOG.error(ex.getMessage());
×
112
        }
1✔
113

114
        try {
115
            // 2) try and find overridden config relative to EXIST_HOME/etc
116
            final Optional<Path> eXistHome = brokerPool.getConfiguration().getExistHome();
1✔
117
            final Path config = FileUtils.resolve(eXistHome, "etc").resolve("webdav.properties");
1✔
118

119
            // Read from file if existent
120
            if (Files.isReadable(config)) {
1!
121
                LOG.info("Read WebDAV configuration from {}", config.toAbsolutePath().toString());
×
122
                try (final InputStream is = Files.newInputStream(config)) {
×
123
                    webDavOptions.load(is);
×
124
                }
125
            }
126
        } catch (final Throwable ex) {
×
127
            LOG.error(ex.getMessage());
×
128
        }
1✔
129

130
    }
1✔
131

132
    /*
133
     * Construct Resource for path. A Document or Collection resource is returned, NULL if type
134
     * could not be detected.
135
     */
136
    @Override
137
    public Resource getResource(String host, String path) {
138

139
        // DWES: work around if no /db is available return nothing.
140
        if (!path.contains("/db")) {
1!
141
            LOG.error("path should at least contain /db");
×
142
            return null;
×
143
        }
144

145
        // Construct path as XmldbURI
146
        XmldbURI xmldbUri = null;
1✔
147
        try {
148
            // Strip preceding path, all up to /db
149
            path = path.substring(path.indexOf("/db"));
1✔
150

151
            // Strip last slash if available
152
            if (path.endsWith("/")) {
1✔
153
                path = path.substring(0, path.lastIndexOf("/"));
1✔
154
            }
155

156
            if (LOG.isDebugEnabled()) {
1!
157
                LOG.debug("host='{}' path='{}'", host, path);
×
158
            }
159

160
            // Create uri inside database
161
            xmldbUri = XmldbURI.xmldbUriFor(path);
1✔
162

163
        } catch (URISyntaxException e) {
×
164
            LOG.error("Unable to convert path '{}}' into an XmldbURI representation.", path);
×
165
            return null;
×
166
        }
1✔
167

168
        // Return appropriate resource
169
        return switch (getResourceType(brokerPool, xmldbUri)) {
1!
170
            case DOCUMENT -> {
171
                MiltonDocument doc = new MiltonDocument(webDavOptions, host, xmldbUri, brokerPool);
1✔
172
                yield doc;
1✔
173
            }
174
            case COLLECTION -> new MiltonCollection(webDavOptions, host, xmldbUri, brokerPool);
1✔
175
            case IGNORABLE -> {
176
                if (LOG.isDebugEnabled()) {
×
177
                    LOG.debug("ignoring file");
×
178
                }
179
                yield null;
×
180
            }
181
            case NOT_EXISTING -> {
182
                if (LOG.isDebugEnabled()) {
1!
183
                    LOG.debug("Resource does not exist: '{}'", xmldbUri);
×
184
                }
185
                yield null;
1✔
186
            }
187
            default -> {
188
                LOG.error("Unkown resource type for {}", xmldbUri);
×
189
                yield null;
×
190
            }
191
        };
192
    }
193

194
    /*
195
     * Returns the resource type indicated by the path: either COLLECTION, DOCUMENT or NOT_EXISTING.
196
     */
197
    private ResourceType getResourceType(BrokerPool brokerPool, XmldbURI xmldbUri) {
198

199
        ResourceType type = ResourceType.NOT_EXISTING;
1✔
200

201
        // MacOsX finder specific files
202
        String documentSeqment = xmldbUri.lastSegment().toString();
1✔
203
        if (documentSeqment.startsWith("._") || ".DS_Store".equals(documentSeqment)) {
1!
204
            //LOG.debug(String.format("Ignoring MacOSX file '%s'", xmldbUri.lastSegment().toString()));
205
            //return ResourceType.IGNORABLE;
206
        }
207

208
        // Documents that start with a dot
209
        if (documentSeqment.startsWith(".")) {
1!
210
            //LOG.debug(String.format("Ignoring '.' file '%s'", xmldbUri.lastSegment().toString()));
211
            //return ResourceType.IGNORABLE;
212
        }
213

214
        // Try to read as system user. Note that the actual user is not know
215
        // yet. In MiltonResource the actual authentication and authorization
216
        // is performed.
217
        try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
1✔
218
                final Collection collection = broker.openCollection(xmldbUri, LockMode.READ_LOCK)) {
1✔
219
            if (LOG.isDebugEnabled()) {
1!
220
                LOG.debug("Path: {}", xmldbUri);
×
221
            }
222

223
            // First check if resource is a collection
224
            if (collection != null) {
1✔
225
                type = ResourceType.COLLECTION;
1✔
226
            } else {
227
                // If it is not a collection, check if it is a document
228
                try (final LockedDocument lockedDoc = broker.getXMLResource(xmldbUri, LockMode.READ_LOCK)) {
1✔
229
                    if (lockedDoc != null) {
1✔
230
                        // Document is found
231
                        type = ResourceType.DOCUMENT;
1✔
232
                    } else {
233
                        // No document and no collection.
234
                        type = ResourceType.NOT_EXISTING;
1✔
235
                    }
236
                }
237
            }
238
        } catch (final Exception ex) {
×
239
            LOG.error("Error determining nature of resource {}", xmldbUri.toString(), ex);
×
240
            type = ResourceType.NOT_EXISTING;
×
241

242
        }
1✔
243

244
        if (LOG.isDebugEnabled()) {
1!
245
            LOG.debug("Resource type={}", type.toString());
×
246
        }
247

248
        return type;
1✔
249
    }
250

251
    private enum ResourceType {
1✔
252
        DOCUMENT, COLLECTION, IGNORABLE, NOT_EXISTING
1✔
253
    }
254

255
}
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