Skip to content

Commit 4391b28

Browse files
committed
frontend: add listing all existing labels
Motivation This is the second part of correction of existing an issues #7727; the API call `/labels` was not working Modifiction New message has been added to list exitig labels together with a new handler. Result now it is possible to call API for `/labels` with result listing all existing labels { "labels" : [ "red11", "red18" ] } Target: master Require-book: yes Acked-by: Tigran Mkrtchyan Patch: https://rb.dcache.org/r/14423/
1 parent 5696faa commit 4391b28

File tree

15 files changed

+772
-8
lines changed

15 files changed

+772
-8
lines changed

modules/chimera/src/main/java/org/dcache/chimera/FileSystemProvider.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,19 @@ DirectoryStreamB<ChimeraDirectoryEntry> newDirectoryStream(FsInode dir)
174174
DirectoryStreamB<ChimeraDirectoryEntry> virtualDirectoryStream(FsInode dir, String labelname)
175175
throws ChimeraFsException;
176176

177+
/**
178+
* Returns {@link DirectoryStreamB} of ChimeraDirectoryEntry in the directory.
179+
* <p>
180+
* The returned stream may keep system resources allocated. The try-with-resources construct
181+
* should be used to ensure that the stream's close method is invoked after the stream
182+
* operations are completed.
183+
*
184+
* @param dir inode of "collection" node
185+
* @return stream of directory entries
186+
*/
187+
DirectoryStreamB<ChimeraDirectoryEntry> listLabelsStream(FsInode dir)
188+
throws ChimeraFsException;
189+
177190
void remove(String path) throws ChimeraFsException;
178191

