Skip to content

Commit 35379e7

Browse files
committed
[1861] Split responsibilities of SysONLibraryPublicationHandler
Bug: #1861 Signed-off-by: Florent Latombe <florent.latombe@obeo.fr>
1 parent 0dcafa8 commit 35379e7

File tree

6 files changed

+282
-203
lines changed

6 files changed

+282
-203
lines changed

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ A new compartment named _satisfy requirements_ has also been added to `PartDefin
7777
- https://github.com/eclipse-syson/syson/issues/1818[#1818] [diagrams] On _Interconnection View_ diagrams, the top-level palette no longer proposes to create `Attributes` and `Ports`, which can not actually be displayed on the diagram's background.
7878
The tools are still available in the palette of e.g. `Parts`.
7979
- https://github.com/eclipse-syson/syson/issues/1819[#1819] [diagrams] In diagrams, when using _direct edit_ tool, you can now set a _multiplicity range part_ [n..n] after or before a _typing part_, _subsetting part_ or _redefinition part_.
80+
- https://github.com/eclipse-syson/syson/issues/1861[#1861] [publication] Split SysONLibraryPublicationHandler in 2 so the publishing logic can be extended or re-used through the ISysMLLibraryPublisher API.
8081

8182
=== New features
8283

Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 Obeo.
2+
* Copyright (c) 2025, 2026 Obeo.
33
* This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v2.0
55
* which accompanies this distribution, and is available at
@@ -12,89 +12,48 @@
1212
*******************************************************************************/
1313
package org.eclipse.syson.application.publication;
1414

15-
import java.util.ArrayList;
16-
import java.util.Collection;
1715
import java.util.List;
1816
import java.util.Objects;
19-
import java.util.Optional;
20-
import java.util.Set;
21-
import java.util.UUID;
22-
import java.util.stream.Collectors;
2317

24-
import org.eclipse.emf.ecore.EObject;
25-
import org.eclipse.emf.ecore.resource.Resource;
26-
import org.eclipse.emf.ecore.resource.ResourceSet;
2718
import org.eclipse.sirius.components.core.api.ErrorPayload;
2819
import org.eclipse.sirius.components.core.api.IEditingContextSearchService;
2920
import org.eclipse.sirius.components.core.api.IPayload;
30-
import org.eclipse.sirius.components.core.api.SuccessPayload;
31-
import org.eclipse.sirius.components.emf.ResourceMetadataAdapter;
32-
import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext;
33-
import org.eclipse.sirius.components.events.ICause;
3421
import org.eclipse.sirius.components.representations.Message;
3522
import org.eclipse.sirius.components.representations.MessageLevel;
36-
import org.eclipse.sirius.web.application.editingcontext.services.DocumentData;
37-
import org.eclipse.sirius.web.application.editingcontext.services.EPackageEntry;
38-
import org.eclipse.sirius.web.application.editingcontext.services.api.IResourceToDocumentService;
3923
import org.eclipse.sirius.web.application.library.dto.PublishLibrariesInput;
40-
import org.eclipse.sirius.web.application.library.services.LibraryMetadataAdapter;
24+
import org.eclipse.sirius.web.application.library.services.api.ILibraryApplicationService;
4125
import org.eclipse.sirius.web.application.library.services.api.ILibraryPublicationHandler;
42-
import org.eclipse.sirius.web.application.studio.services.library.api.DependencyGraph;
43-
import org.eclipse.sirius.web.domain.boundedcontexts.library.Library;
44-
import org.eclipse.sirius.web.domain.boundedcontexts.library.services.api.ILibrarySearchService;
45-
import org.eclipse.sirius.web.domain.boundedcontexts.project.Project;
4626
import org.eclipse.sirius.web.domain.boundedcontexts.project.services.api.IProjectSearchService;
4727
import org.eclipse.sirius.web.domain.boundedcontexts.projectsemanticdata.services.api.IProjectSemanticDataSearchService;
48-
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.Document;
49-
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
50-
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.services.api.ISemanticDataCreationService;
51-
import org.eclipse.sirius.web.domain.services.IResult;
52-
import org.eclipse.sirius.web.domain.services.Success;
53-
import org.eclipse.syson.application.publication.api.ISysONLibraryDependencyCollector;
54-
import org.eclipse.syson.sysml.SysmlPackage;
55-
import org.eclipse.syson.sysml.util.ElementUtil;
56-
import org.slf4j.Logger;
57-
import org.slf4j.LoggerFactory;
28+
import org.eclipse.syson.application.publication.api.ISysMLLibraryPublisher;
5829
import org.springframework.data.jdbc.core.mapping.AggregateReference;
5930
import org.springframework.stereotype.Service;
6031

6132
/**
6233
* {@link ILibraryPublicationHandler} for publishing libraries in SysON.
6334
*
35+
* @see ILibraryApplicationService
6436
* @see SysONLibraryPublicationListener
6537
* @author flatombe
6638
*/
6739
@Service
6840
public class SysONLibraryPublicationHandler implements ILibraryPublicationHandler {
69-
private static final Logger LOGGER = LoggerFactory.getLogger(SysONLibraryPublicationHandler.class);
70-
7141
private final IEditingContextSearchService editingContextSearchService;
7242

73-
private final ISemanticDataCreationService semanticDataCreationService;
74-
75-
private final IResourceToDocumentService resourceToDocumentService;
76-
7743
private final IProjectSemanticDataSearchService projectSemanticDataSearchService;
7844

7945
private final IProjectSearchService projectSearchService;
8046

81-
private final ILibrarySearchService librarySearchService;
82-
83-
private final ISysONLibraryDependencyCollector sysonLibraryDependencyCollector;
47+
private final ISysMLLibraryPublisher sysONSysMLLibraryPublisher;
8448

8549
public SysONLibraryPublicationHandler(final IEditingContextSearchService editingContextSearchService,
86-
final ISemanticDataCreationService semanticDataCreationService,
87-
final IResourceToDocumentService resourceToDocumentService,
88-
final IProjectSemanticDataSearchService projectSemanticDataSearchService, final IProjectSearchService projectSearchService,
89-
final ILibrarySearchService librarySearchService,
90-
final ISysONLibraryDependencyCollector sysonLibraryDependencyCollector) {
91-
this.projectSearchService = Objects.requireNonNull(projectSearchService);
50+
final IProjectSemanticDataSearchService projectSemanticDataSearchService,
51+
final IProjectSearchService projectSearchService,
52+
final ISysMLLibraryPublisher sysONSysMLLibraryPublisher) {
9253
this.editingContextSearchService = Objects.requireNonNull(editingContextSearchService);
93-
this.semanticDataCreationService = Objects.requireNonNull(semanticDataCreationService);
94-
this.resourceToDocumentService = Objects.requireNonNull(resourceToDocumentService);
9554
this.projectSemanticDataSearchService = Objects.requireNonNull(projectSemanticDataSearchService);
96-
this.librarySearchService = Objects.requireNonNull(librarySearchService);
97-
this.sysonLibraryDependencyCollector = Objects.requireNonNull(sysonLibraryDependencyCollector);
55+
this.projectSearchService = Objects.requireNonNull(projectSearchService);
56+
this.sysONSysMLLibraryPublisher = Objects.requireNonNull(sysONSysMLLibraryPublisher);
9857
}
9958

10059
@Override
@@ -107,147 +66,14 @@ public IPayload handle(final PublishLibrariesInput input) {
10766
return this.projectSearchService.findById(input.projectId())
10867
.map(project -> this.projectSemanticDataSearchService.findByProjectId(AggregateReference.to(input.projectId()))
10968
.flatMap(projectSemanticData -> this.editingContextSearchService.findById(projectSemanticData.getSemanticData().getId().toString()))
110-
.filter(IEMFEditingContext.class::isInstance)
111-
.map(IEMFEditingContext.class::cast)
112-
.map(editingContext -> this.handle(input, project, editingContext.getDomain().getResourceSet()))
113-
.orElseGet(() -> new ErrorPayload(input.id(),
114-
List.of(new Message("Could not find the editing context of project '%s'.".formatted(project.getName()), MessageLevel.ERROR)))))
69+
.map(editingContext -> this.sysONSysMLLibraryPublisher.publish(
70+
input,
71+
editingContext,
72+
project.getId(),
73+
project.getName(),
74+
input.version(),
75+
input.description()))
76+
.orElseGet(() -> new ErrorPayload(input.id(), List.of(new Message("Could not find the editing context of project '%s'.".formatted(project.getName()), MessageLevel.ERROR)))))
11577
.orElseGet(() -> new ErrorPayload(input.id(), List.of(new Message("Could not find project with ID '%s'.".formatted(input.projectId()), MessageLevel.ERROR))));
11678
}
117-
118-
protected IPayload handle(final PublishLibrariesInput input, final Project project, final ResourceSet resourceSet) {
119-
final IPayload result;
120-
121-
final String libraryName = project.getName();
122-
final String libraryVersion = input.version();
123-
124-
if (this.librarySearchService.findByNamespaceAndNameAndVersion(project.getId(), libraryName, libraryVersion).isPresent()) {
125-
// There is already a Library from our namespace with that name and version.
126-
result = new ErrorPayload(input.id(),
127-
List.of(new Message("Library '%s' (version '%s') already exists in namespace '%s'.".formatted(libraryName, libraryVersion, project.getId()), MessageLevel.ERROR)));
128-
} else {
129-
final Set<Resource> resourcesToPublish = this.getProperSysMLRootContents(resourceSet);
130-
if (!resourcesToPublish.isEmpty()) {
131-
DependencyGraph<Resource> dependencyGraph = this.sysonLibraryDependencyCollector.collectDependencies(resourceSet);
132-
133-
List<AggregateReference<SemanticData, UUID>> dependencies = this.getDependencies(dependencyGraph, resourcesToPublish);
134-
135-
final Optional<SemanticData> maybePublishedLibrarySemanticData = this.publishAsLibrary(input, resourcesToPublish, libraryName, libraryVersion, dependencies);
136-
// After this transaction is done, SysONLibraryPublicationListener reacts by also creating the
137-
// associated Library metadata.
138-
139-
result = maybePublishedLibrarySemanticData
140-
.map(publishedLibrary -> (IPayload) new SuccessPayload(input.id(),
141-
List.of(new Message(
142-
"Successfully published the SysML contents of project '%s' as version '%s' of library '%s'.".formatted(project.getName(),
143-
libraryVersion,
144-
libraryName),
145-
MessageLevel.SUCCESS))))
146-
.orElseGet(
147-
() -> new ErrorPayload(input.id(),
148-
List.of(new Message("Failed to publish the SysML contents of project '%s' as a library.".formatted(project.getName(), project.getId()),
149-
MessageLevel.ERROR))));
150-
} else {
151-
result = new ErrorPayload(input.id(),
152-
List.of(new Message("There are no SysML contents in project '%s' to publish as library.".formatted(project.getName()), MessageLevel.ERROR)));
153-
}
154-
}
155-
return result;
156-
}
157-
158-
private List<AggregateReference<SemanticData, UUID>> getDependencies(DependencyGraph<Resource> dependencyGraph, final Set<Resource> resourcesToPublish) {
159-
List<AggregateReference<SemanticData, UUID>> dependencies = new ArrayList<>();
160-
161-
for (Resource resourceToPublish : resourcesToPublish) {
162-
for (Resource dependencyCandidate : dependencyGraph.getDependencies(resourceToPublish)) {
163-
Optional<LibraryMetadataAdapter> optionalLibraryMetadata = dependencyCandidate.eAdapters().stream()
164-
.filter(LibraryMetadataAdapter.class::isInstance)
165-
.map(LibraryMetadataAdapter.class::cast)
166-
.findFirst();
167-
if (optionalLibraryMetadata.isPresent()) {
168-
LibraryMetadataAdapter libraryMetadataAdapter = optionalLibraryMetadata.get();
169-
this.librarySearchService.findByNamespaceAndNameAndVersion(libraryMetadataAdapter.getNamespace(), libraryMetadataAdapter.getName(), libraryMetadataAdapter.getVersion())
170-
.map(Library::getSemanticData)
171-
.ifPresentOrElse(dependencies::add, () -> LOGGER.warn("Cannot retrieve library {}:{}:{}", libraryMetadataAdapter.getNamespace(), libraryMetadataAdapter.getName(),
172-
libraryMetadataAdapter.getVersion()));
173-
}
174-
// Ignore the resource if it isn't a library: all non-library resources are published in a single
175-
// library in SysON.
176-
}
177-
}
178-
return dependencies.stream()
179-
.distinct()
180-
.toList();
181-
}
182-
183-
protected Optional<SemanticData> publishAsLibrary(final ICause parentCause, final Set<Resource> resources, final String name, final String version,
184-
final List<AggregateReference<SemanticData, UUID>> dependencies) {
185-
final ICause cause = new SysONPublishedLibrarySemanticDataCreationRequested(parentCause, name);
186-
// Remove the imported flag on the resource: the resource is now a library, and its read-only/read-write nature
187-
// should be determined by its import kind (reference or copy), and not whether it was imported from a textual
188-
// SysML file in the first place.
189-
resources.forEach(resource -> ElementUtil.setIsImported(resource, false));
190-
return this.createSemanticData(cause, resources, dependencies);
191-
}
192-
193-
protected Optional<SemanticData> createSemanticData(final ICause event, final Collection<Resource> resources, final List<AggregateReference<SemanticData, UUID>> dependencies) {
194-
final List<DocumentData> documentDatas = resources.stream()
195-
.map(resource -> this.resourceToDocumentService.toDocument(resource, false))
196-
.filter(Optional::isPresent)
197-
.map(Optional::get)
198-
.toList();
199-
final List<Document> documents = documentDatas.stream()
200-
.map(DocumentData::document)
201-
.toList();
202-
final List<String> domains = documentDatas.stream()
203-
.map(DocumentData::ePackageEntries)
204-
.flatMap(List::stream)
205-
.map(EPackageEntry::nsURI)
206-
.distinct()
207-
.toList();
208-
209-
final IResult<SemanticData> creationResult = this.semanticDataCreationService.create(event, documents, domains, dependencies);
210-
if (creationResult instanceof Success<SemanticData> creationSuccess) {
211-
return Optional.ofNullable(creationSuccess.data());
212-
} else {
213-
return Optional.empty();
214-
}
215-
}
216-
217-
protected Set<Resource> getProperSysMLRootContents(final ResourceSet resourceSet) {
218-
Objects.requireNonNull(resourceSet);
219-
220-
final List<Resource> dependencies = this.getDependenciesToPublishedLibraries(resourceSet);
221-
return resourceSet.getResources().stream()
222-
.filter(resource -> !dependencies.contains(resource))
223-
.collect(Collectors.toSet());
224-
}
225-
226-
protected List<Resource> getDependenciesToPublishedLibraries(ResourceSet resourceSet) {
227-
return resourceSet.getResources().stream()
228-
.filter(resource -> this.getLibraryMetadata(resource).isPresent()
229-
|| resource.getURI().scheme().equals(ElementUtil.KERML_LIBRARY_SCHEME)
230-
|| resource.getURI().scheme().equals(ElementUtil.SYSML_LIBRARY_SCHEME))
231-
.toList();
232-
}
233-
234-
protected Optional<LibraryMetadataAdapter> getLibraryMetadata(final Resource resource) {
235-
return resource.eAdapters().stream()
236-
.filter(LibraryMetadataAdapter.class::isInstance)
237-
.map(LibraryMetadataAdapter.class::cast)
238-
.findFirst();
239-
}
240-
241-
protected boolean isSysmlContent(final EObject rootEObject) {
242-
return rootEObject.eClass().getEPackage() == SysmlPackage.eINSTANCE;
243-
}
244-
245-
protected String getResourceName(final Resource resource) {
246-
return resource.eAdapters().stream()
247-
.filter(ResourceMetadataAdapter.class::isInstance)
248-
.map(ResourceMetadataAdapter.class::cast)
249-
.map(ResourceMetadataAdapter::getName)
250-
.findFirst()
251-
.orElse(null);
252-
}
25379
}

backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONLibraryPublicationListener.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 Obeo.
2+
* Copyright (c) 2025, 2026 Obeo.
33
* This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v2.0
55
* which accompanies this distribution, and is available at
@@ -14,7 +14,6 @@
1414

1515
import java.util.Objects;
1616

17-
import org.eclipse.sirius.web.application.library.dto.PublishLibrariesInput;
1817
import org.eclipse.sirius.web.domain.boundedcontexts.library.Library;
1918
import org.eclipse.sirius.web.domain.boundedcontexts.library.services.api.ILibraryCreationService;
2019
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
@@ -42,16 +41,15 @@ public SysONLibraryPublicationListener(final ILibraryCreationService libraryCrea
4241
@Transactional(propagation = Propagation.REQUIRES_NEW)
4342
@TransactionalEventListener
4443
public void onSemanticDataCreatedEvent(final SemanticDataCreatedEvent semanticDataCreatedEvent) {
45-
if (semanticDataCreatedEvent.causedBy() instanceof SysONPublishedLibrarySemanticDataCreationRequested request
46-
&& request.causedBy() instanceof PublishLibrariesInput publishLibrariesInput) {
44+
if (semanticDataCreatedEvent.causedBy() instanceof SysONPublishedLibrarySemanticDataCreationRequested request) {
4745
final SemanticData createdSemanticData = semanticDataCreatedEvent.semanticData();
4846

4947
final Library createdLibrary = Library.newLibrary()
50-
.namespace(publishLibrariesInput.projectId())
48+
.namespace(request.libraryNamespace())
5149
.name(request.libraryName())
50+
.version(request.libraryVersion())
51+
.description(request.libraryDescription())
5252
.semanticData(AggregateReference.to(createdSemanticData.getId()))
53-
.version(publishLibrariesInput.version())
54-
.description(publishLibrariesInput.description())
5553
.build(semanticDataCreatedEvent);
5654
this.libraryCreationService.createLibrary(createdLibrary);
5755
}

backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONPublishedLibrarySemanticDataCreationRequested.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 Obeo.
2+
* Copyright (c) 2025, 2026 Obeo.
33
* This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v2.0
55
* which accompanies this distribution, and is available at
@@ -27,16 +27,23 @@
2727
public record SysONPublishedLibrarySemanticDataCreationRequested(
2828
UUID id,
2929
ICause causedBy,
30-
String libraryName) implements ICause {
30+
String libraryNamespace,
31+
String libraryName,
32+
String libraryVersion,
33+
String libraryDescription) implements ICause {
3134

32-
public SysONPublishedLibrarySemanticDataCreationRequested(final ICause cause, final String libraryName) {
33-
this(UUID.randomUUID(), cause, libraryName);
35+
public SysONPublishedLibrarySemanticDataCreationRequested(final ICause cause, final String libraryNamespace, final String libraryName, final String libraryVersion,
36+
final String libraryDescription) {
37+
this(UUID.randomUUID(), cause, libraryNamespace, libraryName, libraryVersion, libraryDescription);
3438
}
3539

3640
public SysONPublishedLibrarySemanticDataCreationRequested {
3741
Objects.requireNonNull(id);
3842
Objects.requireNonNull(causedBy);
43+
Objects.requireNonNull(libraryNamespace);
3944
Objects.requireNonNull(libraryName);
45+
Objects.requireNonNull(libraryVersion);
46+
Objects.requireNonNull(libraryDescription);
4047
}
4148

4249
}

0 commit comments

Comments
 (0)