Skip to content

Commit

Permalink
Improved Swagger resource handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
noboomu committed May 2, 2017
1 parent 7280fa4 commit c0b2d65
Show file tree
Hide file tree
Showing 20 changed files with 116 additions and 67 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.sinistral</groupId>
<artifactId>proteus-core</artifactId>
<version>0.1.0</version>
<version>0.1.0-SNAPSHOT</version>
<name>proteus core</name>
<description>Proteus is an extremely light, fast, and flexible Java REST API framework built atop Undertow.</description>
<url>http://proteus.sinistral.io</url>
<url>http://github.com/noboomu/proteus</url>
<packaging>jar</packaging>

<licenses>
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/sinistral/proteus/ProteusApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public class ProteusApplication
public ProteusApplication()
{

injector = Guice.createInjector(new ConfigModule("application.conf"));
injector = Guice.createInjector(new ConfigModule());
injector.injectMembers(this);

}
Expand Down Expand Up @@ -186,7 +186,7 @@ public void shutdown() throws TimeoutException

log.info("Shutting down...");

serviceManager.stopAsync().awaitStopped(5, TimeUnit.SECONDS);
serviceManager.stopAsync().awaitStopped(8, TimeUnit.SECONDS);

log.info("Shutdown complete.");
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/sinistral/proteus/modules/ConfigModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ public class ConfigModule extends AbstractModule

public ConfigModule()
{
this.configFile = System.getenv("config.file");

if(this.configFile == null)
{
this.configFile = "application.conf";
}
}

public ConfigModule(String configFile)
Expand Down
166 changes: 105 additions & 61 deletions src/main/java/io/sinistral/proteus/services/SwaggerService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.jar.JarFile;

import javax.ws.rs.HttpMethod;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -38,13 +43,16 @@
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.RoutingHandler;
import io.undertow.server.handlers.ResponseCodeHandler;
import io.undertow.server.handlers.resource.ClassPathResourceManager;
import io.undertow.server.handlers.resource.FileResourceManager;
import io.undertow.server.handlers.resource.Resource;
import io.undertow.server.handlers.resource.ResourceHandler;
import io.undertow.util.CanonicalPathUtils;
import io.undertow.util.Headers;
import io.undertow.util.Methods;



