001package co.codewizards.cloudstore.core.dto.jaxb;
002
003import static co.codewizards.cloudstore.core.io.StreamUtil.*;
004import static java.util.Objects.*;
005
006import java.io.BufferedInputStream;
007import java.io.BufferedOutputStream;
008import java.io.IOException;
009import java.io.InputStream;
010import java.io.OutputStream;
011import java.lang.reflect.ParameterizedType;
012import java.lang.reflect.Type;
013import java.util.zip.GZIPInputStream;
014import java.util.zip.GZIPOutputStream;
015
016import javax.xml.bind.JAXBException;
017import javax.xml.bind.Marshaller;
018import javax.xml.bind.Unmarshaller;
019
020import co.codewizards.cloudstore.core.io.ByteArrayInputStream;
021import co.codewizards.cloudstore.core.io.ByteArrayOutputStream;
022import co.codewizards.cloudstore.core.io.NoCloseInputStream;
023import co.codewizards.cloudstore.core.io.NoCloseOutputStream;
024import co.codewizards.cloudstore.core.oio.File;
025
026public abstract class DtoIo <D> {
027
028        private final Class<D> dtoClass;
029
030        private Marshaller marshaller;
031        private Unmarshaller unmarshaller;
032
033        protected DtoIo() {
034                final ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
035                final Type[] actualTypeArguments = superclass.getActualTypeArguments();
036                if (actualTypeArguments == null || actualTypeArguments.length < 1)
037                        throw new IllegalStateException("Subclass " + getClass().getName() + " has no generic type argument!");
038
039                @SuppressWarnings("unchecked")
040                final Class<D> c = (Class<D>) actualTypeArguments[0];
041                this.dtoClass = c;
042                if (this.dtoClass == null)
043                        throw new IllegalStateException("Subclass " + getClass().getName() + " has no generic type argument!");
044        }
045
046        public byte[] serialize(final D dto) {
047                final ByteArrayOutputStream out = new ByteArrayOutputStream();
048                serialize(dto, out);
049                return out.toByteArray();
050        }
051
052        public void serialize(final D dto, final OutputStream out) {
053                requireNonNull(dto, "dto");
054                requireNonNull(out, "out");
055                try {
056                        getMarshaller().marshal(dto, new NoCloseOutputStream(out));
057                } catch (final JAXBException e) {
058                        throw new RuntimeException(e);
059                }
060        }
061
062        public byte[] serializeWithGz(final D dto) {
063                final ByteArrayOutputStream out = new ByteArrayOutputStream();
064                serializeWithGz(dto, out);
065                return out.toByteArray();
066        }
067
068        public void serializeWithGz(final D dto, final OutputStream out) {
069                requireNonNull(dto, "dto");
070                requireNonNull(out, "out");
071                try {
072                        try (GZIPOutputStream gzOut = new GZIPOutputStream(new NoCloseOutputStream(out));) {
073                                getMarshaller().marshal(dto, gzOut);
074                        }
075                } catch (JAXBException | IOException e) {
076                        throw new RuntimeException(e);
077                }
078        }
079
080        public void serialize(final D dto, final File file) {
081                requireNonNull(dto, "dto");
082                requireNonNull(file, "file");
083                try {
084                        // Even though https://github.com/cloudstore/cloudstore/issues/31 seems to affect only unmarshal(File),
085                        // we manage the OutputStream ourself, as well.
086                        try (final OutputStream out = new BufferedOutputStream(castStream(file.createOutputStream()))) {
087                                getMarshaller().marshal(dto, out);
088                        }
089                } catch (JAXBException | IOException e) {
090                        throw new RuntimeException("Writing file '" + file.getAbsolutePath() + "' failed: " + e, e);
091                }
092        }
093
094        public void serializeWithGz(final D dto, final File file) {
095                requireNonNull(dto, "dto");
096                requireNonNull(file, "file");
097                try {
098                        // Even though https://github.com/cloudstore/cloudstore/issues/31 seems to affect only unmarshal(File),
099                        // we manage the OutputStream ourself, as well.
100                        try (final OutputStream out = new BufferedOutputStream(castStream(file.createOutputStream()))) {
101                                try (GZIPOutputStream gzOut = new GZIPOutputStream(out);) {
102                                        getMarshaller().marshal(dto, gzOut);
103                                }
104                        }
105                } catch (JAXBException | IOException e) {
106                        throw new RuntimeException("Writing file '" + file.getAbsolutePath() + "' failed: " + e, e);
107                }
108        }
109
110        public D deserialize(final byte[] in) {
111                requireNonNull(in, "in");
112                return deserialize(new ByteArrayInputStream(in));
113        }
114
115        public D deserialize(final InputStream in) {
116                requireNonNull(in, "in");
117                try {
118                        return dtoClass.cast(getUnmarshaller().unmarshal(new NoCloseInputStream(in)));
119                } catch (final JAXBException e) {
120                        throw new RuntimeException(e);
121                }
122        }
123
124        public D deserializeWithGz(final byte[] in) {
125                requireNonNull(in, "in");
126                return deserializeWithGz(new ByteArrayInputStream(in));
127        }
128
129        public D deserializeWithGz(final InputStream in) {
130                requireNonNull(in, "in");
131                try {
132                        try (GZIPInputStream gzIn = new GZIPInputStream(new NoCloseInputStream(in));) {
133                                return dtoClass.cast(getUnmarshaller().unmarshal(gzIn));
134                        }
135                } catch (JAXBException | IOException e) {
136                        throw new RuntimeException(e);
137                }
138        }
139
140        public D deserialize(final File file) {
141                requireNonNull(file, "file");
142                try {
143                        // Because of https://github.com/cloudstore/cloudstore/issues/31 we do not use unmarshal(File), anymore.
144                        try (final InputStream in = new BufferedInputStream(castStream(file.createInputStream()))) {
145                                return dtoClass.cast(getUnmarshaller().unmarshal(in));
146                        }
147                } catch (JAXBException | IOException e) {
148                        throw new RuntimeException("Reading file '" + file.getAbsolutePath() + "' failed: " + e, e);
149                }
150        }
151
152        public D deserializeWithGz(final File file) {
153                requireNonNull(file, "file");
154                try {
155                        // Because of https://github.com/cloudstore/cloudstore/issues/31 we do not use unmarshal(File), anymore.
156                        try (final InputStream in = new BufferedInputStream(castStream(file.createInputStream()))) {
157                                try (GZIPInputStream gzIn = new GZIPInputStream(in);) {
158                                        return dtoClass.cast(getUnmarshaller().unmarshal(gzIn));
159                                }
160                        }
161                } catch (JAXBException | IOException e) {
162                        throw new RuntimeException("Reading file '" + file.getAbsolutePath() + "' failed: " + e, e);
163                }
164        }
165
166        private Marshaller getMarshaller() {
167                if (marshaller == null) {
168                        try {
169                                marshaller = CloudStoreJaxbContext.getJaxbContext().createMarshaller();
170                        } catch (final JAXBException e) {
171                                throw new RuntimeException(e);
172                        }
173                }
174                return marshaller;
175        }
176
177        private Unmarshaller getUnmarshaller() {
178                if (unmarshaller == null) {
179                        try {
180                                unmarshaller = CloudStoreJaxbContext.getJaxbContext().createUnmarshaller();
181                        } catch (final JAXBException e) {
182                                throw new RuntimeException(e);
183                        }
184                }
185                return unmarshaller;
186        }
187}