Skip to content

Commit 9b8a631

Browse files
authored
Merge pull request #285 from mrubanov/dsl2010_refactor
Move binary-model related functionality to DDK
2 parents 2cdd972 + 2632969 commit 9b8a631

File tree

5 files changed

+217
-4
lines changed

5 files changed

+217
-4
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright (c) Avaloq Licence AG
3+
* Schwerzistrasse 6, 8807 Freienbach, Switzerland, http://www.avaloq.com
4+
* All Rights Reserved.
5+
*
6+
* This software is the confidential and proprietary information of Avaloq Licence AG.
7+
* You shall not disclose whole or parts of it and shall use it only in accordance with the terms of the
8+
* licence agreement you entered into with Avaloq Licence AG.
9+
*/
10+
11+
package com.avaloq.tools.ddk.xtext.builder;
12+
13+
import java.io.IOException;
14+
import java.io.InputStream;
15+
import java.io.OutputStream;
16+
import java.util.Map;
17+
18+
import org.eclipse.emf.common.util.URI;
19+
20+
import com.avaloq.tools.ddk.xtext.builder.IBinaryModelStore.NullBinaryModelStore;
21+
import com.google.inject.ImplementedBy;
22+
23+
24+
/**
25+
* Manages the binary resources.
26+
*/
27+
@ImplementedBy(NullBinaryModelStore.class)
28+
public interface IBinaryModelStore {
29+
30+
String SIZE_OPTION = "SIZE"; //$NON-NLS-1$
31+
32+
/**
33+
* Creates an input stream from which a binary model can be read.
34+
*
35+
* @param uri
36+
* binary model URI, must not be {@code null}
37+
* @param options
38+
* a map of options to influence the kind of stream that is returned; can be {@code null} and unrecognized options are ignored
39+
* @return input stream which can be used to read binary model
40+
* @throws IOException
41+
* in case of an I/O error
42+
*/
43+
InputStream createInputStream(URI uri, Map<?, ?> options) throws IOException;
44+
45+
/**
46+
* Creates an output stream to which a binary model can be written.
47+
*
48+
* @param uri
49+
* binary model URI, must not be {@code null}
50+
* @param options
51+
* a map of options to influence the kind of stream that is returned; can be {@code null} and unrecognized options are ignored
52+
* @return output stream which can be used to write binary model
53+
* @throws IOException
54+
* if a database error occurred
55+
*/
56+
OutputStream createOutputStream(URI uri, Map<?, ?> options) throws IOException;
57+
58+
/**
59+
* Checks whether a binary model exists with the given URI.
60+
*
61+
* @param uri
62+
* binary model URI, must not be {@code null}
63+
* @return {@code true} if binary model with given URI exists
64+
* @throws IOException
65+
* if a database error occurred
66+
*/
67+
boolean exists(URI uri) throws IOException;
68+
69+
/**
70+
* Deletes the binary model for the given URI.
71+
*
72+
* @param uri
73+
* binary model URI, must not be {@code null}
74+
* @throws IOException
75+
* if I/O exception occurred
76+
*/
77+
void delete(URI uri) throws IOException;
78+
79+
/**
80+
* Default no-op implementation throwing runtime exceptions when calling {@link #createInputStream(URI)} or {@link #createOutputStream(URI)} as that means
81+
* the application has not been configured correctly.
82+
*/
83+
class NullBinaryModelStore implements IBinaryModelStore {
84+
85+
@Override
86+
public InputStream createInputStream(final URI uri, final Map<?, ?> options) throws IOException {
87+
throw new UnsupportedOperationException();
88+
}
89+
90+
@Override
91+
public OutputStream createOutputStream(final URI uri, final Map<?, ?> options) throws IOException {
92+
throw new UnsupportedOperationException();
93+
}
94+
95+
@Override
96+
public boolean exists(final URI uri) throws IOException {
97+
return false;
98+
}
99+
100+
@Override
101+
public void delete(final URI uri) throws IOException {
102+
throw new UnsupportedOperationException();
103+
}
104+
105+
}
106+
}
107+
108+
/* Copyright (c) Avaloq Licence AG */

