Skip to content

Commit 13142ac

Browse files
author
AlexanderLitus
committed
Merge pull request #122 from SpineEventEngine/proto-messages-validation
Proto command messages validation
2 parents a4bdf50 + fbf60ef commit 13142ac

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+3546
-246
lines changed

client/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ buildscript {
44
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
55
}
66
dependencies {
7-
classpath group: 'org.spine3.tools', name: 'proto-lookup-plugin', version: '1.1', changing: true
7+
classpath group: 'org.spine3.tools', name: 'protobuf-plugin', version: '1.2', changing: true
88
}
99
}
1010

@@ -13,7 +13,7 @@ dependencies {
1313
testCompile project (path: ":testutil", configuration: 'testArtifacts')
1414
}
1515

16-
apply plugin: 'org.spine3.tools.protolookup';
16+
apply plugin: 'org.spine3.tools.protobuf-plugin';
1717

1818
sourceSets {
1919
main {

client/src/main/java/org/spine3/base/Commands.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
package org.spine3.base;
2222

2323
import com.google.common.base.Predicate;
24+
import com.google.protobuf.Descriptors.FileDescriptor;
2425
import com.google.protobuf.Message;
2526
import com.google.protobuf.Timestamp;
27+
import org.spine3.protobuf.EntityPackagesMap;
2628
import org.spine3.protobuf.Messages;
2729
import org.spine3.protobuf.Timestamps;
2830
import org.spine3.time.ZoneOffset;
@@ -45,6 +47,13 @@
4547
*/
4648
public class Commands {
4749

50+
/**
51+
* A substring which the {@code .proto} file containing commands must have in its name.
52+
*/
53+
public static final String COMMANDS_FILE_SUBSTRING = "commands";
54+
55+
private static final char PROTO_FILE_SEPARATOR = '/';
56+
4857
private Commands() {}
4958

5059
/**
@@ -179,4 +188,32 @@ public static String formatMessageTypeAndId(String format, Message commandMessag
179188
final String result = String.format(format, commandType, id);
180189
return result;
181190
}
191+
192+
/**
193+
* Checks if the file is for commands.
194+
*
195+
* @param file a descriptor of a {@code .proto} file to check
196+
* @return {@code true} if the file name contains {@link #COMMANDS_FILE_SUBSTRING} substring, {@code false} otherwise
197+
*/
198+
public static boolean isCommandsFile(FileDescriptor file) {
199+
final String fqn = file.getName();
200+
final int startIndexOfFileName = fqn.lastIndexOf(PROTO_FILE_SEPARATOR) + 1;
201+
final String fileName = fqn.substring(startIndexOfFileName);
202+
final boolean isCommandsFile = fileName.contains(COMMANDS_FILE_SUBSTRING);
203+
return isCommandsFile;
204+
}
205+
206+
/**
207+
* Checks if the file belongs to an entity.
208+
*
209+
* <p>See {@link EntityPackagesMap} for more info.
210+
*
211+
* @param file a descriptor of a {@code .proto} file to check
212+
* @return {@code true} if the file belongs to an entity, {@code false} otherwise
213+
*/
214+
public static boolean isEntityFile(FileDescriptor file) {
215+
final String protoPackage = file.getPackage();
216+
final boolean isCommandForEntity = EntityPackagesMap.contains(protoPackage);
217+
return isCommandForEntity;
218+
}
182219
}

client/src/main/java/org/spine3/base/Identifiers.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import com.google.protobuf.UInt32Value;
3232
import com.google.protobuf.UInt64Value;
3333
import com.google.protobuf.util.TimeUtil;
34-
import org.spine3.protobuf.Messages;
3534

3635
import javax.annotation.Nullable;
3736
import java.util.Collection;
@@ -45,6 +44,8 @@
4544
import static com.google.common.collect.Maps.newHashMap;
4645
import static com.google.protobuf.TextFormat.shortDebugString;
4746
import static org.spine3.protobuf.Messages.fromAny;
47+
import static org.spine3.protobuf.Messages.toAny;
48+
import static org.spine3.protobuf.Values.newStringValue;
4849

4950
/**
5051
* This class manages conversion of identifies to/from string.
@@ -189,16 +190,16 @@ public static <I> Any idToAny(I id) {
189190
//noinspection IfStatementWithTooManyBranches,ChainOfInstanceofChecks
190191
if (id instanceof Message) {
191192
final Message message = (Message) id;
192-
anyId = Messages.toAny(message);
193+
anyId = toAny(message);
193194
} else if (id instanceof String) {
194195
final String strValue = (String) id;
195-
anyId = Messages.toAny(StringValue.newBuilder().setValue(strValue).build());
196+
anyId = toAny(newStringValue(strValue));
196197
} else if (id instanceof Integer) {
197198
final Integer intValue = (Integer) id;
198-
anyId = Messages.toAny(UInt32Value.newBuilder().setValue(intValue).build());
199+
anyId = toAny(UInt32Value.newBuilder().setValue(intValue).build());
199200
} else if (id instanceof Long) {
200201
final Long longValue = (Long) id;
201-
anyId = Messages.toAny(UInt64Value.newBuilder().setValue(longValue).build());
202+
anyId = toAny(UInt64Value.newBuilder().setValue(longValue).build());
202203
} else {
203204
throw unsupportedIdType(id);
204205
}

client/src/main/java/org/spine3/io/IoUtil.java

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,89 @@
2121
package org.spine3.io;
2222

2323
import com.google.common.collect.FluentIterable;
24+
import com.google.common.collect.ImmutableSet;
2425
import org.slf4j.Logger;
2526
import org.slf4j.LoggerFactory;
27+
import org.spine3.Internal;
2628

2729
import javax.annotation.Nullable;
2830
import java.io.Closeable;
2931
import java.io.Flushable;
3032
import java.io.IOException;
33+
import java.io.InputStream;
3134
import java.io.OutputStream;
35+
import java.net.URL;
36+
import java.util.Enumeration;
37+
import java.util.Properties;
3238

3339
import static com.google.common.base.Throwables.propagate;
3440

3541
/**
36-
* Utility class working with I/O: streams etc.
42+
* A utility class working with I/O: streams, resources, etc.
3743
*
3844
* @author Alexander Litus
3945
*/
46+
@Internal
4047
public class IoUtil {
4148

4249
private static final String ERROR_MESSAGE = "Exception while closing:";
4350

4451
private IoUtil() {}
4552

53+
/**
54+
* Loads all data from {@code .properties} file(s) into memory.
55+
*
56+
* <p>Logs {@link IOException} if it occurs.
57+
*
58+
* @param propsFilePath the path of the {@code .properties} file to load
59+
*/
60+
public static ImmutableSet<Properties> loadAllProperties(String propsFilePath) {
61+
final ImmutableSet.Builder<Properties> result = ImmutableSet.builder();
62+
final Enumeration<URL> resources = getResources(propsFilePath);
63+
if (resources == null) {
64+
return result.build();
65+
}
66+
while (resources.hasMoreElements()) {
67+
final URL resourceUrl = resources.nextElement();
68+
final Properties properties = loadPropertiesFile(resourceUrl);
69+
result.add(properties);
70+
}
71+
return result.build();
72+
}
73+
74+
private static Enumeration<URL> getResources(String propsFilePath) {
75+
final ClassLoader classLoader = getContextClassLoader();
76+
Enumeration<URL> resources = null;
77+
try {
78+
resources = classLoader.getResources(propsFilePath);
79+
} catch (IOException e) {
80+
if (log().isWarnEnabled()) {
81+
log().warn("Failed to load resources: " + propsFilePath, e);
82+
}
83+
}
84+
return resources;
85+
}
86+
87+
private static Properties loadPropertiesFile(URL resourceUrl) {
88+
final Properties properties = new Properties();
89+
InputStream inputStream = null;
90+
try {
91+
inputStream = resourceUrl.openStream();
92+
properties.load(inputStream);
93+
} catch (IOException e) {
94+
if (log().isWarnEnabled()) {
95+
log().warn("Failed to load properties file.", e);
96+
}
97+
} finally {
98+
close(inputStream);
99+
}
100+
return properties;
101+
}
102+
103+
private static ClassLoader getContextClassLoader() {
104+
return Thread.currentThread().getContextClassLoader();
105+
}
106+
46107
/**
47108
* Closes passed closeables one by one.
48109
*
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2016, TeamDev Ltd. All rights reserved.
3+
*
4+
* Redistribution and use in source and/or binary forms, with or without
5+
* modification, must retain the above copyright notice and the following
6+
* disclaimer.
7+
*
8+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
11+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
12+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
14+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
16+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19+
*/
20+
21+
package org.spine3.protobuf;
22+
23+
import com.google.common.collect.ImmutableSet;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
import org.spine3.Internal;
27+
import org.spine3.client.EntityType;
28+
29+
import javax.annotation.Nullable;
30+
import java.util.Map;
31+
import java.util.Properties;
32+
import java.util.Set;
33+
34+
import static com.google.common.collect.Maps.newHashMap;
35+
import static org.spine3.io.IoUtil.loadAllProperties;
36+
37+
/**
38+
* A map from Protobuf package to {@link EntityType} (the package contains files for this entity type (aggregate, etc)).
39+
*
40+
* <p>If there is a {@code state_of} option in the entity state Protobuf message definition,
41+
* all files in the current package are considered belonging to the specified type of the entity
42+
* (containing entity state, commands, events, etc).
43+
*
44+
* <p>This information is needed when validating a command to check if it is sent to an entity
45+
* (to check entity ID field in the command message).
46+
*
47+
* @author Alexander Litus
48+
*/
49+
@Internal
50+
public class EntityPackagesMap {
51+
52+
/**
53+
* A path to a file which contains Protobuf packages and appropriate {@link EntityType}s.
54+
* Is generated by Gradle during build process.
55+
*/
56+
private static final String PROPS_FILE_PATH = "entities.properties";
57+
58+
/**
59+
* A map from Protobuf package to {@link EntityType}.
60+
* The package contains files for this type of entity (aggregate, etc).
61+
*
62+
* <p>Example:
63+
* <p>{@code com.example.order} - {@code AGGREGATE}
64+
*/
65+
private static final Map<String, EntityType> PACKAGES_MAP = buildPackagesMap();
66+
67+
private EntityPackagesMap() {}
68+
69+
/**
70+
* Returns an entity type for the Protobuf package.
71+
*
72+
* @param protoPackage a protobuf package name where entity files are located
73+
* @return a type of the entity to which the package belongs
74+
*/
75+
@Nullable
76+
public static EntityType get(String protoPackage) {
77+
final EntityType result = PACKAGES_MAP.get(protoPackage);
78+
return result;
79+
}
80+
81+
/**
82+
* Returns {@code true} if the map contains an entity type for a Protobuf package, {@code false} otherwise.
83+
*
84+
* @param protoPackage a protobuf package name where entity files are located
85+
* @return {@code true} if the package belongs to an entity
86+
*/
87+
public static boolean contains(String protoPackage) {
88+
final EntityType result = get(protoPackage);
89+
final boolean contains = result != null;
90+
return contains;
91+
}
92+
93+
private static Map<String, EntityType> buildPackagesMap() {
94+
final Map<String, EntityType> result = newHashMap();
95+
final ImmutableSet<Properties> propertiesSet = loadAllProperties(PROPS_FILE_PATH);
96+
for (Properties properties : propertiesSet) {
97+
putTo(result, properties);
98+
}
99+
if (log().isDebugEnabled()) {
100+
log().debug("Entry count in EntityPackagesMap: " + result.size());
101+
}
102+
return result;
103+
}
104+
105+
private static void putTo(Map<String, EntityType> result, Properties properties) {
106+
final Set<String> protoPackages = properties.stringPropertyNames();
107+
for (String protoPackage : protoPackages) {
108+
final String entityTypeStr = properties.getProperty(protoPackage);
109+
final EntityType entityType = EntityType.valueOf(entityTypeStr);
110+
result.put(protoPackage, entityType);
111+
}
112+
}
113+
114+
private enum LogSingleton {
115+
INSTANCE;
116+
@SuppressWarnings("NonSerializableFieldInSerializableClass")
117+
private final Logger value = LoggerFactory.getLogger(EntityPackagesMap.class);
118+
}
119+
120+
private static Logger log() {
121+
return LogSingleton.INSTANCE.value;
122+
}
123+
}

client/src/main/java/org/spine3/protobuf/Messages.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,5 +208,4 @@ public static Descriptors.GenericDescriptor getClassDescriptor(Class<? extends M
208208
throw new MissingDescriptorException(clazz, e.getCause());
209209
}
210210
}
211-
212211
}

0 commit comments

Comments
 (0)