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}