/* This file is part of Airsonic. Airsonic is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Airsonic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Airsonic. If not, see . Copyright 2016 (C) Airsonic Authors Based upon Subsonic, Copyright 2009 (C) Sindre Mehus */ package org.airsonic.player.controller; import org.airsonic.player.util.StringUtil; import org.apache.commons.io.IOUtils; import org.eclipse.persistence.jaxb.JAXBContext; import org.eclipse.persistence.jaxb.MarshallerProperties; import org.jdom.Attribute; import org.jdom.Document; import org.jdom.input.SAXBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.subsonic.restapi.Error; import org.subsonic.restapi.ObjectFactory; import org.subsonic.restapi.Response; import org.subsonic.restapi.ResponseStatus; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.Date; import java.util.GregorianCalendar; import static org.springframework.web.bind.ServletRequestUtils.getStringParameter; /** * @author Sindre Mehus * @version $Id$ */ public class JAXBWriter { private static final Logger LOG = LoggerFactory.getLogger(JAXBWriter.class); private final javax.xml.bind.JAXBContext jaxbContext; private final DatatypeFactory datatypeFactory; private final String restProtocolVersion; public JAXBWriter() { try { jaxbContext = JAXBContext.newInstance(Response.class); datatypeFactory = DatatypeFactory.newInstance(); restProtocolVersion = getRESTProtocolVersion(); } catch (Exception x) { throw new RuntimeException(x); } } private Marshaller createXmlMarshaller() { Marshaller marshaller = null; try { marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, StringUtil.ENCODING_UTF8); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); return marshaller; } catch (JAXBException e) { throw new RuntimeException(e); } } private Marshaller createJsonMarshaller() { try { Marshaller marshaller; marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, StringUtil.ENCODING_UTF8); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json"); marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, true); return marshaller; } catch (JAXBException e) { throw new RuntimeException(e); } } private String getRESTProtocolVersion() throws Exception { InputStream in = null; try { in = StringUtil.class.getResourceAsStream("/subsonic-rest-api.xsd"); Document document = new SAXBuilder().build(in); Attribute version = document.getRootElement().getAttribute("version"); return version.getValue(); } finally { IOUtils.closeQuietly(in); } } public String getRestProtocolVersion() { return restProtocolVersion; } public Response createResponse(boolean ok) { Response response = new ObjectFactory().createResponse(); response.setStatus(ok ? ResponseStatus.OK : ResponseStatus.FAILED); response.setVersion(restProtocolVersion); return response; } public void writeResponse(HttpServletRequest request, HttpServletResponse httpResponse, Response jaxbResponse) { String format = getStringParameter(request, "f", "xml"); String jsonpCallback = request.getParameter("callback"); boolean json = "json".equals(format); boolean jsonp = "jsonp".equals(format) && jsonpCallback != null; Marshaller marshaller; if (json) { marshaller = createJsonMarshaller(); httpResponse.setContentType("application/json"); } else if (jsonp) { marshaller = createJsonMarshaller(); httpResponse.setContentType("text/javascript"); } else { marshaller = createXmlMarshaller(); httpResponse.setContentType("text/xml"); } httpResponse.setCharacterEncoding(StringUtil.ENCODING_UTF8); try { StringWriter writer = new StringWriter(); if (jsonp) { writer.append(jsonpCallback).append('('); } marshaller.marshal(new ObjectFactory().createSubsonicResponse(jaxbResponse), writer); if (jsonp) { writer.append(");"); } httpResponse.getWriter().append(writer.getBuffer()); } catch (JAXBException | IOException x) { LOG.error("Failed to marshal JAXB", x); throw new RuntimeException(x); } } public void writeErrorResponse(HttpServletRequest request, HttpServletResponse response, RESTController.ErrorCode code, String message) throws Exception { Response res = createResponse(false); Error error = new Error(); res.setError(error); error.setCode(code.getCode()); error.setMessage(message); writeResponse(request, response, res); } public XMLGregorianCalendar convertDate(Date date) { if (date == null) { return null; } GregorianCalendar c = new GregorianCalendar(); c.setTime(date); return datatypeFactory.newXMLGregorianCalendar(c).normalize(); } }