001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: *
019: */
020: package org.apache.mina.common;
021:
022: import java.util.ArrayList;
023: import java.util.Collections;
024: import java.util.Iterator;
025: import java.util.LinkedHashMap;
026: import java.util.List;
027: import java.util.ListIterator;
028: import java.util.Map;
029: import java.util.Random;
030: import java.util.concurrent.CopyOnWriteArrayList;
031:
032: import org.apache.mina.common.IoFilter.NextFilter;
033: import org.apache.mina.common.IoFilterChain.Entry;
034: import org.slf4j.Logger;
035: import org.slf4j.LoggerFactory;
036:
037: /**
038: * The default implementation of {@link IoFilterChainBuilder} which is useful
039: * in most cases. {@link DefaultIoFilterChainBuilder} has an identical interface
040: * with {@link IoFilter}; it contains a list of {@link IoFilter}s that you can
041: * modify. The {@link IoFilter}s which are added to this builder will be appended
042: * to the {@link IoFilterChain} when {@link #buildFilterChain(IoFilterChain)} is
043: * invoked.
044: * <p>
045: * However, the identical interface doesn't mean that it behaves in an exactly
046: * same way with {@link IoFilterChain}. {@link DefaultIoFilterChainBuilder}
047: * doesn't manage the life cycle of the {@link IoFilter}s at all, and the
048: * existing {@link IoSession}s won't get affected by the changes in this builder.
049: * {@link IoFilterChainBuilder}s affect only newly created {@link IoSession}s.
050: *
051: * <pre>
052: * IoAcceptor acceptor = ...;
053: * DefaultIoFilterChainBuilder builder = acceptor.getFilterChain();
054: * builder.addLast( "myFilter", new MyFilter() );
055: * ...
056: * </pre>
057: *
058: * @author The Apache MINA Project (dev@mina.apache.org)
059: * @version $Rev: 594846 $, $Date: 2007-11-14 05:03:07 -0700 (Wed, 14 Nov 2007) $
060: */
061: public class DefaultIoFilterChainBuilder implements
062: IoFilterChainBuilder {
063:
064: private final Logger logger = LoggerFactory.getLogger(getClass());
065: private final List<Entry> entries;
066:
067: /**
068: * Creates a new instance with an empty filter list.
069: */
070: public DefaultIoFilterChainBuilder() {
071: entries = new CopyOnWriteArrayList<Entry>();
072: }
073:
074: /**
075: * Creates a new copy of the specified {@link DefaultIoFilterChainBuilder}.
076: */
077: public DefaultIoFilterChainBuilder(
078: DefaultIoFilterChainBuilder filterChain) {
079: if (filterChain == null) {
080: throw new NullPointerException("filterChain");
081: }
082: entries = new CopyOnWriteArrayList<Entry>(filterChain.entries);
083: }
084:
085: /**
086: * @see IoFilterChain#getEntry(String)
087: */
088: public Entry getEntry(String name) {
089: for (Entry e : entries) {
090: if (e.getName().equals(name)) {
091: return e;
092: }
093: }
094:
095: return null;
096: }
097:
098: /**
099: * @see IoFilterChain#getEntry(IoFilter)
100: */
101: public Entry getEntry(IoFilter filter) {
102: for (Entry e : entries) {
103: if (e.getFilter() == filter) {
104: return e;
105: }
106: }
107:
108: return null;
109: }
110:
111: /**
112: * @see IoFilterChain#getEntry(Class)
113: */
114: public Entry getEntry(Class<? extends IoFilter> filterType) {
115: for (Entry e : entries) {
116: if (filterType.isAssignableFrom(e.getFilter().getClass())) {
117: return e;
118: }
119: }
120:
121: return null;
122: }
123:
124: /**
125: * @see IoFilterChain#get(String)
126: */
127: public IoFilter get(String name) {
128: Entry e = getEntry(name);
129: if (e == null) {
130: return null;
131: }
132:
133: return e.getFilter();
134: }
135:
136: /**
137: * @see IoFilterChain#get(Class)
138: */
139: public IoFilter get(Class<? extends IoFilter> filterType) {
140: Entry e = getEntry(filterType);
141: if (e == null) {
142: return null;
143: }
144:
145: return e.getFilter();
146: }
147:
148: /**
149: * @see IoFilterChain#getAll()
150: */
151: public List<Entry> getAll() {
152: return new ArrayList<Entry>(entries);
153: }
154:
155: /**
156: * @see IoFilterChain#getAllReversed()
157: */
158: public List<Entry> getAllReversed() {
159: List<Entry> result = getAll();
160: Collections.reverse(result);
161: return result;
162: }
163:
164: /**
165: * @see IoFilterChain#contains(String)
166: */
167: public boolean contains(String name) {
168: return getEntry(name) != null;
169: }
170:
171: /**
172: * @see IoFilterChain#contains(IoFilter)
173: */
174: public boolean contains(IoFilter filter) {
175: return getEntry(filter) != null;
176: }
177:
178: /**
179: * @see IoFilterChain#contains(Class)
180: */
181: public boolean contains(Class<? extends IoFilter> filterType) {
182: return getEntry(filterType) != null;
183: }
184:
185: /**
186: * @see IoFilterChain#addFirst(String, IoFilter)
187: */
188: public synchronized void addFirst(String name, IoFilter filter) {
189: register(0, new EntryImpl(name, filter));
190: }
191:
192: /**
193: * @see IoFilterChain#addLast(String, IoFilter)
194: */
195: public synchronized void addLast(String name, IoFilter filter) {
196: register(entries.size(), new EntryImpl(name, filter));
197: }
198:
199: /**
200: * @see IoFilterChain#addBefore(String, String, IoFilter)
201: */
202: public synchronized void addBefore(String baseName, String name,
203: IoFilter filter) {
204: checkBaseName(baseName);
205:
206: for (ListIterator<Entry> i = entries.listIterator(); i
207: .hasNext();) {
208: Entry base = i.next();
209: if (base.getName().equals(baseName)) {
210: register(i.previousIndex(), new EntryImpl(name, filter));
211: break;
212: }
213: }
214: }
215:
216: /**
217: * @see IoFilterChain#addAfter(String, String, IoFilter)
218: */
219: public synchronized void addAfter(String baseName, String name,
220: IoFilter filter) {
221: checkBaseName(baseName);
222:
223: for (ListIterator<Entry> i = entries.listIterator(); i
224: .hasNext();) {
225: Entry base = i.next();
226: if (base.getName().equals(baseName)) {
227: register(i.nextIndex(), new EntryImpl(name, filter));
228: break;
229: }
230: }
231: }
232:
233: /**
234: * @see IoFilterChain#remove(String)
235: */
236: public synchronized IoFilter remove(String name) {
237: if (name == null) {
238: throw new NullPointerException("name");
239: }
240:
241: for (ListIterator<Entry> i = entries.listIterator(); i
242: .hasNext();) {
243: Entry e = i.next();
244: if (e.getName().equals(name)) {
245: entries.remove(i.previousIndex());
246: return e.getFilter();
247: }
248: }
249:
250: throw new IllegalArgumentException("Unknown filter name: "
251: + name);
252: }
253:
254: /**
255: * @see IoFilterChain#remove(IoFilter)
256: */
257: public synchronized IoFilter remove(IoFilter filter) {
258: if (filter == null) {
259: throw new NullPointerException("filter");
260: }
261:
262: for (ListIterator<Entry> i = entries.listIterator(); i
263: .hasNext();) {
264: Entry e = i.next();
265: if (e.getFilter() == filter) {
266: entries.remove(i.previousIndex());
267: return e.getFilter();
268: }
269: }
270:
271: throw new IllegalArgumentException("Filter not found: "
272: + filter.getClass().getName());
273: }
274:
275: /**
276: * @see IoFilterChain#remove(Class)
277: */
278: public synchronized IoFilter remove(
279: Class<? extends IoFilter> filterType) {
280: if (filterType == null) {
281: throw new NullPointerException("filterType");
282: }
283:
284: for (ListIterator<Entry> i = entries.listIterator(); i
285: .hasNext();) {
286: Entry e = i.next();
287: if (filterType.isAssignableFrom(e.getFilter().getClass())) {
288: entries.remove(i.previousIndex());
289: return e.getFilter();
290: }
291: }
292:
293: throw new IllegalArgumentException("Filter not found: "
294: + filterType.getName());
295: }
296:
297: public synchronized IoFilter replace(String name, IoFilter newFilter) {
298: checkBaseName(name);
299: EntryImpl e = (EntryImpl) get(name);
300: IoFilter oldFilter = e.getFilter();
301: e.setFilter(newFilter);
302: return oldFilter;
303: }
304:
305: public synchronized void replace(IoFilter oldFilter,
306: IoFilter newFilter) {
307: for (Entry e : entries) {
308: if (e.getFilter() == oldFilter) {
309: ((EntryImpl) e).setFilter(newFilter);
310: return;
311: }
312: }
313: throw new IllegalArgumentException("Filter not found: "
314: + oldFilter.getClass().getName());
315: }
316:
317: public synchronized void replace(
318: Class<? extends IoFilter> oldFilterType, IoFilter newFilter) {
319: for (Entry e : entries) {
320: if (oldFilterType
321: .isAssignableFrom(e.getFilter().getClass())) {
322: ((EntryImpl) e).setFilter(newFilter);
323: return;
324: }
325: }
326: throw new IllegalArgumentException("Filter not found: "
327: + oldFilterType.getName());
328: }
329:
330: /**
331: * @see IoFilterChain#clear()
332: */
333: public synchronized void clear() {
334: entries.clear();
335: }
336:
337: /**
338: * Clears the current list of filters and adds the specified
339: * filter mapping to this builder. Please note that you must specify
340: * a {@link Map} implementation that iterates the filter mapping in the
341: * order of insertion such as {@link LinkedHashMap}. Otherwise, it will
342: * throw an {@link IllegalArgumentException}.
343: */
344: public void setFilters(Map<String, ? extends IoFilter> filters) {
345: if (filters == null) {
346: throw new NullPointerException("filters");
347: }
348:
349: if (!isOrderedMap(filters)) {
350: throw new IllegalArgumentException(
351: "filters is not an ordered map. Please try "
352: + LinkedHashMap.class.getName() + ".");
353: }
354:
355: filters = new LinkedHashMap<String, IoFilter>(filters);
356: for (Map.Entry<String, ? extends IoFilter> e : filters
357: .entrySet()) {
358: if (e.getKey() == null) {
359: throw new NullPointerException(
360: "filters contains a null key.");
361: }
362: if (e.getValue() == null) {
363: throw new NullPointerException(
364: "filters contains a null value.");
365: }
366: }
367:
368: synchronized (this ) {
369: clear();
370: for (Map.Entry<String, ? extends IoFilter> e : filters
371: .entrySet()) {
372: addLast(e.getKey(), e.getValue());
373: }
374: }
375: }
376:
377: @SuppressWarnings("unchecked")
378: private boolean isOrderedMap(Map map) {
379: Class<?> mapType = map.getClass();
380: if (LinkedHashMap.class.isAssignableFrom(mapType)) {
381: if (logger.isDebugEnabled()) {
382: logger.debug(mapType.getSimpleName()
383: + " is an ordered map.");
384: }
385: return true;
386: }
387:
388: if (logger.isDebugEnabled()) {
389: logger.debug(mapType.getName() + " is not a "
390: + LinkedHashMap.class.getSimpleName());
391: }
392:
393: // Detect Jakarta Commons Collections OrderedMap implementations.
394: Class<?> type = mapType;
395: while (type != null) {
396: for (Class<?> i : type.getInterfaces()) {
397: if (i.getName().endsWith("OrderedMap")) {
398: if (logger.isDebugEnabled()) {
399: logger
400: .debug(mapType.getSimpleName()
401: + " is an ordered map (guessed from that it "
402: + " implements OrderedMap interface.)");
403: }
404: return true;
405: }
406: }
407: type = type.getSuperclass();
408: }
409:
410: if (logger.isDebugEnabled()) {
411: logger.debug(mapType.getName()
412: + " doesn't implement OrderedMap interface.");
413: }
414:
415: // Last resort: try to create a new instance and test if it maintains
416: // the insertion order.
417: logger
418: .debug("Last resort; trying to create a new map instance with a "
419: + "default constructor and test if insertion order is "
420: + "maintained.");
421:
422: Map newMap;
423: try {
424: newMap = (Map) mapType.newInstance();
425: } catch (Exception e) {
426: if (logger.isDebugEnabled()) {
427: logger.debug("Failed to create a new map instance of '"
428: + mapType.getName() + "'.", e);
429: }
430: return false;
431: }
432:
433: Random rand = new Random();
434: List<String> expectedNames = new ArrayList<String>();
435: IoFilter dummyFilter = new IoFilterAdapter();
436: for (int i = 0; i < 65536; i++) {
437: String filterName;
438: do {
439: filterName = String.valueOf(rand.nextInt());
440: } while (newMap.containsKey(filterName));
441:
442: newMap.put(filterName, dummyFilter);
443: expectedNames.add(filterName);
444:
445: Iterator<String> it = expectedNames.iterator();
446: for (Object key : newMap.keySet()) {
447: if (!it.next().equals(key)) {
448: if (logger.isDebugEnabled()) {
449: logger
450: .debug("The specified map didn't pass the insertion "
451: + "order test after "
452: + (i + 1)
453: + " tries.");
454: }
455: return false;
456: }
457: }
458: }
459:
460: if (logger.isDebugEnabled()) {
461: logger
462: .debug("The specified map passed the insertion order test.");
463: }
464: return true;
465: }
466:
467: public void buildFilterChain(IoFilterChain chain) throws Exception {
468: for (Entry e : entries) {
469: chain.addLast(e.getName(), e.getFilter());
470: }
471: }
472:
473: @Override
474: public String toString() {
475: StringBuffer buf = new StringBuffer();
476: buf.append("{ ");
477:
478: boolean empty = true;
479:
480: for (Entry e : entries) {
481: if (!empty) {
482: buf.append(", ");
483: } else {
484: empty = false;
485: }
486:
487: buf.append('(');
488: buf.append(e.getName());
489: buf.append(':');
490: buf.append(e.getFilter());
491: buf.append(')');
492: }
493:
494: if (empty) {
495: buf.append("empty");
496: }
497:
498: buf.append(" }");
499:
500: return buf.toString();
501: }
502:
503: private void checkBaseName(String baseName) {
504: if (baseName == null) {
505: throw new NullPointerException("baseName");
506: }
507:
508: if (!contains(baseName)) {
509: throw new IllegalArgumentException("Unknown filter name: "
510: + baseName);
511: }
512: }
513:
514: private void register(int index, Entry e) {
515: if (contains(e.getName())) {
516: throw new IllegalArgumentException(
517: "Other filter is using the same name: "
518: + e.getName());
519: }
520:
521: entries.add(index, e);
522: }
523:
524: private class EntryImpl implements Entry {
525: private final String name;
526: private volatile IoFilter filter;
527:
528: private EntryImpl(String name, IoFilter filter) {
529: if (name == null) {
530: throw new NullPointerException("name");
531: }
532: if (filter == null) {
533: throw new NullPointerException("filter");
534: }
535:
536: this .name = name;
537: this .filter = filter;
538: }
539:
540: public String getName() {
541: return name;
542: }
543:
544: public IoFilter getFilter() {
545: return filter;
546: }
547:
548: private void setFilter(IoFilter filter) {
549: this .filter = filter;
550: }
551:
552: public NextFilter getNextFilter() {
553: throw new IllegalStateException();
554: }
555:
556: @Override
557: public String toString() {
558: return "(" + getName() + ':' + filter + ')';
559: }
560:
561: public void addAfter(String name, IoFilter filter) {
562: DefaultIoFilterChainBuilder.this .addAfter(getName(), name,
563: filter);
564: }
565:
566: public void addBefore(String name, IoFilter filter) {
567: DefaultIoFilterChainBuilder.this .addBefore(getName(), name,
568: filter);
569: }
570:
571: public void remove() {
572: DefaultIoFilterChainBuilder.this .remove(getName());
573: }
574:
575: public void replace(IoFilter newFilter) {
576: DefaultIoFilterChainBuilder.this.replace(getName(),
577: newFilter);
578: }
579: }
580: }
|