001/* 002 * Syncany, www.syncany.org 003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package org.syncany.operations.daemon.messages.api; 019 020import java.io.StringWriter; 021import java.util.logging.Level; 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025import org.simpleframework.xml.Serializer; 026import org.simpleframework.xml.convert.Converter; 027import org.simpleframework.xml.convert.Registry; 028import org.simpleframework.xml.convert.RegistryStrategy; 029import org.simpleframework.xml.core.Persister; 030import org.simpleframework.xml.stream.InputNode; 031import org.simpleframework.xml.stream.OutputNode; 032import org.syncany.database.FileContent; 033import org.syncany.database.PartialFileHistory; 034import org.syncany.database.VectorClock; 035 036/** 037 * Factory class to serialize and deserialize {@link Message}s from/to 038 * XML (via SimpleXML). 039 * 040 * @see <a href="http://simple.sourceforge.net/">Simple framework</a> 041 * @author Christian Roth (christian.roth@port17.de) 042 */ 043public abstract class XmlMessageFactory extends MessageFactory { 044 private static final Pattern MESSAGE_TYPE_PATTERN = Pattern.compile("\\<([^\\/>\\s]+)"); 045 private static final int MESSAGE_TYPE_PATTERN_GROUP = 1; 046 047 private static final Serializer serializer; 048 049 static { 050 try { 051 Registry registry = new Registry(); 052 053 registry.bind(PartialFileHistory.FileHistoryId.class, new FileHistoryIdConverter()); 054 registry.bind(FileContent.FileChecksum.class, new FileChecksumConverter()); 055 registry.bind(VectorClock.class, new VectorClockConverter()); 056 057 serializer = new Persister(new RegistryStrategy(registry)); 058 } 059 catch (Exception e) { 060 throw new RuntimeException(e); 061 } 062 } 063 064 public static Response toResponse(String responseMessageXml) throws Exception { 065 Message responseMessage = toMessage(responseMessageXml); 066 067 if (!(responseMessage instanceof Response)) { 068 throw new Exception("Invalid class: Message is not a response type: " + responseMessage.getClass()); 069 } 070 071 return (Response) responseMessage; 072 } 073 074 public static Request toRequest(String requestMessageXml) throws Exception { 075 Message requestMessage = toMessage(requestMessageXml); 076 077 if (!(requestMessage instanceof Request)) { 078 throw new Exception("Invalid class: Message is not a request type: " + requestMessage.getClass()); 079 } 080 081 return (Request) requestMessage; 082 } 083 084 public static Message toMessage(String messageStr) throws Exception { 085 String messageType = getMessageType(messageStr); 086 Class<? extends Message> messageClass = getMessageClass(messageType); 087 088 Message message = serializer.read(messageClass, messageStr); 089 logger.log(Level.INFO, "Message created: " + message); 090 091 return message; 092 } 093 094 public static String toXml(Message response) throws Exception { 095 StringWriter messageWriter = new StringWriter(); 096 serializer.write(response, messageWriter); 097 098 return messageWriter.toString(); 099 } 100 101 private static String getMessageType(String message) throws Exception { 102 Matcher messageTypeMatcher = MESSAGE_TYPE_PATTERN.matcher(message); 103 104 if (messageTypeMatcher.find()) { 105 return messageTypeMatcher.group(MESSAGE_TYPE_PATTERN_GROUP); 106 } 107 else { 108 throw new Exception("Cannot find type of message. Invalid XML: " + message); 109 } 110 } 111 112 private static class FileHistoryIdConverter implements Converter<PartialFileHistory.FileHistoryId> { 113 @Override 114 public PartialFileHistory.FileHistoryId read(InputNode node) throws Exception { 115 return PartialFileHistory.FileHistoryId.parseFileId(node.getValue()); 116 } 117 118 @Override 119 public void write(OutputNode node, PartialFileHistory.FileHistoryId value) throws Exception { 120 node.setValue(value.toString()); 121 } 122 } 123 124 private static class FileChecksumConverter implements Converter<FileContent.FileChecksum> { 125 @Override 126 public FileContent.FileChecksum read(InputNode node) throws Exception { 127 return FileContent.FileChecksum.parseFileChecksum(node.getValue()); 128 } 129 130 @Override 131 public void write(OutputNode node, FileContent.FileChecksum value) throws Exception { 132 node.setValue(value.toString()); 133 } 134 } 135 136 private static class VectorClockConverter implements Converter<VectorClock> { 137 @Override 138 public VectorClock read(InputNode node) throws Exception { 139 return VectorClock.parseVectorClock(node.getValue()); 140 } 141 142 @Override 143 public void write(OutputNode node, VectorClock value) throws Exception { 144 node.setValue(value.toString()); 145 } 146 } 147}