Initial commit

This commit is contained in:
Keith Armstrong 2024-12-18 16:12:28 +00:00
commit 1048f0508f
15 changed files with 950 additions and 0 deletions

29
.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
### IntelliJ IDEA ###
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

9
.idea/artifacts/soapClient_jar.xml generated Normal file
View File

@ -0,0 +1,9 @@
<component name="ArtifactManager">
<artifact type="jar" name="soapClient:jar">
<output-path>$PROJECT_DIR$/out/artifacts/soapClient_jar</output-path>
<root id="archive" name="soapClient.jar">
<element id="module-output" name="soapClient" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jcommander-1.27.jar" path-in-jar="/" />
</root>
</artifact>
</component>

9
.idea/libraries/jcommander_1_27.xml generated Normal file
View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="jcommander-1.27">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/jcommander-1.27.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

6
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="19" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/soapClient.iml" filepath="$PROJECT_DIR$/soapClient.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

BIN
lib/jcommander-1.27.jar Normal file

Binary file not shown.

12
soapClient.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="jcommander-1.27" level="project" />
</component>
</module>

3
src/META-INF/MANIFEST.MF Normal file
View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: com.belkast.soap.Webservice

View File

@ -0,0 +1,396 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.belkast.soap;
/**
*
* @author Keith
*/
public class Base64 {
//The line separator string of the operating system.
private static final String systemLineSeparator = System.getProperty("line.separator");
//Mapping table from 6-bit nibbles to Base64 characters.
private static char[] map1 = new char[64];
static {
int i=0;
for (char c='A'; c<='Z'; c++) map1[i++] = c;
for (char c='a'; c<='z'; c++) map1[i++] = c;
for (char c='0'; c<='9'; c++) map1[i++] = c;
map1[i++] = '+'; map1[i++] = '/'; }
//Mapping table from Base64 characters to 6-bit nibbles.
private static byte[] map2 = new byte[128];
static {
for (int i=0; i<map2.length; i++) map2[i] = -1;
for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
/**
* Encodes a string into Base64 format.
* No blanks or line breaks are inserted.
* @param s A String to be encoded.
* @return A String containing the Base64 encoded data.
*/
public static String encodeString (String s) {
return new String(encode(s.getBytes())); }
/**
* Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters.
* This method is compatible with <code>sun.misc.BASE64Encoder.encodeBuffer(byte[])</code>.
* @param in An array containing the data bytes to be encoded.
* @return A String containing the Base64 encoded data, broken into lines.
*/
public static String encodeLines (byte[] in) {
return encodeLines(in, 0, in.length, 76, systemLineSeparator); }
/**
* Encodes a byte array into Base 64 format and breaks the output into lines.
* @param in An array containing the data bytes to be encoded.
* @param iOff Offset of the first byte in <code>in</code> to be processed.
* @param iLen Number of bytes to be processed in <code>in</code>, starting at <code>iOff</code>.
* @param lineLen Line length for the output data. Should be a multiple of 4.
* @param lineSeparator The line separator to be used to separate the output lines.
* @return A String containing the Base64 encoded data, broken into lines.
*/
public static String encodeLines (byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) {
int blockLen = (lineLen*3) / 4;
if (blockLen <= 0) throw new IllegalArgumentException();
int lines = (iLen+blockLen-1) / blockLen;
int bufLen = ((iLen+2)/3)*4 + lines*lineSeparator.length();
StringBuilder buf = new StringBuilder(bufLen);
int ip = 0;
while (ip < iLen) {
int l = Math.min(iLen-ip, blockLen);
buf.append (encode(in, iOff+ip, l));
buf.append (lineSeparator);
ip += l; }
return buf.toString(); }
/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted in the output.
* @param in An array containing the data bytes to be encoded.
* @return A character array containing the Base64 encoded data.
*/
public static char[] encode (byte[] in) {
return encode(in, 0, in.length); }
/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted in the output.
* @param in An array containing the data bytes to be encoded.
* @param iLen Number of bytes to process in <code>in</code>.
* @return A character array containing the Base64 encoded data.
*/
public static char[] encode (byte[] in, int iLen) {
return encode(in, 0, iLen); }
/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted in the output.
* @param in An array containing the data bytes to be encoded.
* @param iOff Offset of the first byte in <code>in</code> to be processed.
* @param iLen Number of bytes to process in <code>in</code>, starting at <code>iOff</code>.
* @return A character array containing the Base64 encoded data.
*/
public static char[] encode (byte[] in, int iOff, int iLen) {
int oDataLen = (iLen*4+2)/3; // output length without padding
int oLen = ((iLen+2)/3)*4; // output length including padding
char[] out = new char[oLen];
int ip = iOff;
int iEnd = iOff + iLen;
int op = 0;
while (ip < iEnd) {
int i0 = in[ip++] & 0xff;
int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
int o0 = i0 >>> 2;
int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
int o3 = i2 & 0x3F;
out[op++] = map1[o0];
out[op++] = map1[o1];
out[op] = op < oDataLen ? map1[o2] : '='; op++;
out[op] = op < oDataLen ? map1[o3] : '='; op++; }
return out; }
/**
* Decodes a string from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded input data.
* @param s A Base64 String to be decoded.
* @return A String containing the decoded data.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static String decodeString (String s) {
return new String(decode(s)); }
/**
* Decodes a byte array from Base64 format and ignores line separators, tabs and blanks.
* CR, LF, Tab and Space characters are ignored in the input data.
* This method is compatible with <code>sun.misc.BASE64Decoder.decodeBuffer(String)</code>.
* @param s A Base64 String to be decoded.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static byte[] decodeLines (String s) {
char[] buf = new char[s.length()];
int p = 0;
for (int ip = 0; ip < s.length(); ip++) {
char c = s.charAt(ip);
if (c != ' ' && c != '\r' && c != '\n' && c != '\t')
buf[p++] = c; }
return decode(buf, 0, p); }
/**
* Decodes a byte array from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded input data.
* @param s A Base64 String to be decoded.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static byte[] decode (String s) {
return decode(s.toCharArray()); }
/**
* Decodes a byte array from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded input data.
* @param in A character array containing the Base64 encoded data.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static byte[] decode (char[] in) {
return decode(in, 0, in.length); }
/**
* Decodes a byte array from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded input data.
* @param in A character array containing the Base64 encoded data.
* @param iOff Offset of the first character in <code>in</code> to be processed.
* @param iLen Number of characters to process in <code>in</code>, starting at <code>iOff</code>.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException If the input is not valid Base64 encoded data.
*/
public static byte[] decode (char[] in, int iOff, int iLen) {
if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--;
int oLen = (iLen*3) / 4;
byte[] out = new byte[oLen];
int ip = iOff;
int iEnd = iOff + iLen;
int op = 0;
while (ip < iEnd) {
int i0 = in[ip++];
int i1 = in[ip++];
int i2 = ip < iEnd ? in[ip++] : 'A';
int i3 = ip < iEnd ? in[ip++] : 'A';
if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
int b0 = map2[i0];
int b1 = map2[i1];
int b2 = map2[i2];
int b3 = map2[i3];
if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
int o0 = ( b0 <<2) | (b1>>>4);
int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
int o2 = ((b2 & 3)<<6) | b3;
out[op++] = (byte)o0;
if (op<oLen) out[op++] = (byte)o1;
if (op<oLen) out[op++] = (byte)o2; }
return out; }
}

View File

@ -0,0 +1,30 @@
package com.belkast.soap;
import com.beust.jcommander.*;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
public class Parser {
@Parameter(description = "parameters")
public List<String> parameters = new ArrayList<String>();
@Parameter(names = "--props" , description = "Location of the Properties file")
public String varPropertiesFile;
@Parameter(names = "--key" , description = "Key used for encryption", required = true)
public String varKey;
@Parameter(names = "--encrypt", description = "Encrypt this value using the Key")
public String varEncrypt;
@Parameter(names = "--replace", description = "Tokens for which to search", variableArity = true, splitter = SpaceParameterSplitter.class)
public List<String> varTokens = new ArrayList<String>();
@Parameter(names = "--help", description = "print this message", help = true)
public boolean help;
@Parameter(names = "--debug", description = "Used to debug the program")
public String debug;
}

View File

@ -0,0 +1,39 @@
package com.belkast.soap;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Protector
{
private static final String ALGORITHM = "AES";
public static char[] encrypt(byte[] varKey, String valueToEnc) throws Exception
{
System.out.println("## Encrypting password ##");
Key key = generateKey(varKey);
Cipher c = Cipher.getInstance("AES");
c.init(1, key);
byte[] encValue = c.doFinal(valueToEnc.getBytes());
char[] encryptedValue = Base64.encode(encValue);
return encryptedValue;
}
public static String decrypt(byte[] varKey, String encryptedValue) throws Exception
{
System.out.println("## Decrypting password ##");
Key key = generateKey(varKey);
Cipher c = Cipher.getInstance("AES");
c.init(2, key);
byte[] decordedValue = Base64.decode(encryptedValue);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
private static Key generateKey(byte[] varKey) throws Exception
{
Key key = new SecretKeySpec(varKey, "AES");
return key;
}
}

View File

@ -0,0 +1,14 @@
package com.belkast.soap;
import com.beust.jcommander.converters.*;
import java.util.Arrays;
import java.util.List;
public class SpaceParameterSplitter implements IParameterSplitter
{
public List<String> split(String value)
{
return Arrays.asList(value.split("###"));
}
}

View File

@ -0,0 +1,386 @@
package com.belkast.soap;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import com.beust.jcommander.*;
public class Webservice
{
private static Boolean varDebug;
private static String varDebugToFile = "false";
private static String varPassword;
private static String programStatus;
private static String NEW_STATUS = null;
public static void main(String[] args) throws Exception
{
try
{
soapResponse(args);
}
catch(Exception ex)
{
throw new RuntimeException(ex);
}
}
public static String soapResponse(String[] args) throws Exception
{
SimpleDateFormat varFormat = new SimpleDateFormat("HH:mm:ss.SSS");
Date dateStart = new Date();
System.out.println("\n<debug>Start Time: " + varFormat.format(dateStart) + "</debug>");
Parser commandLine = new Parser();
JCommander jct = new JCommander (commandLine);
File configFilePath = new File("");
try
{
jct.parse(args);
}
catch (ParameterException ex)
{
System.out.println(ex.getMessage());
jct.usage();
}
if (commandLine.varKey != null && commandLine.varEncrypt != null)
{
varPassword = encryptPassword(commandLine.varKey, commandLine.varEncrypt);
return "";
}
if (commandLine.varPropertiesFile == null)
{
programStatus = "Properties file not valid";
return programStatus;
}
varDebug = Boolean.parseBoolean(commandLine.debug);
System.out.println("<debug>Debug is: " + varDebug.toString() + "</debug>");
try
{
configFilePath = new File(commandLine.varPropertiesFile);
FileReader fileReader = new FileReader(configFilePath);
}
catch (IOException e)
{
programStatus = e.toString();
return programStatus;
}
String varDelimiter = readProperty(varDebug, configFilePath, "ARGUMENT_DELIMITER");
varDebugToFile = readProperty(varDebug, configFilePath, "DEBUG_TO_FILE");
debugMessage(varDebug, "==================== START ====================", varDebugToFile);
debugMessage(varDebug, "Start Time: " + varFormat.format(dateStart), varDebugToFile);
HashMap hashMap = generateHashMap(commandLine.varTokens.size(), varDelimiter, commandLine.varTokens);
System.out.println();
String varURL = readProperty(varDebug, configFilePath, "SHIM_URL");
String varInputFile = readProperty(varDebug, configFilePath, "INPUT_FILE");
String varUsername = readProperty(varDebug, configFilePath, "USERNAME");
String varPasswordTemp = readProperty(varDebug, configFilePath, "PASSWORD");
String varUseSSL = readProperty(varDebug, configFilePath, "SSL");
String varKeystore_LOC = readProperty(varDebug, configFilePath, "JAVA_KS_LOCATION");
String varKeystore_PW = readProperty(varDebug, configFilePath, "JAVA_KS_PASSWORD");
try
{
varPassword = Protector.decrypt(commandLine.varKey.getBytes(), varPasswordTemp);
}
catch (Exception e)
{
programStatus = e.toString();
return programStatus;
}
if ((varURL.equalsIgnoreCase("")) || (varInputFile.equalsIgnoreCase("")))
{
programStatus = "Properties file " + configFilePath.toString() + " is not valid";
return programStatus;
}
System.out.println();
String varDocument = readTextFile(varInputFile, hashMap);
if (varDocument.length() > 0)
{
String response = writeMessage(varUsername, varPassword, varURL, varInputFile, varUseSSL, varKeystore_LOC, varKeystore_PW, hashMap, varDocument.getBytes());
System.out.println();
System.out.println("<debug>SOAP Document received: " + response + "</debug>");
debugMessage(varDebug, "SOAP Document received: " + response, varDebugToFile);
Date dateEnd = new Date();
System.out.println("\n<debug>End Time: " + varFormat.format(dateEnd) + "</debug>");
debugMessage(varDebug, "End Time: " + varFormat.format(dateEnd), varDebugToFile);
debugMessage(varDebug, "==================== FINISH ====================", varDebugToFile);
if (programStatus == null)
{
return "WS response: " + response;
}
}
return programStatus;
}
private static String debugMessage(Boolean varLevel, String varMessage, String varDebugToFile)
{
// System.out.println("I AM HERE [" + varMessage + "]");
if (varLevel.booleanValue() == true)
{
System.out.println(varMessage);
}
if (varDebugToFile.equalsIgnoreCase("true"))
{
File file = new File("debug.log");
try
{
//if file doesnt exists, then create it
if (!file.exists())
{
System.out.println("Creating a new file!");
file.createNewFile();
}
BufferedWriter bw = null;
bw = new BufferedWriter(new FileWriter(file, true));
bw.write(varMessage);
bw.newLine();
bw.flush();
bw.close();
}
catch (IOException e)
{
return programStatus;
}
}
return "OK";
}
private static HashMap generateHashMap(Integer size, String delimiter, List<String> tokens) throws Exception
{
System.out.println("\n## Generating a HashMap based on " + tokens.toString() + " ##");
String[] argumentsArray = new String[size];
HashMap hashMap = new HashMap();
System.out.println("<debug>Token size " + size.toString() + "</debug>");
for (int i = 0; i < size; i++)
{
argumentsArray[i] = tokens.get(i);
System.out.println("<debug>Token [" + i + "]: " + argumentsArray[i] + "</debug>");
String words[] = argumentsArray[i].split(delimiter);
if (words.length != 2)
continue;
System.out.println("<debug>hashMap: " + words[0] + " and " + words[1] + "</debug>");
hashMap.put(words[0], words[1]);
}
debugMessage(varDebug, "<debug>Array Size " + argumentsArray.length + "</debug>", varDebugToFile);
debugMessage(varDebug, "<debug>HashMap contains " + hashMap.size() + " key value pairs</debug>", varDebugToFile);
return hashMap;
}
private static String encryptPassword(String tempKey, String varPassword) throws Exception
{
char[] varPasswordIn = new char[0];
Integer validKeyLength = 15;
System.out.println("\n## Generating an encrypted password ##");
try
{
tempKey.charAt(validKeyLength);
byte[] thisKey = tempKey.getBytes();
System.out.println("Key to use: " + new String(thisKey));
System.out.println("Value to encrypt: " + varPassword);
varPasswordIn = Protector.encrypt(thisKey, varPassword);
System.out.println("Encrypted value: " + new String(varPasswordIn));
String varPasswordOut = Protector.decrypt(thisKey, new String(varPasswordIn));
System.out.println("Test Decrypted value: " + varPasswordOut);
}
catch(Exception e)
{
e.printStackTrace();
}
return varPasswordIn.toString();
}
public static String readProperty(Boolean varDebug, File MyFile, String varProperty) throws Exception
{
Properties props = new Properties();
String propertyValue = "";
if (varProperty.equals("SHOW_DEBUG"))
{
varDebug = true;
}
try
{
props.load(new FileInputStream(MyFile));
propertyValue = props.getProperty(varProperty);
if (propertyValue == null)
{
propertyValue = "";
}
debugMessage(varDebug, "<debug>Reading properties file for " + varProperty + ", got " + propertyValue + "</debug>", varDebugToFile);
}
catch (Exception e)
{
e.printStackTrace();
}
return propertyValue;
}
public static String readTextFile(String fullPathFilename, HashMap hashMap) throws IOException
{
System.out.println("## Preparing the SOAP document ##");
StringBuilder sb = new StringBuilder(43);
try
{
File myFile = new File(fullPathFilename);
FileReader fileReader = new FileReader(myFile);
BufferedReader reader = new BufferedReader(fileReader);
String line;
while ((line = reader.readLine()) != null)
{
sb.append(line);
}
reader.close();
}
catch (IOException e)
{
e.printStackTrace();
return ("");
}
String returnString = sb.toString();
Set set = hashMap.entrySet();
Iterator i = set.iterator();
while (i.hasNext())
{
Map.Entry me = (Map.Entry)i.next();
System.out.println("<debug>Replacing " + me.getKey().toString() + " with " + me.getValue().toString() + "</debug>");
debugMessage(varDebug, "Replacing " + me.getKey().toString() + " with " + me.getValue().toString(), varDebugToFile);
returnString = returnString.replaceAll(me.getKey().toString(), me.getValue().toString());
}
System.out.println("<debug>SOAP Document to send: " + returnString + "</debug>");
debugMessage(varDebug, "SOAP Document to send: " + returnString, varDebugToFile);
return returnString;
}
public static String writeMessage(String username, String password, String varURL, String varInputFile, String varUseSSL, String thisKeystore, String thisPassword, HashMap hashMap, byte[] document) throws Exception
{
programStatus = NEW_STATUS;
StringBuilder response = new StringBuilder(500);
String login = username + ":" + password;
String encoding = Base64.encodeString(login);
URL url = new URL(varURL);
try {
System.out.println("\n## Sending SOAP Document ##");
if ( Boolean.parseBoolean(varUseSSL) )
{
System.setProperty("javax.net.ssl.trustStore", thisKeystore);
System.setProperty("javax.net.ssl.trustStorePassword", thisPassword);
}
URLConnection connection = url.openConnection();
connection.setRequestProperty("Authorization", "Basic " + encoding);
HttpURLConnection httpConn = (HttpURLConnection)connection;
ByteArrayOutputStream outstr = new ByteArrayOutputStream();
httpConn.setRequestProperty("Content-Length", String.valueOf(document.length));
httpConn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
httpConn.setRequestMethod("POST");
httpConn.setDoOutput(true);
httpConn.setDoInput(true);
OutputStream out = httpConn.getOutputStream();
out.write(document);
out.close();
InputStreamReader ireader = new InputStreamReader(httpConn.getInputStream());
BufferedReader in = new BufferedReader(ireader);
String inputLine;
while ((inputLine = in.readLine()) != null)
{
response.append(inputLine);
}
in.close();
httpConn.disconnect();
}
catch (Exception e)
{
e.printStackTrace();
programStatus = e.toString();
return programStatus;
}
return response.toString();
}
static
{
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
{
public boolean verify(String hostname, SSLSession sslSession)
{
System.out.println("<debug>Hostname: " + hostname + "</debug>");
return true;
}
});
}
}