public class SwaggerService extends BaseService implements Supplier<RoutingHandler>
{
Expand All @@ -53,9 +61,9 @@ public class SwaggerService extends BaseService implements Supplier<RoutingHan

protected io.sinistral.proteus.server.swagger.Reader reader = null;

protected final String swaggerResourcePath = "swagger";
protected final String swaggerResourcePathPrefix = "swagger";

protected final String swaggerThemesPath = "swagger/themes";
protected final String swaggerResourcePrefix = "io/sinistral/proteus/swagger";

protected Swagger swagger = null;

Expand Down Expand Up @@ -110,7 +118,9 @@ public class SwaggerService extends BaseService implements Supplier<RoutingHan

protected ObjectMapper mapper = new ObjectMapper();

protected ObjectWriter writer = null;
protected ObjectWriter writer = null;

protected Path swaggerResourcePath = null;

protected ClassLoader serviceClassLoader = null;

Expand Down Expand Up @@ -217,10 +227,9 @@ public void generateSwaggerHTML()
{
try
{

this.serviceClassLoader = this.getClass().getClassLoader();


final InputStream templateInputStream = this.getClass().getClassLoader().getResourceAsStream("swagger/index.html");
final InputStream templateInputStream = this.getClass().getClassLoader().getResourceAsStream(swaggerResourcePrefix + "/index.html");

byte[] templateBytes = IOUtils.toByteArray(templateInputStream);

Expand All @@ -238,20 +247,69 @@ public void generateSwaggerHTML()
templateString = templateString.replaceAll("\\{\\{ title \\}\\}",applicationName + " Swagger UI");
templateString = templateString.replaceAll("\\{\\{ swaggerFullPath \\}\\}","//" + host + ((port != 80 && port != 443) ? ":" + port : "") + this.swaggerBasePath + ".json");

this.swaggerIndexHTML = templateString;

this.swaggerIndexHTML = templateString;

URL url = this.getClass().getClassLoader().getResource(swaggerResourcePrefix);

// final InputStream resourceInputStream = this.getClass().getClassLoader().getResourceAsStream("swagger/swagger-ui-bundle.js");
//
// byte[] resourceBytes = IOUtils.toByteArray(resourceInputStream);
//
// String resourceString = new String(resourceBytes,Charset.defaultCharset());
//
// System.out.println("resource: " + resourceString);
//
if( url.toExternalForm().contains("!") )
{
log.debug("Copying Swagger resources...");

String jarPathString = url.toExternalForm().substring(0, url.toExternalForm().indexOf("!") ).replaceAll("file:", "").replaceAll("jar:", "");

File srcFile = new File(jarPathString);

try(JarFile jarFile = new JarFile(srcFile, false))
{
Path tmpDirParent = Paths.get(config.getString("application.tmpdir"));

Path swaggerTmpDir = tmpDirParent.resolve("swagger/");

if(swaggerTmpDir.toFile().exists())
{
log.debug("Deleting existing Swagger directory at " + swaggerTmpDir);

try
{
FileUtils.deleteDirectory(swaggerTmpDir.toFile());

} catch (java.lang.IllegalArgumentException e)
{
log.debug("Swagger tmp directory is not a directory...");
swaggerTmpDir.toFile().delete();
}
}

java.nio.file.Files.createDirectory( swaggerTmpDir );

this.swaggerResourcePath = swaggerTmpDir;

jarFile.stream().filter( ze -> ze.getName().endsWith("js") || ze.getName().endsWith("css") || ze.getName().endsWith("map") || ze.getName().endsWith("html") ).forEach( ze -> {

try
{
final InputStream entryInputStream = jarFile.getInputStream(ze);

String filename = ze.getName().substring(swaggerResourcePrefix.length() + 1);

Path entryFilePath = swaggerTmpDir.resolve(filename);

java.nio.file.Files.createDirectories(entryFilePath.getParent());

java.nio.file.Files.copy(entryInputStream, entryFilePath,StandardCopyOption.REPLACE_EXISTING);

} catch (Exception e)
{
log.error(e.getMessage() + " for entry " + ze.getName());
}
});
}
}
else
{
this.swaggerResourcePath = Paths.get(this.getClass().getClassLoader().getResource(this.swaggerResourcePrefix).toURI());
this.serviceClassLoader = this.getClass().getClassLoader();
}

} catch (Exception e)
{
Expand All @@ -266,16 +324,15 @@ public RoutingHandler get()

String pathTemplate = this.swaggerBasePath + ".json";

FileResourceManager resourceManager = new FileResourceManager(this.swaggerResourcePath.toFile(),1024);

router.add(HttpMethod.GET, pathTemplate, new HttpHandler(){

@Override
public void handleRequest(HttpServerExchange exchange) throws Exception
{

exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MimeTypes.APPLICATION_JSON_TYPE);


exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MimeTypes.APPLICATION_JSON_TYPE);
exchange.getResponseSender().send(swaggerSpec);

}
Expand All @@ -287,7 +344,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception

pathTemplate = this.swaggerBasePath;


router.add(HttpMethod.GET, pathTemplate , new HttpHandler(){

@Override
Expand All @@ -304,63 +361,50 @@ public void handleRequest(HttpServerExchange exchange) throws Exception

this.registeredEndpoints.add(EndpointInfo.builder().withConsumes("*/*").withProduces("text/html").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).build());

pathTemplate = this.swaggerBasePath + "/themes/*";

router.add(HttpMethod.GET, pathTemplate, new HttpHandler(){

@Override
public void handleRequest(HttpServerExchange exchange) throws Exception
{

String canonicalPath = CanonicalPathUtils.canonicalize((exchange.getRelativePath()));

canonicalPath = swaggerThemesPath + canonicalPath.split(swaggerBasePath+"/themes")[1];

try(final InputStream resourceInputStream = serviceClassLoader.getResourceAsStream(canonicalPath))
{

byte[] resourceBytes = IOUtils.toByteArray(resourceInputStream);

exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, io.sinistral.proteus.server.MediaType.TEXT_CSS_UTF8.toString());

exchange.getResponseSender().send(ByteBuffer.wrap(resourceBytes));

}
}

});


this.registeredEndpoints.add(EndpointInfo.builder().withConsumes("*/*").withProduces("text/css").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).build());


try
{


pathTemplate = this.swaggerBasePath + "/*";

router.add(HttpMethod.GET, pathTemplate, new HttpHandler(){
router.add(HttpMethod.GET, pathTemplate, new ResourceHandler(resourceManager){

@Override
public void handleRequest(HttpServerExchange exchange) throws Exception
{

String canonicalPath = CanonicalPathUtils.canonicalize((exchange.getRelativePath()));

canonicalPath = swaggerResourcePath + canonicalPath.split(swaggerBasePath)[1];

System.out.println("canonicalPath: " + canonicalPath);
canonicalPath = canonicalPath.split(swaggerBasePath)[1];

try(final InputStream resourceInputStream = serviceClassLoader.getResourceAsStream(canonicalPath))
{

byte[] resourceBytes = IOUtils.toByteArray(resourceInputStream);

exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, io.sinistral.proteus.server.MediaType.APPLICATION_JAVASCRIPT_UTF8.toString());

exchange.getResponseSender().send(ByteBuffer.wrap(resourceBytes));
exchange.setRelativePath(canonicalPath);

if(serviceClassLoader == null)
{
super.handleRequest(exchange);
}
else
{
canonicalPath = swaggerResourcePrefix + canonicalPath;

try(final InputStream resourceInputStream = serviceClassLoader.getResourceAsStream( canonicalPath))
{

if(resourceInputStream == null)
{
ResponseCodeHandler.HANDLE_404.handleRequest(exchange);
return;
}

byte[] resourceBytes = IOUtils.toByteArray(resourceInputStream);

io.sinistral.proteus.server.MediaType mediaType = io.sinistral.proteus.server.MediaType.getByFileName(canonicalPath);

exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, mediaType.toString());

exchange.getResponseSender().send(ByteBuffer.wrap(resourceBytes));
}
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@
<script src="{{ swaggerBasePath }}/swagger-ui-standalone-preset.js"> </script>
<script>

var hasDefaultTheme = "{{themePath}}" == 'default';
if(hasDefaultTheme)
var hasDefaultTheme = "{{ themePath }}" == 'default';
if(!hasDefaultTheme)
{
var newStyle = document.createElement('link');
newStyle.setAttribute('rel','stylesheet');
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit c0b2d65

Please sign in to comment.