com.avaloq.tools.ddk.xtext.builder/src/com/avaloq/tools/ddk/xtext/builder/MonitoredClusteringBuilderState.java

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import java.util.Optional;
1818
import java.util.Queue;
1919
import java.util.Set;
20+
import java.util.concurrent.CompletableFuture;
21+
import java.util.concurrent.ForkJoinPool;
2022
import java.util.concurrent.TimeUnit;
2123
import java.util.concurrent.TimeoutException;
2224
import java.util.stream.Collectors;
@@ -38,6 +40,8 @@
3840
import org.eclipse.xtext.builder.impl.BuildData;
3941
import org.eclipse.xtext.builder.resourceloader.IResourceLoader;
4042
import org.eclipse.xtext.builder.resourceloader.IResourceLoader.LoadOperationException;
43+
import org.eclipse.xtext.generator.IFileSystemAccess;
44+
import org.eclipse.xtext.generator.IFileSystemAccessExtension3;
4145
import org.eclipse.xtext.resource.IEObjectDescription;
4246
import org.eclipse.xtext.resource.IReferenceDescription;
4347
import org.eclipse.xtext.resource.IResourceDescription;
@@ -49,6 +53,7 @@
4953
import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionDelta;
5054
import org.eclipse.xtext.resource.impl.ResourceDescriptionChangeEvent;
5155
import org.eclipse.xtext.resource.impl.ResourceDescriptionsData;
56+
import org.eclipse.xtext.resource.persistence.IResourceStorageFacade;
5257
import org.eclipse.xtext.resource.persistence.StorageAwareResource;
5358
import org.eclipse.xtext.service.OperationCanceledManager;
5459
import org.eclipse.xtext.util.CancelIndicator;
@@ -80,6 +85,7 @@
8085
import com.avaloq.tools.ddk.xtext.resource.AbstractResourceDescriptionDelta;
8186
import com.avaloq.tools.ddk.xtext.resource.extensions.ForwardingResourceDescriptions;
8287
import com.avaloq.tools.ddk.xtext.resource.extensions.IResourceDescriptions2;
88+
import com.avaloq.tools.ddk.xtext.resource.persistence.DirectLinkingResourceStorageFacade;
8389
import com.avaloq.tools.ddk.xtext.resource.persistence.DirectLinkingSourceLevelURIsAdapter;
8490
import com.avaloq.tools.ddk.xtext.scoping.ImplicitReferencesAdapter;
8591
import com.avaloq.tools.ddk.xtext.tracing.ITraceSet;
@@ -163,6 +169,11 @@ public class MonitoredClusteringBuilderState extends ClusteringBuilderState
163169
@Inject
164170
private IResourceServiceProvider.Registry resourceServiceProviderRegistry;
165171

