package com.github.sriki77.apiproxy.instrument.io;
import com.github.sriki77.apiproxy.instrument.model.Endpoint;
import com.github.sriki77.apiproxy.instrument.model.PolicyUpdate;
import com.thoughtworks.xstream.XStream;
import org.apache.commons.io.FileUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.github.sriki77.apiproxy.instrument.io.Util.xStreamInit;
public class ProxyDirectoryHandler implements ProxyFileHandler {
private File apiProxyDir;
private File proxyFilesDir;
private File targetFilesDir;
private XStream xStream;
private DocumentBuilder builder;
private Transformer transformer;
protected File proxyDir;
private File policyDir;
private String proxyName;
public ProxyDirectoryHandler(File proxyDir) throws IOException, ParserConfigurationException, TransformerConfigurationException {
initProxyRelatedDirectories(proxyDir);
initXMLInfra();
determineProxyName();
}
private void determineProxyName() {
final File[] files = apiProxyDir.listFiles((dir, name) -> name.endsWith(".xml"));
if (files.length == 0) {
return;
}
try {
final Document document = builder.parse(files[0]);
final Node apiProxy = document.getElementsByTagName("APIProxy").item(0);
proxyName = apiProxy.getAttributes().getNamedItem("name").getNodeValue();
} catch (Exception e) {
throw new RuntimeException("Failed to parse file: " + files[0].getAbsolutePath(), e);
}
}
private void initXMLInfra() throws ParserConfigurationException, TransformerConfigurationException {
xStream = xStreamInit();
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
transformer = TransformerFactory.newInstance().newTransformer();
}
private void initProxyRelatedDirectories(File proxyDir) {
this.proxyDir = proxyDir;
apiProxyDir = apiProxyDir(proxyDir);
proxyFilesDir = getDirNamed("proxies");
targetFilesDir = getDirNamed("targets");
policyDir = getDirNamed("policies");
}
private File getDirNamed(String dirName) {
if (apiProxyDir == null) {
return null;
}
final File proxies = new File(apiProxyDir, dirName);
if (!proxies.exists()) {
return null;
}
return proxies;
}
private File apiProxyDir(File proxyDir) {
final File apiproxy = new File(proxyDir, "apiproxy");
if (!apiproxy.exists()) {
return null;
}
return apiproxy;
}
@Override
public List<Endpoint> getEndpoints() {
final List<Endpoint> endpoints = getProxyFiles().map(this::toEndpoint).collect(Collectors.toList());
if (endpoints.isEmpty()) {
System.err.println("Warning!! No endpoints found.");
}
return endpoints;
}
private Endpoint toEndpoint(File file) {
try {
final Document cleanedDocument = cleanupProxyFile(file);
final Endpoint endpoint = (Endpoint) xStream.fromXML(toString(cleanedDocument));
endpoint.init(file, builder.parse(file));
return endpoint;
} catch (Exception e) {
System.err.println("Failed Processing File: " + file);
throw new RuntimeException(e);
}
}
private String toString(Document cleanedDocument) throws TransformerException {
final StringWriter cleanedXml = new StringWriter();
transformer.transform(new DOMSource(cleanedDocument), new StreamResult(cleanedXml));
return cleanedXml.toString();
}
private Document cleanupProxyFile(File file) throws IOException, SAXException, TransformerException {
final Document document = builder.parse(file);
cleanupNode(document);
return document;
}
private void cleanupNode(Node node) {
final NodeList topChildren = node.getChildNodes();
for (int i = 0; i < topChildren.getLength(); i++) {
final Node n = topChildren.item(i);
NodeCleaner.clean(n);
cleanupNode(n);
}
}
Stream<File> getProxyFiles() {
return Stream.concat(getFilesFrom(proxyFilesDir), getFilesFrom(targetFilesDir));
}
private Stream<File> getFilesFrom(File filesDir) {
if (filesDir == null) {
return Stream.empty();
}
return Arrays.stream(filesDir.listFiles((dir, name) -> name.endsWith(".xml")));
}
@Override
public void updateEndpoint(Endpoint endpoint) {
try {
transformer.transform(new DOMSource(endpoint.getNode()), new StreamResult(endpoint.getXmlFile()));
} catch (TransformerException e) {
throw new RuntimeException(e);
}
final List<PolicyUpdate> updates = endpoint.updates();
updates.forEach(this::createFile);
}
@Override
public String proxyName() {
return proxyName;
}
private void createFile(PolicyUpdate u) {
try {
final File policyName = new File(policyDir, u.name + ".xml");
FileUtils.writeStringToFile(policyName, u.policyData);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() throws IOException {
}
}