com.hextremelabs.ussd.internal.Internal Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lib-ussd Show documentation
Show all versions of lib-ussd Show documentation
A lightweight USSD application framework
The newest version!
package com.hextremelabs.ussd.internal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Priority;
import javax.enterprise.concurrent.ManagedScheduledExecutorService;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.util.Nonbinding;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InterceptorBinding;
import javax.interceptor.InvocationContext;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.NoResultException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.*;
import java.util.concurrent.*;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.util.concurrent.TimeUnit.MINUTES;
/**
* This is a very shameful attempt at eliminating a dependency. Look away there is nothing to see here.
*
* @author Sayo Oladeji
*/
public class Internal {
private Internal() {
}
public static Date currentDate() {
return new Date();
}
private static long diffFromNow(Date date) {
return absoluteDistance(date, currentDate());
}
private static long absoluteDistance(Date start, Date end) {
return Math.abs(start.getTime() - end.getTime());
}
public static abstract class Pair implements Serializable {
protected static final long serialVersionUID = 1L;
public static ImmutablePair newImmutablePairOf(final K key, final V value) {
return ImmutablePair.of(key, value);
}
public static MutablePair newMutablePairOf(final K key, final V value) {
return MutablePair.of(key, value);
}
public abstract K getKey();
public abstract void setKey(K key);
public abstract V getValue();
public abstract void setValue(V value);
}
public static class MutablePair extends Pair implements Serializable {
private K key;
private V value;
public MutablePair() {
}
private MutablePair(final K key, final V value) {
this.key = key;
this.value = value;
}
static MutablePair of(final K key, final V value) {
return new MutablePair(key, value);
}
@Override
@XmlElement(name = "key")
public K getKey() {
return this.key;
}
@Override
public void setKey(K key) {
this.key = key;
}
@Override
@XmlElement(name = "value")
public V getValue() {
return this.value;
}
@Override
public void setValue(V value) {
this.value = value;
}
@Override
public String toString() {
return "MutablePair{" + "key=" + key + ", value=" + value + '}';
}
}
public static final class ImmutablePair extends Pair {
private final K KEY;
private final V VALUE;
private ImmutablePair(final K key, final V value) {
this.KEY = key;
this.VALUE = value;
}
public static ImmutablePair of(final K key, final V value) {
return new ImmutablePair(key, value);
}
@Override
public K getKey() {
return KEY;
}
@Override
public void setKey(K key) {
throw new UnsupportedOperationException("Cannot modify the key of an immutable pair!");
}
@Override
public V getValue() {
return VALUE;
}
@Override
public void setValue(V value) {
throw new UnsupportedOperationException("Cannot modify the value of an immutable pair!");
}
@Override
public String toString() {
return "ImmutablePair{" + "KEY=" + KEY + ", VALUE=" + VALUE + '}';
}
}
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, METHOD})
public static @interface Log {
@Nonbinding
public boolean secure() default false;
}
@Log
@Interceptor
@Priority(Interceptor.Priority.APPLICATION + 5)
@Dependent
public static class TraceLogger implements Serializable {
private static final long serialVersionUID = 1L;
// TODO(oluwasayo): Log this in a more sophisticated datastore like Reddis.
private final Logger L = LoggerFactory.getLogger(TraceLogger.class);
private static String join(Map argMap) {
if (argMap == null) {
return "";
}
StringBuilder sb = new StringBuilder();
argMap.keySet().stream().forEach((entry) -> {
sb.append(entry).append(" = ").append(argMap.get(entry)).append(", ");
});
if (sb.length() > 1) {
sb.delete(sb.length() - 2, sb.length());
}
return sb.toString();
}
/**
* Use at your own risk. This may kill your kitten or blow up your Playstation.
*
* @param args
* @return
*/
public static Map build(Object... args) {
Map result = new HashMap<>();
if (args.length == 0) {
return result;
}
if ((args.length & 1) != 0) { // Odd number of elements in args.
throw new RuntimeException("Malformed log data! Argument length should be even!");
}
for (int i = 0; i < args.length; i += 2) {
result.put(args[i].toString(), args[i + 1]);
}
return result;
}
@AroundInvoke
public Object logAroundInvoke(InvocationContext ic) throws Exception {
Class clazz = ic.getMethod().getDeclaringClass();
Log log = ic.getMethod().getAnnotation(Log.class);
if (DataHelper.hasBlank(log)) {
log = (Log) clazz.getAnnotation(Log.class);
}
boolean secure = (!DataHelper.hasBlank(log) && log.secure());
Object[] params = new Object[]{clazz.getName(), ic.getMethod().getName(),
Joiner.on(", ").useForNull("").join(ic.getParameters())};
L.debug("Entering - {}#{} : " + (secure ? "*****" : "{}"), ic.getMethod().getDeclaringClass().getSimpleName(),
ic.getMethod().getName(), params);
Object result = ic.proceed();
params[2] = result;
L.debug("Exiting - {}#{} : " + (secure ? "*****" : "{}"), ic.getMethod().getDeclaringClass().getSimpleName(),
ic.getMethod().getName(), result);
return result;
}
}
public static class DataHelper {
private static final Logger L = LoggerFactory.getLogger(DataHelper.class);
private static final Map JAXB_CONTEXT_MAP = new HashMap<>();
private DataHelper() {
}
/**
* Inspects input for possible null references, or emptiness (if an element is a {@link String}). Because we are
* dealing primarily with SOAP, a {@link String} "{@code ?}" is also considered empty. Empty {@link Collection}s and
* {@link Map}s are also considered specially.
*/
public static boolean hasBlank(Object... inputs) {
for (Object obj : inputs) {
if (obj == null) {
return true;
}
if (obj instanceof String) {
if (((String) obj).isEmpty() || "?".equals(obj)) {
return true;
}
}
if (obj instanceof Collection) {
if (((Collection) obj).isEmpty()) {
return true;
}
}
if (obj instanceof Map) {
if (((Map) obj).isEmpty()) {
return true;
}
}
// Arrays are difficult to deal with so I'm not performing a special check on them.
}
return false;
}
public static String marshalXml(T object) {
return marshalXml(object, (XmlOptions) null);
}
public static String marshalXml(T object, XmlOptions options) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
marshalXml(object, outputStream, options);
try {
return outputStream.toString("UTF-8");
} catch (UnsupportedEncodingException ex) {
L.error("Error converting byte stream to String. UTF-8 encoding not supported.", ex);
return null;
}
}
public static void marshalXml(T object, OutputStream outputStream) {
marshalXml(object, outputStream, null);
}
public static void marshalXml(T object, OutputStream outputStream, XmlOptions options) {
try {
JAXBContext jaxbContext = JAXB_CONTEXT_MAP.get(object.getClass());
if (jaxbContext == null) {
jaxbContext = JAXBContext.newInstance(object.getClass());
JAXB_CONTEXT_MAP.put(object.getClass(), jaxbContext);
}
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
if (options != null) {
if (options.formattedOutput) {
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, options.formattedOutput);
}
if (options.fragment) {
jaxbMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, options.fragment);
}
if (options.noNamespaceSchemaLocation != null) {
jaxbMarshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, options.noNamespaceSchemaLocation);
}
if (options.schemaLocation != null) {
jaxbMarshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, options.schemaLocation);
}
jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, options.encoding);
}
jaxbMarshaller.marshal(object, outputStream);
} catch (JAXBException exc) {
L.error("An error occured while marshalling an object of type: " + object.getClass().getSimpleName(), exc);
}
}
public static T unmarshalXml(Class type, String xml) {
if (xml == null) {
return null;
}
T result = null;
try {
return unmarshalXml(type, new ByteArrayInputStream(xml.getBytes("UTF-8")));
} catch (Exception exc) {
L.warn("Unable to unmarshal the xml. Xml = {}", xml, exc);
}
return result;
}
public static T unmarshalXml(Class type, InputStream input) {
if (input == null) {
return null;
}
T result = null;
try {
JAXBContext jaxbContext = JAXB_CONTEXT_MAP.get(type);
if (jaxbContext == null) {
jaxbContext = JAXBContext.newInstance(type);
JAXB_CONTEXT_MAP.put(type, jaxbContext);
}
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
result = (T) unmarshaller.unmarshal(input);
} catch (Exception exc) {
L.warn("Unable to unmarshal the xml stream.", exc);
}
return result;
}
public static final class XmlOptions {
private boolean fragment = false;
private boolean formattedOutput = false;
private String encoding = "UTF-8";
private String schemaLocation = null;
private String noNamespaceSchemaLocation = null;
public XmlOptions fragment(boolean fragment) {
this.fragment = fragment;
return this;
}
public XmlOptions formattedOutput(boolean formattedOutput) {
this.formattedOutput = formattedOutput;
return this;
}
public XmlOptions encoding(String encoding) {
this.encoding = encoding;
return this;
}
public XmlOptions schemaLocation(String schemaLocation) {
this.schemaLocation = schemaLocation;
return this;
}
public XmlOptions noNamespaceSchemaLocation(String noNamespaceSchemaLocation) {
this.noNamespaceSchemaLocation = noNamespaceSchemaLocation;
return this;
}
}
}
public static class Joiner {
private final String separator;
private String nullToken;
private boolean skipNull;
private Joiner(String separator) {
this.separator = separator;
}
public static Joiner on(String separator) {
return new Joiner(separator);
}
public static Joiner blankJoiner() {
return on("");
}
public Joiner useForNull(String nullToken) {
this.nullToken = nullToken;
return this;
}
public Joiner skipNull() {
this.skipNull = true;
return this;
}
public String join(final Collection