Add logging.
[sonitus.git] / src / main / java / net / pterodactylus / sonitus / data / filter / PipelineFilter.java
1 /*
2  * Sonitus - PipelineFilter.java - Copyright © 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sonitus.data.filter;
19
20 import java.io.IOException;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.logging.Logger;
26
27 import net.pterodactylus.sonitus.data.AbstractFilter;
28 import net.pterodactylus.sonitus.data.DataPacket;
29 import net.pterodactylus.sonitus.data.Filter;
30 import net.pterodactylus.sonitus.data.Metadata;
31 import net.pterodactylus.sonitus.data.Pipeline.Connection;
32
33 import com.google.common.collect.Lists;
34 import com.google.common.collect.Maps;
35
36 /**
37  * {@link Filter} that combines several filters into one.
38  *
39  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
40  */
41 public class PipelineFilter extends AbstractFilter implements Filter {
42
43         /** The logger. */
44         private static final Logger logger = Logger.getLogger(PipelineFilter.class.getName());
45
46         /** The first filter. */
47         private final Filter source;
48
49         /** All following filters. */
50         private final List<Filter> filters = Lists.newArrayList();
51
52         /** The last filter (for convenience). */
53         private final Filter lastFilter;
54
55         /** The connections for each filter. */
56         private final Map<Filter, Connection> filterConnections = Maps.newHashMap();
57
58         /**
59          * Creates a new pipeline filter.
60          *
61          * @param name
62          *              The name of the filter
63          * @param source
64          *              The first source of the filter
65          * @param filters
66          *              All other filters in correct order
67          */
68         private PipelineFilter(String name, Filter source, Collection<Filter> filters) {
69                 super(name);
70                 this.source = source;
71                 this.filters.addAll(filters);
72                 this.lastFilter = this.filters.get(filters.size() - 1);
73         }
74
75         //
76         // FILTER METHODS
77         //
78
79         @Override
80         public Metadata metadata() {
81                 return lastFilter.metadata();
82         }
83
84         @Override
85         public void open(Metadata metadata) throws IOException {
86                 /* open the source and all filters in the correct order. */
87                 source.open(metadata);
88                 Metadata currentMetadata = source.metadata();
89                 Filter currentSource = source;
90                 for (Filter filter : filters) {
91                         filter.open(currentMetadata);
92                         currentMetadata = filter.metadata();
93                         Connection connection = new Connection(currentSource, Arrays.asList(filter));
94                         filterConnections.put(filter, connection);
95                         String threadName = String.format("%s → %s", connection.source().name(), filter.name());
96                         logger.info(String.format("Starting Thread: %s.", threadName));
97                         new Thread(connection, threadName).start();
98                         currentSource = filter;
99                 }
100                 metadataUpdated(currentMetadata);
101         }
102
103         @Override
104         public DataPacket get(int bufferSize) throws IOException {
105                 if (filterConnections.get(lastFilter).ioException().isPresent()) {
106                         logger.info(String.format("Rethrowing exception from %s: %s", lastFilter.name(), filterConnections.get(lastFilter).ioException().get().getMessage()));
107                         throw filterConnections.get(lastFilter).ioException().get();
108                 }
109                 logger.info(String.format("Requesting %d bytes from %s...", bufferSize, lastFilter.name()));
110                 return lastFilter.get(bufferSize);
111         }
112
113         @Override
114         public void process(DataPacket dataPacket) throws IOException {
115                 source.process(dataPacket);
116         }
117
118         //
119         // STATIC METHODS
120         //
121
122         /**
123          * Returns a builder that can create pipeline filters.
124          *
125          * @param source
126          *              The source filter of the pipeline
127          * @return The pipeline filter builder
128          */
129         public static Builder builder(Filter source) {
130                 return new Builder(source);
131         }
132
133         /**
134          * Builder for a {@link PipelineFilter}.
135          *
136          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
137          */
138         public static class Builder {
139
140                 /** The source of the pipeline. */
141                 private final Filter source;
142
143                 /** All other filters of the pipeline. */
144                 private final List<Filter> filters = Lists.newArrayList();
145
146                 /**
147                  * Creates a new builder with the given source.
148                  *
149                  * @param source
150                  *              The source of the pipeline filter
151                  */
152                 private Builder(Filter source) {
153                         this.source = source;
154                 }
155
156                 /**
157                  * Connects the given filter at the end of the pipeline being build.
158                  *
159                  * @param filter
160                  *              The filter to add
161                  * @return This builder
162                  */
163                 public Builder to(Filter filter) {
164                         filters.add(filter);
165                         return this;
166                 }
167
168                 /**
169                  * Builds a filter using the given name. If no filters other than the source
170                  * have been added, only the source filter is being returned.
171                  *
172                  * @param name
173                  *              The name of the pipeline filter to build
174                  * @return The created filter, or the source filter
175                  */
176                 public Filter build(String name) {
177                         if (filters.isEmpty()) {
178                                 return source;
179                         }
180                         return new PipelineFilter(name, source, filters);
181                 }
182
183         }
184
185 }