Skip to content
This repository has been archived by the owner on Aug 13, 2020. It is now read-only.

Basic version of JMS endpoint generator (happy path) #18

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions adapters/adapters-test-utils/pom.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>adapters</artifactId>
<groupId>uk.gov.justice.services</groupId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>adapters-test-utils</artifactId>

<dependencies>
Expand All @@ -29,4 +27,4 @@
<artifactId>raml-parser</artifactId>
</dependency>
</dependencies>
</project>
</project>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file only has formatting changes - can it be excluded from the changeset?

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uk.gov.justice.services.adapters.test.utils;
package uk.gov.justice.services.adapters.test.utils.builder;

import org.raml.model.Action;
import org.raml.model.ActionType;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package uk.gov.justice.services.adapters.test.utils;
package uk.gov.justice.services.adapters.test.utils.builder;

import org.raml.model.ActionType;
import org.raml.model.Raml;
import org.raml.model.Resource;

import static uk.gov.justice.services.adapters.test.utils.ActionBuilder.action;
import static uk.gov.justice.services.adapters.test.utils.ResourceBuilder.resource;
import static uk.gov.justice.services.adapters.test.utils.builder.ActionBuilder.action;
import static uk.gov.justice.services.adapters.test.utils.builder.ResourceBuilder.resource;

