You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
6.6 KiB
187 lines
6.6 KiB
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
Copyright 2016 (C) Airsonic Authors
|
|
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
|
|
*/
|
|
package org.airsonic.player.controller;
|
|
|
|
import org.airsonic.player.util.FileUtil;
|
|
import org.airsonic.player.util.StringUtil;
|
|
import org.eclipse.persistence.jaxb.JAXBContext;
|
|
import org.eclipse.persistence.jaxb.MarshallerProperties;
|
|
import org.jdom2.Attribute;
|
|
import org.jdom2.Document;
|
|
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.Calendar;
|
|
import java.util.Date;
|
|
import java.util.GregorianCalendar;
|
|
|
|
import static org.airsonic.player.util.XMLUtil.createSAXBuilder;
|
|
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 = createSAXBuilder().build(in);
|
|
Attribute version = document.getRootElement().getAttribute("version");
|
|
return version.getValue();
|
|
} finally {
|
|
FileUtil.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,
|
|
SubsonicRESTController.ErrorCode code, String message) {
|
|
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();
|
|
}
|
|
|
|
public XMLGregorianCalendar convertCalendar(Calendar calendar) {
|
|
if (calendar == null) {
|
|
return null;
|
|
}
|
|
|
|
return datatypeFactory.newXMLGregorianCalendar((GregorianCalendar)calendar).normalize();
|
|
}
|
|
}
|
|
|