172+
@Inject(optional = true)
173+
private IFileSystemAccess fileSystemAccess;
174+
175+
private final ForkJoinPool binaryStorageExecutor = new ForkJoinPool(4);
176+
166177
/**
167178
* Handle to the ResourceDescriptionsData we use viewed as a IResourceDescriptions2 (with findReferences()). Parent class does not provide direct access to
168179
* its ResourceDescriptionsData, and anyway it wouldn't know about out new interfaces.
@@ -414,6 +425,7 @@ protected Collection<Delta> doUpdate(final BuildData buildData, final ResourceDe
414425
final Map<URI, DerivedObjectAssociations> oldDerivedObjectAssociations = saveOldDerivedObjectAssociations(buildData);
415426

416427
buildData.getSourceLevelURICache().cacheAsSourceURIs(toBeDeleted);
428+
deleteBinaryResources(toBeDeleted);
417429
installSourceLevelURIs(buildData);
418430

419431
// Step 3: Create a queue; write new temporary resource descriptions for the added or updated resources
@@ -612,6 +624,7 @@ public boolean isCanceled() {
612624
if (resource instanceof XtextResource) {
613625
((XtextResource) resource).getCache().clear(resource);
614626
}
627+
storeBinaryResource(resource, buildData);
615628
traceSet.ended(ResourceProcessingEvent.class);
616629
buildData.getSourceLevelURICache().getSources().remove(changedURI);
617630
subProgress.worked(1);
@@ -644,6 +657,7 @@ public boolean isCanceled() {
644657
}
645658
traceSet.ended(BuildLinkingEvent.class);
646659
watchdog.interrupt();
660+
awaitBinaryStorageExecutorQuiescence();
647661
}
648662
return allDeltas;
649663
// CHECKSTYLE:CHECK-ON NestedTryDepth
@@ -666,6 +680,70 @@ protected Resource addResource(final Resource resource, final ResourceSet resour
666680
}
667681
}
668682

683+
/**
684+
* Stores the process resource as a binary if it doesn't contain syntax or linking errors.
685+
*
686+
* @param resource
687+
* resource to store, must not be {@code null}
688+
* @param buildData
689+
* build data, must not be {@code null}
690+
*/
691+
protected void storeBinaryResource(final Resource resource, final BuildData buildData) {
692+
if (resource instanceof StorageAwareResource && ((StorageAwareResource) resource).getResourceStorageFacade() != null
693+
&& fileSystemAccess instanceof IFileSystemAccessExtension3) {
694+
CompletableFuture.runAsync(() -> {
695+
final long maxTaskExecutionNanos = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
696+
697+
try {
698+
long elapsed = System.nanoTime();
699+
700+
IResourceStorageFacade storageFacade = ((StorageAwareResource) resource).getResourceStorageFacade();
701+
storageFacade.saveResource((StorageAwareResource) resource, (IFileSystemAccessExtension3) fileSystemAccess);
702+
buildData.getSourceLevelURICache().getSources().remove(resource.getURI());
703+
704+
elapsed = System.nanoTime() - elapsed;
705+
if (elapsed > maxTaskExecutionNanos) {
706+
LOGGER.info("saving binary taking longer than expected (" + elapsed + " ns) : " + resource.getURI()); //$NON-NLS-1$ //$NON-NLS-2$
707+
}
708+
// CHECKSTYLE:OFF
709+
} catch (Throwable ex) {
710+
// CHECKSTYLE:ON
711+
LOGGER.error("Failed to save binary for " + resource.getURI(), ex); //$NON-NLS-1$
712+
}
713+
}, binaryStorageExecutor);
714+
}
715+
}
716+
717+
/**
718+
* Deletes the binary models for the given set of URIs.
719+
*
720+
* @param toBeDeleted
721+
* set of URIs, must not be {@code null}
722+
*/
723+
protected void deleteBinaryResources(final Set<URI> toBeDeleted) {
724+
if (fileSystemAccess == null) {
725+
return;
726+
}
727+
for (URI uri : toBeDeleted) {
728+
IResourceStorageFacade resourceStorageFacade = resourceServiceProviderRegistry.getResourceServiceProvider(uri).get(IResourceStorageFacade.class);
729+
if (resourceStorageFacade instanceof DirectLinkingResourceStorageFacade) {
730+
((DirectLinkingResourceStorageFacade) resourceStorageFacade).deleteStorage(uri, fileSystemAccess);
731+
}
732+
}
733+
}
734+
735+
/**
736+
* Waits until binary models are stored.
737+
*/
738+
protected void awaitBinaryStorageExecutorQuiescence() {
739+
int activeThreadCount = binaryStorageExecutor.getActiveThreadCount();
740+
long queuedTaskCount = binaryStorageExecutor.getQueuedTaskCount();
741+
if (!binaryStorageExecutor.awaitQuiescence(1, TimeUnit.MINUTES)) {
742+
LOGGER.warn(String.format("Binary resource storage tasks not completed in time, start with task / queue %d / %d; now have %d / %d", //$NON-NLS-1$
743+
activeThreadCount, queuedTaskCount, binaryStorageExecutor.getActiveThreadCount(), binaryStorageExecutor.getQueuedTaskCount()));
744+
}
745+
}
746+
669747
/**
670748
* Log the first and last 10 StackOverflowError Stack Trace.
671749
*
@@ -841,7 +919,9 @@ protected boolean recordDeltaAsNew(final Delta newDelta) {
841919
* The progress monitor used for user feedback
842920
* @return the list of {@link URI}s of loaded resources to be processed in the second phase
843921
*/
844-
private List<URI> writeResources(final Collection<URI> toWrite, final BuildData buildData, final IResourceDescriptions oldState, final CurrentDescriptions newState, final IProgressMonitor monitor) { // NOPMD NPath Complexity
922+
private List<URI> writeResources(final Collection<URI> toWrite, final BuildData buildData, final IResourceDescriptions oldState, final CurrentDescriptions newState, final IProgressMonitor monitor) { // NOPMD
923+
// NPath
924+
// Complexity
845925
ResourceSet resourceSet = buildData.getResourceSet();
846926
IProject currentProject = getBuiltProject(buildData);
847927
List<URI> toBuild = Lists.newLinkedList();
@@ -885,7 +965,7 @@ private List<URI> writeResources(final Collection<URI> toWrite, final BuildData
885965
if (uri == null && ex instanceof LoadOperationException) { // NOPMD
886966
uri = ((LoadOperationException) ex).getUri();
887967
}
888-
LOGGER.error(NLS.bind(Messages.MonitoredClusteringBuilderState_CANNOT_LOAD_RESOURCE, uri != null ? uri: "unknown uri"), ex); //$NON-NLS-1$
968+
LOGGER.error(NLS.bind(Messages.MonitoredClusteringBuilderState_CANNOT_LOAD_RESOURCE, uri != null ? uri : "unknown uri"), ex); //$NON-NLS-1$
889969
if (resource != null) {
890970
resourceSet.getResources().remove(resource);
891971
}
@@ -971,6 +1051,10 @@ protected void writeNewResourceDescriptions(final BuildData buildData, final IRe
9711051
*/
9721052
@Override
9731053
protected void clearResourceSet(final ResourceSet resourceSet) {
1054+
// this is important as otherwise the resources would unexpectedly become detached from the resource set
1055+
while (!binaryStorageExecutor.awaitQuiescence(1, TimeUnit.SECONDS)) {
1056+
LOGGER.warn("Waiting for binary resource storage tasks to complete."); //$NON-NLS-1$
1057+
}
9741058
traceSet.started(BuildResourceSetClearEvent.class, resourceSet.getResources().size());
9751059
try {
9761060
EmfResourceSetUtil.clearResourceSetWithoutNotifications(resourceSet);

com.avaloq.tools.ddk.xtext.builder/src/com/avaloq/tools/ddk/xtext/builder/layered/DefaultXtextTargetPlatform.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@
1212

1313
import java.io.IOException;
1414
import java.util.Collection;
15+
import java.util.Collections;
1516
import java.util.Map;
1617

1718
import org.eclipse.core.runtime.IProgressMonitor;
1819
import org.eclipse.xtext.builder.builderState.PersistedStateProvider;
1920
import org.eclipse.xtext.resource.impl.ResourceDescriptionsData;
2021

22+
import com.avaloq.tools.ddk.xtext.builder.IBinaryModelStore;
2123
import com.avaloq.tools.ddk.xtext.builder.IDerivedObjectAssociationsStore;
2224
import com.avaloq.tools.ddk.xtext.extensions.DelegatingResourceDescriptionsData;
2325
import com.avaloq.tools.ddk.xtext.extensions.IResourceDescriptionsData;
24-
import com.google.common.collect.ImmutableMap;
2526
import com.google.inject.Inject;
2627

2728

@@ -73,12 +74,17 @@ public IDerivedObjectAssociationsStore getAssociationsStore() {
7374
/** {@inheritDoc} */
7475
@Override
7576
public Map<String, String> getMetadata(final Collection<String> keys, final IProgressMonitor monitor) {
76-
return ImmutableMap.of();
77+
return Collections.emptyMap();
7778
}
7879

7980
/** {@inheritDoc} */
8081
@Override
8182
public void setMetadata(final Map<String, String> options) {
8283
// nothing to do
8384
}
85+
86+
@Override
87+
public IBinaryModelStore getBinaryModelStore() {
88+
return null;
89+
}
8490
}

com.avaloq.tools.ddk.xtext.builder/src/com/avaloq/tools/ddk/xtext/builder/layered/IXtextTargetPlatform.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import org.eclipse.core.runtime.IProgressMonitor;
1818

19+
import com.avaloq.tools.ddk.xtext.builder.IBinaryModelStore;
1920
import com.avaloq.tools.ddk.xtext.builder.IDerivedObjectAssociationsStore;
2021
import com.avaloq.tools.ddk.xtext.extensions.IResourceDescriptionsData;
2122
import com.google.inject.ImplementedBy;
@@ -109,6 +110,13 @@ public interface IXtextTargetPlatform {
109110
*/
110111
IDerivedObjectAssociationsStore getAssociationsStore();
111112

113+
/**
114+
* Returns the store of binary resources.
115+
*
116+
* @return the platform's store, can be {@code null} if not supported
117+
*/
118+
IBinaryModelStore getBinaryModelStore();
119+
112120
/**
113121
* Platforms are supposed to be closed when no longer in use. At any time, there must be only one open platform.
114122
* The platform manager should ensure that there are never two platforms open at the same time.

com.avaloq.tools.ddk.xtext.builder/src/com/avaloq/tools/ddk/xtext/builder/layered/NullXtextTargetPlatform.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import org.eclipse.core.runtime.IProgressMonitor;
1717

18+
import com.avaloq.tools.ddk.xtext.builder.IBinaryModelStore;
1819
import com.avaloq.tools.ddk.xtext.builder.IDerivedObjectAssociationsStore;
1920
import com.avaloq.tools.ddk.xtext.extensions.IResourceDescriptionsData;
2021

@@ -53,6 +54,11 @@ public IDerivedObjectAssociationsStore getAssociationsStore() {
5354
return null;
5455
}
5556

57+
@Override
58+
public IBinaryModelStore getBinaryModelStore() {
59+
return null;
60+
}
61+
5662
/** {@inheritDoc} */
5763
@Override
5864
public Map<String, String> getMetadata(final Collection<String> keys, final IProgressMonitor monitor) {
@@ -64,4 +70,5 @@ public Map<String, String> getMetadata(final Collection<String> keys, final IPro
6470
public void setMetadata(final Map<String, String> options) {
6571
// Nothing to do.
6672
}
73+
6774
}

0 commit comments

Comments
 (0)