import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -70,4 +70,4 @@ public Raml build() {
raml.setResources(resources);
return raml;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uk.gov.justice.services.adapters.test.utils;
package uk.gov.justice.services.adapters.test.utils.builder;

import org.raml.model.Action;
import org.raml.model.ActionType;
Expand All @@ -9,6 +9,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static uk.gov.justice.services.adapters.test.utils.builder.ActionBuilder.action;

public class ResourceBuilder {
private final List<ActionBuilder> actionBuilders = new ArrayList<>();
Expand Down Expand Up @@ -61,4 +62,9 @@ public Resource build() {
resource.setActions(actions);
return resource;
}

public ResourceBuilder withDefaultAction() {
with(action().with(ActionType.POST));
return this;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uk.gov.justice.services.adapters.test.utils;
package uk.gov.justice.services.adapters.test.utils.compiler;

import org.reflections.scanners.SubTypesScanner;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uk.gov.justice.services.adapters.test.utils;
package uk.gov.justice.services.adapters.test.utils.compiler;

public class CompilationException extends RuntimeException {
private static final long serialVersionUID = 1L;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package uk.gov.justice.services.adapters.test.utils;
package uk.gov.justice.services.adapters.test.utils.compiler;

import com.google.common.collect.Sets;
import org.apache.commons.io.FileUtils;
Expand Down
56 changes: 56 additions & 0 deletions adapters/jms-adapter-generator/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>adapters</artifactId>
<groupId>uk.gov.justice.services</groupId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>jms-adapter-generator</artifactId>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>uk.gov.justice</groupId>
<artifactId>raml-generator-core</artifactId>
<version>${raml-maven-plugin.version}</version>
</dependency>
<dependency>
<groupId>org.raml</groupId>
<artifactId>raml-parser</artifactId>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>adapters-test-utils</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package uk.gov.justice.raml.jms.core;

import static java.lang.String.format;
import static org.apache.commons.io.FileUtils.write;
import static uk.gov.justice.raml.jms.core.TemplateRenderer.render;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.raml.model.Action;
import org.raml.model.ActionType;
import org.raml.model.Raml;
import org.raml.model.Resource;

import uk.gov.justice.raml.core.Generator;
import uk.gov.justice.raml.core.GeneratorConfig;
import uk.gov.justice.services.core.annotation.Component;

/**
* Generates JMS endpoint classes out of RAML object
*
*/
public class JmsEndpointGenerator implements Generator {

private static final String UTF_8 = "UTF-8";
private static final String TEMPLATE_LOADING_ERROR = "Failed to load template resource JmsListenerTemplate.tpm";
private static final String ACTIONS_EMPTY_ERROR = "No actions to process";
private static final String OUTPUT_FILE_GENERATION_ERROR = "Failed to create output file for %s";
private static final String FILENAME_POSTFIX = "JmsListener.java";
private static final String JMS_TEMPLATE_RESOURCE = "JmsListenerTemplate.tpm";
private static final Pattern MEDIA_TYPE_PATTERN = Pattern.compile("(application/vnd.)(\\S+)(\\+\\S+)");

/**
* Generates JMS endpoint classes out of RAML object
*
* @param raml
* - the RAML object
* @param generatorConfig
* - contains package of generated sources, as well as source and
* destination folders
*/
@Override
public void run(final Raml raml, final GeneratorConfig configuration) {
final File outputDirectory = createOutputDirectories(configuration);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already create and clear the output directory in the plugin - we should standardize on that approach, I think.

final String jmsTemplate = jmsListenerTemplate();
final Collection<Resource> ramlResourceModels = raml.getResources().values();

ramlResourceModels.stream()
.map(resource -> templateModelFrom(resource, configuration))
.forEach(model -> writeToTemplateFile(model, jmsTemplate, outputDirectory));

}

private void writeToTemplateFile(final Attributes attributes, final String jmsTemplate,
final File outputDirectory) {
final File file = new File(outputDirectory, createJmsFilenameFrom(attributes.uri));
try {
write(file, render(jmsTemplate, attributes.attributesMap));
} catch (IOException e) {
throw new JmsEndpointGeneratorException(format(OUTPUT_FILE_GENERATION_ERROR, attributes.uri),
e);
}
}

private Attributes templateModelFrom(final Resource resource, final GeneratorConfig configuration) {
final String uri = resource.getUri();
final HashMap<String, String> data = new HashMap<>();

data.put("PACKAGE_NAME", configuration.getBasePackageName());
data.put("CLASS_NAME", classNameOf(uri));
data.put("ADAPTER_TYPE", componentOf(uri).name());
data.put("DESTINATION_LOOKUP", destinationNameOf(uri));
data.put("MESSAGE_SELECTOR", messageSelectorsFrom(resource.getActions()));

return new Attributes(data, uri);
}

private String messageSelectorsFrom(final Map<ActionType, Action> actions) {
if (actions.isEmpty()) {
throw new JmsEndpointGeneratorException(ACTIONS_EMPTY_ERROR);
}
return format("'%s'", parse(actions.get(ActionType.POST)));
}

private String parse(final Action action) {
return action.getBody().keySet().stream().map(this::commandNameOf).collect(Collectors.joining("','"));
}

private String commandNameOf(final String mediaType) {
final Matcher m = MEDIA_TYPE_PATTERN.matcher(mediaType);
m.find();
return m.group(2);
}

private String destinationNameOf(final String uri) {
return uri.replaceAll("/", "");
}

private Component componentOf(final String uri) {
final String[] uriParts = uri.split("\\.");
return Component.valueOf(uriParts[uriParts.length - 1], uriParts[uriParts.length - 2]);
}

@SuppressWarnings("resource")
private String jmsListenerTemplate() {
try (final InputStream stream = getClass().getResourceAsStream(JMS_TEMPLATE_RESOURCE)) {
return new Scanner(stream, UTF_8).useDelimiter("\\A").next();
} catch (IOException e) {
throw new JmsEndpointGeneratorException(TEMPLATE_LOADING_ERROR, e);
}
}

private String createJmsFilenameFrom(final String uri) {
return classNameOf(uri) + FILENAME_POSTFIX;
}

private String classNameOf(final String uri) {
return toCamelCase(uri);
}

private String toCamelCase(final String s) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guava has a CaseFormat class for doing this sort of thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we've considered it but it does not fit the the bill here (we need to camel case dot and/or slash delimited string)

final String[] parts = s.split("/|\\.");
String camelCaseString = "";
for (String part : parts) {
camelCaseString = camelCaseString + toProperCase(part);
}
return camelCaseString;
}

private String toProperCase(final String s) {
if (!s.isEmpty()) {
return s.substring(0, 1).toUpperCase() +
s.substring(1).toLowerCase();
}
return s;
}

private File createOutputDirectories(final GeneratorConfig configuration) {
final File packageFolder = new File(format("%s/%s", configuration.getOutputDirectory(),
configuration.getBasePackageName().replace(".", "/")));
packageFolder.mkdirs();
return packageFolder;
}

private class Attributes {
final Map<String, String> attributesMap;
final String uri;

public Attributes(final Map<String, String> attributesMap, final String uri) {
this.attributesMap = attributesMap;
this.uri = uri;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package uk.gov.justice.raml.jms.core;

public class JmsEndpointGeneratorException extends RuntimeException {
private static final long serialVersionUID = 1L;

public JmsEndpointGeneratorException(final String message, final Throwable e) {
super(message, e);
}

public JmsEndpointGeneratorException(final String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package uk.gov.justice.raml.jms.core;

import static java.lang.String.format;

import java.util.Map;

import org.apache.commons.lang.text.StrBuilder;

/**
* Basic template rendering class
*
*/
public class TemplateRenderer {
public static final String ATTRIBUTE_KEY_FORMAT = "${%s}";

private TemplateRenderer() {
}

/**
* @param template - string containing template
* @param attributes - attribute keys and values to be used in rendering of the template
* @return - rendered template
*/
public static String render(final String template, final Map<String, String> attributes) {
final StrBuilder stringBuilder = new StrBuilder(template);
attributes.entrySet().forEach(e -> stringBuilder.replaceAll(format(ATTRIBUTE_KEY_FORMAT, e.getKey()), e.getValue()));
return stringBuilder.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ${PACKAGE_NAME};

import javax.inject.Inject;
import javax.ejb.MessageDriven;
import javax.ejb.ActivationConfigProperty;
import uk.gov.justice.services.core.annotation.Adapter;
import uk.gov.justice.services.core.dispatcher.Dispatcher;
import uk.gov.justice.services.core.jms.AbstractJMSListener;
import static uk.gov.justice.services.core.annotation.Component.*;

@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "${DESTINATION_LOOKUP}"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "messageSelector", propertyValue = "CPPNAME in(${MESSAGE_SELECTOR})")
})
@Adapter(${ADAPTER_TYPE})
public class ${CLASS_NAME}JmsListener extends AbstractJMSListener {

@Inject
Dispatcher dispatcher;

protected Dispatcher getDispatcher() {
return dispatcher;
}
}
Loading