1 /*****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one *
3 * or more contributor license agreements. See the NOTICE file *
4 * distributed with this work for additional information *
5 * regarding copyright ownership. The ASF licenses this file *
6 * to you under the Apache License, Version 2.0 (the *
7 * "License"); you may not use this file except in compliance *
8 * with the License. You may obtain a copy of the License at *
9 * *
10 * http://www.apache.org/licenses/LICENSE-2.0 *
11 * *
12 * Unless required by applicable law or agreed to in writing, *
13 * software distributed under the License is distributed on an *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15 * KIND, either express or implied. See the License for the *
16 * specific language governing permissions and limitations *
17 * under the License. *
18 ****************************************************************/
19
20 package org.apache.james.nntpserver.repository;
21
22 import org.apache.avalon.framework.logger.AbstractLogEnabled;
23 import org.apache.james.nntpserver.DateSinceFileFilter;
24 import org.apache.james.util.io.AndFileFilter;
25 import org.apache.james.util.io.ExtensionFileFilter;
26 import org.apache.james.util.io.IOUtil;
27 import org.apache.james.util.io.InvertedFileFilter;
28
29 import java.io.File;
30 import java.io.FileOutputStream;
31 import java.io.InputStream;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Date;
35 import java.util.Iterator;
36 import java.util.List;
37
38 /***
39 * Group is represented by a directory.
40 * Articles are stored in files with the name of file == article number
41 *
42 */
43 class NNTPGroupImpl extends AbstractLogEnabled implements NNTPGroup {
44
45 /***
46 * The directory to which this group maps.
47 */
48 private final File root;
49
50 /***
51 * The last article number in the group
52 */
53 private int lastArticle;
54
55 /***
56 * The last article number in the group
57 */
58 private int firstArticle;
59
60 /***
61 * The number of articles in the group.
62 */
63 private int numOfArticles;
64
65 /***
66 * Whether the first, last, and total number of articles in the
67 * group have been read from disk.
68 * An instance may collect range info once. This involves disk I/O
69 */
70 private boolean articleRangeInfoCollected = false;
71
72 /***
73 * The sole constructor for this particular NNTPGroupImpl.
74 *
75 * @param root the directory containing the articles
76 */
77 NNTPGroupImpl(File root) {
78 this.root = root;
79 }
80
81 /***
82 * @see org.apache.james.nntpserver.NNTPGroup#getName()
83 */
84 public String getName() {
85 return root.getName();
86 }
87
88 /***
89 * @see org.apache.james.nntpserver.NNTPGroup#getDescription()
90 */
91 public String getDescription() {
92 return getName();
93 }
94
95 /***
96 * @see org.apache.james.nntpserver.NNTPGroup#isPostAllowed()
97 */
98 public boolean isPostAllowed() {
99 return true;
100 }
101
102 /***
103 * Generates the first, last, and number of articles from the
104 * information in the group directory.
105 */
106 private void collectArticleRangeInfo() {
107 if ( articleRangeInfoCollected ) {
108 return;
109 }
110 String[] list = root.list();
111 int first = -1;
112 int last = -1;
113 for ( int i = 0 ; i < list.length ; i++ ) {
114 int num = Integer.parseInt(list[i]);
115 if ( first == -1 || num < first ) {
116 first = num;
117 }
118 if ( num > last ) {
119 last = num;
120 }
121 }
122 numOfArticles = list.length;
123 firstArticle = Math.max(first,0);
124 lastArticle = Math.max(last,0);
125 articleRangeInfoCollected = true;
126 }
127
128 /***
129 * @see org.apache.james.nntpserver.NNTPGroup#getNumberOfArticles()
130 */
131 public int getNumberOfArticles() {
132 collectArticleRangeInfo();
133 return numOfArticles;
134 }
135
136 /***
137 * @see org.apache.james.nntpserver.NNTPGroup#getFirstArticleNumber()
138 */
139 public int getFirstArticleNumber() {
140 collectArticleRangeInfo();
141 return firstArticle;
142 }
143
144 /***
145 * @see org.apache.james.nntpserver.NNTPGroup#getLastArticleNumber()
146 */
147 public int getLastArticleNumber() {
148 collectArticleRangeInfo();
149 return lastArticle;
150 }
151
152 /***
153 * @see org.apache.james.nntpserver.NNTPGroup#getArticle(int)
154 */
155 public NNTPArticle getArticle(int number) {
156 File f = new File(root,number + "");
157 return f.exists() ? new NNTPArticleImpl(this, f) : null;
158 }
159
160 /***
161 * @see org.apache.james.nntpserver.NNTPGroup#getArticlesSince(Date)
162 */
163 public Iterator getArticlesSince(Date dt) {
164 File[] f = root.listFiles(new AndFileFilter
165 (new DateSinceFileFilter(dt.getTime()),
166 new InvertedFileFilter(new ExtensionFileFilter(".id"))));
167 List list = new ArrayList();
168 for ( int i = 0 ; i < f.length ; i++ ) {
169 list.add(new NNTPArticleImpl(this, f[i]));
170 }
171 return list.iterator();
172 }
173
174 /***
175 * @see org.apache.james.nntpserver.NNTPGroup#getArticles()
176 */
177 public Iterator getArticles() {
178 File[] f = root.listFiles();
179 List list = new ArrayList();
180 for ( int i = 0 ; i < f.length ; i++ )
181 list.add(new NNTPArticleImpl(this, f[i]));
182 return list.iterator();
183 }
184
185 /***
186 * @see org.apache.james.nntpserver.repository.NNTPGroup#getListFormat()
187 */
188 public String getListFormat() {
189 StringBuffer showBuffer =
190 new StringBuffer(128)
191 .append(getName())
192 .append(" ")
193 .append(getLastArticleNumber())
194 .append(" ")
195 .append(getFirstArticleNumber())
196 .append(" ")
197 .append((isPostAllowed() ? "y":"n"));
198 return showBuffer.toString();
199 }
200
201 /***
202 * @see org.apache.james.nntpserver.repository.NNTPGroup#getListNewsgroupsFormat()
203 */
204 public String getListNewsgroupsFormat() {
205 StringBuffer showBuffer =
206 new StringBuffer(128)
207 .append(getName())
208 .append(" ")
209 .append(getDescription());
210 return showBuffer.toString();
211 }
212
213 /***
214 * @see org.apache.james.nntpserver.repository.NNTPGroup#addArticle(InputStream)
215 */
216 public NNTPArticle addArticle(InputStream newsStream)
217 throws IOException {
218 File articleFile = null;
219 synchronized (this) {
220 int artNum;
221 if (numOfArticles < 0)
222 throw new IllegalStateException("NNTP Group is corrupt (articles < 0).");
223 else if (numOfArticles == 0) {
224 firstArticle = 1;
225 artNum = 1;
226 } else {
227 artNum = getLastArticleNumber() + 1;
228 }
229
230 articleFile = new File(root, artNum + "");
231 articleFile.createNewFile();
232 lastArticle = artNum;
233 numOfArticles++;
234 }
235 if (getLogger().isDebugEnabled()) {
236 getLogger().debug("Copying message to: "+articleFile.getAbsolutePath());
237 }
238 FileOutputStream fout = null;
239 try {
240 fout = new FileOutputStream(articleFile);
241 IOUtil.copy(newsStream,fout);
242 fout.flush();
243 } finally {
244 try {
245 if (fout != null) {
246 fout.close();
247 }
248 } catch (IOException ioe) {
249
250
251 }
252 }
253 return new NNTPArticleImpl(this, articleFile);
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279 }