/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.web; import java.io.CharArrayReader; import java.io.StringReader; import java.io.StringWriter; import java.util.Locale; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.commons.lang.StringUtils; import org.fudgemsg.FudgeContext; import org.fudgemsg.FudgeMsg; import org.fudgemsg.FudgeMsgEnvelope; import org.fudgemsg.mapping.FudgeDeserializer; import org.fudgemsg.wire.FudgeMsgReader; import org.fudgemsg.wire.FudgeMsgWriter; import org.fudgemsg.wire.xml.FudgeXMLStreamReader; import org.fudgemsg.wire.xml.FudgeXMLStreamWriter; import org.joda.beans.Bean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.util.JodaBeanSerialization; import com.opengamma.util.fudgemsg.OpenGammaFudgeContext; import com.opengamma.util.paging.PagingRequest; import com.opengamma.web.json.FudgeMsgJSONReader; /** * Abstract base class for RESTful resources intended for websites. * <p> * Websites and web-services are related but different RESTful elements. * This is because a website needs to bend the RESTful rules in order to be usable. */ public abstract class AbstractWebResource { /** * Logger. */ private static final Logger s_logger = LoggerFactory.getLogger(AbstractWebResource.class); private static final int INDENTATION_SIZE = 4; /** * The Fudge context. */ private final FudgeContext _fudgeContext = OpenGammaFudgeContext.getInstance(); /** * Creates the resource, used by the root resource. */ protected AbstractWebResource() { } //------------------------------------------------------------------------- /** * Builds the paging request. * <p> * This method is lenient, applying sensible default values. * * @param pgIdx the paging first-item index, null if not input * @param pgNum the paging page, null if not input * @param pgSze the paging size, null if not input * @return the paging request, not null */ protected PagingRequest buildPagingRequest(Integer pgIdx, Integer pgNum, Integer pgSze) { int size = (pgSze != null ? pgSze : PagingRequest.DEFAULT_PAGING_SIZE); if (pgIdx != null) { return PagingRequest.ofIndex(pgIdx, size); } else if (pgNum != null) { return PagingRequest.ofPage(pgNum, size); } else { return PagingRequest.ofPage(1, size); } } /** * Builds the sort order. * <p> * This method is lenient, returning the default in case of error. * * @param <T> the sort order type * @param order the sort order, null or empty returns default * @param defaultOrder the default order, not null * @return the sort order, not null */ protected <T extends Enum<T>> T buildSortOrder(String order, T defaultOrder) { if (StringUtils.isEmpty(order)) { return defaultOrder; } String parsedOrder = order.toUpperCase(Locale.ENGLISH); if (parsedOrder.endsWith(" ASC")) { parsedOrder = StringUtils.replace(parsedOrder, " ASC", "_ASC"); } else if (parsedOrder.endsWith(" DESC")) { parsedOrder = StringUtils.replace(parsedOrder, " DESC", "_DESC"); } else if (parsedOrder.endsWith("_ASC") == false && parsedOrder.endsWith("_DESC") == false) { parsedOrder = parsedOrder + "_ASC"; } try { Class<T> cls = defaultOrder.getDeclaringClass(); return Enum.valueOf(cls, parsedOrder); } catch (IllegalArgumentException ex) { return defaultOrder; } } /** * Utility method to convert XML to configuration object. * * @param <T> the type to parse to * @param xml the configuration xml, not null * @param type the type to parse to, not null * @return the configuration object */ @SuppressWarnings("unchecked") protected <T> T parseXML(String xml, Class<T> type) { if (xml.contains("<fudgeEnvelope")) { return (T) parseXML(xml); } else { return JodaBeanSerialization.deserializer().xmlReader().read(xml, type); } } /** * Utility method to convert XML to configuration object * @param xml the configuration xml * @return the configuration object */ protected Object parseXML(String xml) { final CharArrayReader car = new CharArrayReader(xml.toCharArray()); @SuppressWarnings("resource") final FudgeMsgReader fmr = new FudgeMsgReader(new FudgeXMLStreamReader(getFudgeContext(), car)); final FudgeMsg message = fmr.nextMessage(); return getFudgeContext().fromFudgeMsg(message); } protected String createBeanXML(Object obj) { if (obj instanceof Bean) { try { // NOTE jim 8-Jan-2014 -- changed last param from false to true so bean type is set. Not necessary for UI, but enables easier parsing if cut and pasted elsewhere. return JodaBeanSerialization.serializer(true).xmlWriter().write((Bean) obj, true); } catch (RuntimeException ex) { s_logger.warn("Error serialising bean to XML with JodaBean serializer", ex); return createXML(obj); } } return createXML(obj); } protected String createXML(Object obj) { // get xml and pretty print it FudgeMsgEnvelope msg = getFudgeContext().toFudgeMsg(obj); s_logger.debug("{} converted to fudge {}", obj, msg); StringWriter buf = new StringWriter(1024); @SuppressWarnings("resource") FudgeMsgWriter writer = new FudgeMsgWriter(new FudgeXMLStreamWriter(getFudgeContext(), buf)); writer.writeMessageEnvelope(msg); s_logger.debug("{} converted to xmk {}", obj, buf.toString()); try { return prettyXML(buf.toString(), INDENTATION_SIZE); } catch (Exception ex) { throw new IllegalStateException(ex); } } protected String prettyXML(String input, int indent) throws TransformerException { Source xmlInput = new StreamSource(new StringReader(input)); StreamResult xmlOutput = new StreamResult(new StringWriter()); TransformerFactory transformerFactory = TransformerFactory.newInstance(); try { transformerFactory.setAttribute("indent-number", indent); } catch (IllegalArgumentException e) { //ignore } Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.transform(xmlInput, xmlOutput); return xmlOutput.getWriter().toString(); } /** * Converts JSON to configuration object * * @param json the config document in JSON * @return the configuration object */ protected Object parseJSON(String json) { s_logger.debug("converting JSON to java: " + json); FudgeMsgJSONReader fudgeJSONReader = new FudgeMsgJSONReader(getFudgeContext(), new StringReader(json)); FudgeMsg fudgeMsg = fudgeJSONReader.readMessage(); s_logger.debug("converted FudgeMsg: " + fudgeMsg); return new FudgeDeserializer(getFudgeContext()).fudgeMsgToObject(fudgeMsg); } /** * Gets the fudgeContext. * @return the fudgeContext */ public FudgeContext getFudgeContext() { return _fudgeContext; } }