179192
/**

modules/chimera/src/main/java/org/dcache/chimera/FsInode.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,10 @@ public DirectoryStreamB<ChimeraDirectoryEntry> virtualDirectoryStream(String lab
435435
return _fs.virtualDirectoryStream(this, labelname);
436436
}
437437

438+
public DirectoryStreamB<ChimeraDirectoryEntry> listLabelsStream() throws ChimeraFsException {
439+
return _fs.listLabelsStream(this);
440+
}
441+
438442
public String getId() throws ChimeraFsException {
439443
Stat stat = _stat;
440444
return (stat != null) ? stat.getId() : _fs.inode2id(this);

modules/chimera/src/main/java/org/dcache/chimera/JdbcFs.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,14 @@ public DirectoryStreamB<ChimeraDirectoryEntry> virtualDirectoryStream(FsInode di
563563
return _sqlDriver.virtualDirectoryStream(dir, labelname);
564564
}
565565

566+
567+
@Override
568+
public DirectoryStreamB<ChimeraDirectoryEntry> listLabelsStream(FsInode dir) throws ChimeraFsException {
569+
return _sqlDriver.labelsDirectoryStream(dir);
570+
}
571+
572+
573+
566574
@Override
567575
public void remove(String path) throws ChimeraFsException {
568576

modules/dcache-chimera/src/main/java/org/dcache/chimera/namespace/ChimeraNameSpaceProvider.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,13 @@
9898
import org.dcache.chimera.FileSystemProvider;
9999
import org.dcache.chimera.FileSystemProvider.SetXattrMode;
100100
import org.dcache.chimera.FsInode;
101-
import org.dcache.chimera.FsInode_LABEL;
101+
import org.dcache.chimera.NoLabelChimeraException;
102102
import org.dcache.chimera.NoXdataChimeraException;
103103
import org.dcache.chimera.NotDirChimeraException;
104104
import org.dcache.chimera.StorageGenericLocation;
105105
import org.dcache.chimera.StorageLocatable;
106106
import org.dcache.chimera.UnixPermission;
107+
107108
import org.dcache.chimera.posix.Stat;
108109
import org.dcache.commons.stats.MonitoringProxy;
109110
import org.dcache.commons.stats.RequestCounters;
@@ -1391,6 +1392,46 @@ public void listVirtualDirectory(Subject subject, String path, Range<Integer> ra
13911392
}
13921393
}
13931394

1395+
1396+
@Override
1397+
public void listLabels(Subject subject, Range<Integer> range,
1398+
Set<FileAttribute> attrs, ListHandler handler)
1399+
throws CacheException
1400+
{
1401+
try {
1402+
int counter = 0;
1403+
//TODO check permissions
1404+
try (DirectoryStreamB<ChimeraDirectoryEntry> dirStream = FsInode.getRoot(_fs)
1405+
.listLabelsStream()) {
1406+
for (ChimeraDirectoryEntry entry : dirStream) {
1407+
try {
1408+
String name = entry.getName();
1409+
1410+
// FIXME: actually, ChimeraDirectoryEntry
1411+
// already contains most of attributes
1412+
1413+
FileAttributes fa =
1414+
attrs.isEmpty()
1415+
? null
1416+
: getFileAttributes(new ExtendedInode(_fs, entry.getInode()), attrs);
1417+
handler.addEntry(name, fa);
1418+
1419+
} catch (NoLabelChimeraException e) {
1420+
/* Not an error; files may be deleted during the
1421+
* list operation.
1422+
*/
1423+
}
1424+
}
1425+
}
1426+
1427+
} catch (FileNotFoundChimeraFsException e) {
1428+
throw new FileNotFoundCacheException("there are no any labels added to files : ");
1429+
} catch (IOException e) {
1430+
LOGGER.error("Exception in list: {}", e.getMessage());
1431+
throw new CacheException(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, e.getMessage());
1432+
}
1433+
}
1434+
13941435
private ExtendedInode mkdir(Subject subject, ExtendedInode parent, String name, int uid,
13951436
int gid, int mode)
13961437
throws ChimeraFsException, CacheException {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* dCache - http://www.dcache.org/
3+
*
4+
* Copyright (C) 2025 Deutsches Elektronen-Synchrotron
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Affero General Public License as
8+
* published by the Free Software Foundation, either version 3 of the
9+
* License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Affero General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
package org.dcache.restful.providers;
20+
21+
import io.swagger.annotations.ApiModel;
22+
import io.swagger.annotations.ApiModelProperty;
23+
import java.util.HashSet;
24+
import java.util.Set;
25+
26+
@ApiModel(description = "Specifies all existing lables")
27+
public class JsonListLabels {
28+
29+
30+
@ApiModelProperty("All existing labels.")
31+
private Set<String> labels = new HashSet();
32+
33+
public void setLabels(Set<String> labelnames) {
34+
if (labelnames == null) {
35+
return;
36+
}
37+
38+
labels.addAll(labelnames);
39+
}
40+
41+
public Set<String> getLabels() {
42+
return labels;
43+
}
44+
}

modules/dcache-frontend/src/main/java/org/dcache/restful/resources/labels/LabelsResources.java

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import io.swagger.annotations.ApiResponses;
3434
import io.swagger.annotations.Authorization;
3535
import java.util.ArrayList;
36+
import java.util.HashSet;
3637
import java.util.List;
3738
import java.util.Set;
3839
import javax.inject.Inject;
@@ -56,12 +57,13 @@
5657
import org.dcache.namespace.FileAttribute;
5758
import org.dcache.poolmanager.PoolMonitor;
5859
import org.dcache.restful.providers.JsonFileAttributes;
60+
import org.dcache.restful.providers.JsonListLabels;
5961
import org.dcache.restful.util.HttpServletRequests;
6062
import org.dcache.restful.util.RequestUser;
6163
import org.dcache.restful.util.namespace.NamespaceUtils;
6264
import org.dcache.util.list.DirectoryEntry;
6365
import org.dcache.util.list.DirectoryStream;
64-
import org.dcache.util.list.ListDirectoryHandler;
66+
import org.dcache.util.list.LabelsListHandler;
6567
import org.dcache.util.list.VirtualDirectoryListHandler;
6668
import org.slf4j.Logger;
6769
import org.slf4j.LoggerFactory;
@@ -96,14 +98,83 @@ public class LabelsResources {
9698
private VirtualDirectoryListHandler virtualDirectoryListHandler;
9799

98100
@Inject
99-
@Named("pool-manager-stub")
100-
private CellStub poolmanager;
101+
private LabelsListHandler labelsListHandler;
101102

102103
@Inject
103104
@Named("pinManagerStub")
104105
private CellStub pinmanager;
105106

107+
@GET
108+
@ApiOperation(value = "List all existing labels.",
109+
notes = "The method offers the possibility to query of all existing labels.")
110+
@ApiResponses({
111+
@ApiResponse(code = 401, message = "Unauthorized"),
112+
@ApiResponse(code = 403, message = "Forbidden"),
113+
@ApiResponse(code = 404, message = "Not Found"),
114+
@ApiResponse(code = 500, message = "Internal Server Error"),
115+
})
116+
@Produces(MediaType.APPLICATION_JSON)
117+
public JsonListLabels getListLabels(
118+
@ApiParam("Limit number of replies in labels listing.")
119+
@QueryParam("limit") String limit,
120+
@ApiParam("Number of entries to skip in labels listing.")
121+
@QueryParam("offset") String offset) throws CacheException {
122+
123+
JsonListLabels labels = new JsonListLabels();
106124

125+
Set<FileAttribute> attributes =
126+
NamespaceUtils.getRequestedAttributes(false,
127+
false,
128+
false,
129+
false,
130+
false);
131+
132+
FsPath path = pathMapper.asDcachePath(request, "/", ForbiddenException::new);
133+
134+
135+
Range<Integer> range;
136+
try {
137+
int lower = (offset == null) ? 0 : Integer.parseInt(offset);
138+
int ceiling = (limit == null) ? Integer.MAX_VALUE : Integer.parseInt(limit);
139+
if (ceiling < 0 || lower < 0) {
140+
throw new BadRequestException("limit and offset can not be less than zero.");
141+
}
142+
range = (Integer.MAX_VALUE - lower < ceiling) ? Range.atLeast(lower)
143+
: Range.closedOpen(lower, lower + ceiling);
144+
} catch (NumberFormatException e) {
145+
throw new BadRequestException("limit and offset must be an integer value.");
146+
}
147+
try {
148+
DirectoryStream stream = labelsListHandler.listLabels(
149+
HttpServletRequests.roleAwareSubject(request),
150+
HttpServletRequests.roleAwareRestriction(request),
151+
range,
152+
attributes);
153+
154+
155+
Set<String> labelsList = new HashSet<>();
156+
157+
for (DirectoryEntry entry : stream) {
158+
String labelName = entry.getName();
159+
labelsList.add(labelName);
160+
}
161+
162+
163+
labels.setLabels(labelsList);
164+
165+
}
166+
catch (PermissionDeniedCacheException e) {
167+
if (RequestUser.isAnonymous()) {
168+
throw new NotAuthorizedException(e);
169+
} else {
170+
throw new ForbiddenException(e);
171+
}
172+
} catch (CacheException | InterruptedException ex) {
173+
LOGGER.warn(Exceptions.meaningfulMessage(ex));
174+
throw new InternalServerErrorException(ex);
175+
}
176+
return labels;
177+
}
107178

108179
@GET
109180
@ApiOperation(value = "Find metadata and optionally virtual directory contents.",
@@ -161,6 +232,8 @@ public JsonFileAttributes getFileAttributes(@ApiParam("Path of file or directory
161232
throw new BadRequestException("limit and offset must be an integer value.");
162233
}
163234
try {
235+
236+
164237
List<JsonFileAttributes> children = new ArrayList<>();
165238

166239
DirectoryStream stream = virtualDirectoryListHandler.listVirtualDirectory(

modules/dcache-frontend/src/main/resources/org/dcache/frontend/frontend.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@
9797
</bean>
9898
</constructor-arg>
9999
</bean>
100+
101+
<bean id="labels-list-handler" class="org.dcache.util.list.LabelsListHandler">
102+
<description>Client stub for directory listing</description>
103+
<constructor-arg>
104+
<bean class="diskCacheV111.util.PnfsHandler">
105+
<constructor-arg ref="pnfs-stub"/>
106+
</bean>
107+
</constructor-arg>
108+
</bean>
100109
<bean id="path-mapper" class="org.dcache.http.PathMapper">
101110
<description>Mapping between request paths and dCache paths</description>
102111
<property name="rootPath" value="${frontend.root}"/>

0 commit comments

Comments
 (0)