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