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 */
020package org.apache.directory.api.ldap.codec.osgi;
021
022
023import java.util.Collections;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.Map;
027
028import javax.naming.NamingException;
029import javax.naming.ldap.BasicControl;
030
031import org.apache.directory.api.asn1.DecoderException;
032import org.apache.directory.api.asn1.EncoderException;
033import org.apache.directory.api.asn1.util.Asn1Buffer;
034import org.apache.directory.api.i18n.I18n;
035import org.apache.directory.api.ldap.codec.BasicControlDecorator;
036import org.apache.directory.api.ldap.codec.api.ControlFactory;
037import org.apache.directory.api.ldap.codec.api.ExtendedOperationFactory;
038import org.apache.directory.api.ldap.codec.api.IntermediateOperationFactory;
039import org.apache.directory.api.ldap.codec.api.LdapApiService;
040import org.apache.directory.api.ldap.codec.controls.cascade.CascadeFactory;
041import org.apache.directory.api.ldap.codec.controls.manageDsaIT.ManageDsaITFactory;
042import org.apache.directory.api.ldap.codec.controls.proxiedauthz.ProxiedAuthzFactory;
043import org.apache.directory.api.ldap.codec.controls.search.entryChange.EntryChangeFactory;
044import org.apache.directory.api.ldap.codec.controls.search.pagedSearch.PagedResultsFactory;
045import org.apache.directory.api.ldap.codec.controls.search.persistentSearch.PersistentSearchFactory;
046import org.apache.directory.api.ldap.codec.controls.search.subentries.SubentriesFactory;
047import org.apache.directory.api.ldap.codec.controls.sort.SortRequestFactory;
048import org.apache.directory.api.ldap.codec.controls.sort.SortResponseFactory;
049import org.apache.directory.api.ldap.model.message.Control;
050import org.apache.directory.api.ldap.model.message.ExtendedRequest;
051import org.apache.directory.api.ldap.model.message.OpaqueExtendedRequest;
052import org.apache.directory.api.ldap.model.message.ExtendedResponse;
053import org.apache.directory.api.ldap.model.message.controls.Cascade;
054import org.apache.directory.api.ldap.model.message.controls.EntryChange;
055import org.apache.directory.api.ldap.model.message.controls.ManageDsaIT;
056import org.apache.directory.api.ldap.model.message.controls.OpaqueControl;
057import org.apache.directory.api.ldap.model.message.controls.PagedResults;
058import org.apache.directory.api.ldap.model.message.controls.PersistentSearch;
059import org.apache.directory.api.ldap.model.message.controls.ProxiedAuthz;
060import org.apache.directory.api.ldap.model.message.controls.SortRequest;
061import org.apache.directory.api.ldap.model.message.controls.SortResponse;
062import org.apache.directory.api.ldap.model.message.controls.Subentries;
063import org.apache.directory.api.util.Strings;
064import org.apache.directory.api.util.exception.NotImplementedException;
065import org.apache.mina.filter.codec.ProtocolCodecFactory;
066import org.slf4j.Logger;
067import org.slf4j.LoggerFactory;
068
069
070/**
071 * The default {@link LdapApiService} implementation.
072 *
073 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
074 * @version $Rev$, $Date$
075 */
076public class DefaultLdapCodecService implements LdapApiService
077{
078    /** A logger */
079    private static final Logger LOG = LoggerFactory.getLogger( DefaultLdapCodecService.class );
080
081    /** The map of registered request {@link ControlFactory}'s */
082    private Map<String, ControlFactory<? extends Control>> requestControlFactories = new HashMap<>();
083
084    /** The map of registered response {@link ControlFactory}'s */
085    private Map<String, ControlFactory<? extends Control>> responseControlFactories = new HashMap<>();
086
087    /** The map of registered {@link ExtendedOperationFactory}'s by request OID */
088    private Map<String, ExtendedOperationFactory> extendedRequestFactories = new HashMap<>();
089
090    /** The map of registered {@link ExtendedOperationFactory}'s by request OID */
091    private Map<String, ExtendedOperationFactory> extendedResponseFactories = new HashMap<>();
092
093    /** The map of registered {@link IntermediateOperationFactory}'s by request OID */
094    private Map<String, IntermediateOperationFactory> intermediateResponseFactories = new HashMap<>();
095
096    /** The registered ProtocolCodecFactory */
097    private ProtocolCodecFactory protocolCodecFactory;
098
099
100    /**
101     * Creates a new instance of DefaultLdapCodecService.
102     */
103    public DefaultLdapCodecService()
104    {
105        loadStockControls();
106    }
107
108
109    /**
110     * Loads the Controls implement out of the box in the codec.
111     */
112    private void loadStockControls()
113    {
114        ControlFactory<Cascade> cascadeFactory = new CascadeFactory( this );
115        requestControlFactories.put( cascadeFactory.getOid(), cascadeFactory );
116
117        if ( LOG.isInfoEnabled() )
118        {
119            LOG.info( I18n.msg( I18n.MSG_06000_REGISTERED_CONTROL_FACTORY, cascadeFactory.getOid() ) );
120        }
121
122        ControlFactory<EntryChange> entryChangeFactory = new EntryChangeFactory( this );
123        responseControlFactories.put( entryChangeFactory.getOid(), entryChangeFactory );
124
125        if ( LOG.isInfoEnabled() )
126        {
127            LOG.info( I18n.msg( I18n.MSG_06000_REGISTERED_CONTROL_FACTORY, entryChangeFactory.getOid() ) );
128        }
129
130        ControlFactory<ManageDsaIT> manageDsaItFactory = new ManageDsaITFactory( this );
131        requestControlFactories.put( manageDsaItFactory.getOid(), manageDsaItFactory );
132
133        if ( LOG.isInfoEnabled() )
134        {
135            LOG.info( I18n.msg( I18n.MSG_06000_REGISTERED_CONTROL_FACTORY, manageDsaItFactory.getOid() ) );
136        }
137
138        ControlFactory<PagedResults> pageResultsFactory = new PagedResultsFactory( this );
139        requestControlFactories.put( pageResultsFactory.getOid(), pageResultsFactory );
140        responseControlFactories.put( pageResultsFactory.getOid(), pageResultsFactory );
141
142        if ( LOG.isInfoEnabled() )
143        {
144            LOG.info( I18n.msg( I18n.MSG_06000_REGISTERED_CONTROL_FACTORY, pageResultsFactory.getOid() ) );
145        }
146
147        ControlFactory<PersistentSearch> persistentSearchFactory = new PersistentSearchFactory( this );
148        requestControlFactories.put( persistentSearchFactory.getOid(), persistentSearchFactory );
149
150        if ( LOG.isInfoEnabled() )
151        {
152            LOG.info( I18n.msg( I18n.MSG_06000_REGISTERED_CONTROL_FACTORY, persistentSearchFactory.getOid() ) );
153        }
154
155        ControlFactory<ProxiedAuthz> proxiedAuthzFactory = new ProxiedAuthzFactory( this );
156        requestControlFactories.put( proxiedAuthzFactory.getOid(), proxiedAuthzFactory );
157
158        if ( LOG.isInfoEnabled() )
159        {
160            LOG.info( I18n.msg( I18n.MSG_06000_REGISTERED_CONTROL_FACTORY, proxiedAuthzFactory.getOid() ) );
161        }
162
163        ControlFactory<SortRequest> sortRequestFactory = new SortRequestFactory( this );
164        requestControlFactories.put( sortRequestFactory.getOid(), sortRequestFactory );
165
166        if ( LOG.isInfoEnabled() )
167        {
168            LOG.info( I18n.msg( I18n.MSG_06000_REGISTERED_CONTROL_FACTORY, sortRequestFactory.getOid() ) );
169        }
170
171        ControlFactory<SortResponse> sortResponseFactory = new SortResponseFactory( this );
172        responseControlFactories.put( sortResponseFactory.getOid(), sortResponseFactory );
173
174        if ( LOG.isInfoEnabled() )
175        {
176            LOG.info( I18n.msg( I18n.MSG_06000_REGISTERED_CONTROL_FACTORY, sortResponseFactory.getOid() ) );
177        }
178
179        ControlFactory<Subentries> subentriesFactory = new SubentriesFactory( this );
180        requestControlFactories.put( subentriesFactory.getOid(), subentriesFactory );
181
182        if ( LOG.isInfoEnabled() )
183        {
184            LOG.info( I18n.msg( I18n.MSG_06000_REGISTERED_CONTROL_FACTORY, subentriesFactory.getOid() ) );
185        }
186    }
187
188
189    //-------------------------------------------------------------------------
190    // LdapCodecService implementation methods
191    //-------------------------------------------------------------------------
192
193    /**
194     * {@inheritDoc}
195     */
196    @Override
197    public ControlFactory<?> registerRequestControl( ControlFactory<?> factory )
198    {
199        return requestControlFactories.put( factory.getOid(), factory );
200    }
201
202    /**
203     * {@inheritDoc}
204     */
205    @Override
206    public ControlFactory<?> registerResponseControl( ControlFactory<?> factory )
207    {
208        return responseControlFactories.put( factory.getOid(), factory );
209    }
210
211
212    /**
213     * {@inheritDoc}
214     */
215    @Override
216    public ControlFactory<?> unregisterRequestControl( String oid )
217    {
218        return requestControlFactories.remove( oid );
219    }
220
221
222    /**
223     * {@inheritDoc}
224     */
225    @Override
226    public ControlFactory<?> unregisterResponseControl( String oid )
227    {
228        return responseControlFactories.remove( oid );
229    }
230
231
232    /**
233     * {@inheritDoc}
234     */
235    @Override
236    public Iterator<String> registeredRequestControls()
237    {
238        return Collections.unmodifiableSet( requestControlFactories.keySet() ).iterator();
239    }
240
241
242    /**
243     * {@inheritDoc}
244     */
245    @Override
246    public Iterator<String> registeredResponseControls()
247    {
248        return Collections.unmodifiableSet( responseControlFactories.keySet() ).iterator();
249    }
250
251
252    /**
253     * {@inheritDoc}
254     */
255    @Override
256    public boolean isControlRegistered( String oid )
257    {
258        return requestControlFactories.containsKey( oid ) || responseControlFactories.containsKey( oid );
259    }
260
261
262    /**
263     * {@inheritDoc}
264     */
265    @Override
266    public Iterator<String> registeredExtendedRequests()
267    {
268        return Collections.unmodifiableSet( extendedRequestFactories.keySet() ).iterator();
269    }
270
271
272    /**
273     * {@inheritDoc}
274     */
275    @Override
276    public Iterator<String> registeredExtendedResponses()
277    {
278        return Collections.unmodifiableSet( extendedResponseFactories.keySet() ).iterator();
279    }
280
281
282    /**
283     * {@inheritDoc}
284     */
285    @Override
286    public ExtendedOperationFactory registerExtendedRequest( ExtendedOperationFactory factory )
287    {
288        return extendedRequestFactories.put( factory.getOid(), factory );
289    }
290
291
292    /**
293     * {@inheritDoc}
294     */
295    @Override
296    public ExtendedOperationFactory registerExtendedResponse( ExtendedOperationFactory factory )
297    {
298        return extendedResponseFactories.put( factory.getOid(), factory );
299    }
300
301
302    /**
303     * {@inheritDoc}
304     */
305    @Override
306    public Iterator<String> registeredIntermediateResponses()
307    {
308        return Collections.unmodifiableSet( intermediateResponseFactories.keySet() ).iterator();
309    }
310
311
312    /**
313     * {@inheritDoc}
314     */
315    @Override
316    public IntermediateOperationFactory registerIntermediateResponse( IntermediateOperationFactory factory )
317    {
318        return intermediateResponseFactories.put( factory.getOid(), factory );
319    }
320
321
322    /**
323     * {@inheritDoc}
324     */
325    @Override
326    public ProtocolCodecFactory getProtocolCodecFactory()
327    {
328        return protocolCodecFactory;
329    }
330
331
332    /**
333     * {@inheritDoc}
334     */
335    @Override
336    public ProtocolCodecFactory registerProtocolCodecFactory( ProtocolCodecFactory protocolCodecFactory )
337    {
338        ProtocolCodecFactory oldFactory = this.protocolCodecFactory;
339        this.protocolCodecFactory = protocolCodecFactory;
340        return oldFactory;
341    }
342
343
344    /**
345     * {@inheritDoc}
346     */
347    @Override
348    public javax.naming.ldap.Control toJndiControl( Control control ) throws EncoderException
349    {
350        // We don't know if it's a request or a response control. Test with request contriols
351        ControlFactory<?> factory = requestControlFactories.get( control.getOid() );
352        
353        if ( factory == null )
354        {
355            if ( control instanceof OpaqueControl )
356            {
357                return new BasicControl( control.getOid(), control.isCritical(), ( ( OpaqueControl ) control ).getEncodedValue() );
358            }
359            else
360            {
361                return new BasicControl( control.getOid(), control.isCritical(), null );
362            }
363        }
364        else
365        {
366            Asn1Buffer asn1Buffer = new Asn1Buffer();
367            factory.encodeValue( asn1Buffer, control );
368    
369            return new BasicControl( control.getOid(), control.isCritical(), asn1Buffer.getBytes().array() );
370        }
371    }
372
373
374    /**
375     * {@inheritDoc}
376     */
377    @Override
378    public Control fromJndiRequestControl( javax.naming.ldap.Control control ) throws DecoderException
379    {
380        @SuppressWarnings("rawtypes")
381        ControlFactory factory = requestControlFactories.get( control.getID() );
382
383        if ( factory == null )
384        {
385            OpaqueControl ourControl = new OpaqueControl( control.getID() );
386            ourControl.setCritical( control.isCritical() );
387            BasicControlDecorator decorator =
388                new BasicControlDecorator( this, ourControl );
389            decorator.setValue( control.getEncodedValue() );
390            return decorator;
391        }
392
393        Control ourControl = factory.newControl();
394        ourControl.setCritical( control.isCritical() );
395        factory.decodeValue( ourControl, control.getEncodedValue() );
396
397        return ourControl;
398    }
399
400
401    /**
402     * {@inheritDoc}
403     */
404    @Override
405    public Control fromJndiResponseControl( javax.naming.ldap.Control control ) throws DecoderException
406    {
407        @SuppressWarnings("rawtypes")
408        ControlFactory factory = responseControlFactories.get( control.getID() );
409
410        if ( factory == null )
411        {
412            OpaqueControl ourControl = new OpaqueControl( control.getID() );
413            ourControl.setCritical( control.isCritical() );
414            BasicControlDecorator decorator =
415                new BasicControlDecorator( this, ourControl );
416            decorator.setValue( control.getEncodedValue() );
417            return decorator;
418        }
419
420        Control ourControl = factory.newControl();
421        ourControl.setCritical( control.isCritical() );
422        factory.decodeValue( ourControl, control.getEncodedValue() );
423
424        return ourControl;
425    }
426
427
428    /**
429     * {@inheritDoc}
430     */
431    @Override
432    public ExtendedOperationFactory unregisterExtendedRequest( String oid )
433    {
434        return extendedRequestFactories.remove( oid );
435    }
436
437
438    /**
439     * {@inheritDoc}
440     */
441    @Override
442    public ExtendedOperationFactory unregisterExtendedResponse( String oid )
443    {
444        return extendedResponseFactories.remove( oid );
445    }
446
447
448    /**
449     * {@inheritDoc}
450     */
451    @Override
452    public IntermediateOperationFactory unregisterIntermediateResponse( String oid )
453    {
454        return intermediateResponseFactories.remove( oid );
455    }
456
457
458    /**
459     * {@inheritDoc}
460     */
461    @Override
462    public javax.naming.ldap.ExtendedResponse toJndi( final ExtendedResponse modelResponse ) throws EncoderException
463    {
464        throw new NotImplementedException( I18n.err( I18n.ERR_05401_FIGURE_OUT_HOW_TO_TRANSFORM ) );
465    }
466
467
468    /**
469     * {@inheritDoc}
470     */
471    @Override
472    public ExtendedResponse fromJndi( javax.naming.ldap.ExtendedResponse jndiResponse ) throws DecoderException
473    {
474        throw new NotImplementedException( I18n.err( I18n.ERR_05401_FIGURE_OUT_HOW_TO_TRANSFORM ) );
475    }
476
477
478    /**
479     * {@inheritDoc}
480     */
481    @Override
482    public ExtendedRequest fromJndi( javax.naming.ldap.ExtendedRequest jndiRequest ) throws DecoderException
483    {
484        ExtendedOperationFactory extendedRequestFactory = extendedRequestFactories.get( jndiRequest
485            .getID() );
486
487        if ( extendedRequestFactory != null )
488        {
489            return extendedRequestFactory.newRequest( jndiRequest.getEncodedValue() );
490        }
491        else
492        {
493            return new OpaqueExtendedRequest( jndiRequest.getID(), jndiRequest.getEncodedValue() );
494        }
495    }
496
497
498    /**
499     * {@inheritDoc}
500     */
501    @Override
502    public javax.naming.ldap.ExtendedRequest toJndi( final ExtendedRequest modelRequest ) throws EncoderException
503    {
504        final String oid = modelRequest.getRequestName();
505
506        // have to ask the factory to decorate for us - can't do it ourselves
507        ExtendedOperationFactory extendedRequestFactory = extendedRequestFactories.get( modelRequest
508            .getRequestName() );
509        Asn1Buffer asn1Buffer = new Asn1Buffer();
510        extendedRequestFactory.encodeValue( asn1Buffer, modelRequest );
511        
512        final byte[] value = asn1Buffer.getBytes().array();
513
514        return new javax.naming.ldap.ExtendedRequest()
515        {
516            private static final long serialVersionUID = -4160980385909987475L;
517
518
519            @Override
520            public String getID()
521            {
522                return oid;
523            }
524
525
526            @Override
527            public byte[] getEncodedValue()
528            {
529                return value;
530            }
531
532
533            @Override
534            public javax.naming.ldap.ExtendedResponse createExtendedResponse( String id, byte[] berValue, int offset,
535                int length ) throws NamingException
536            {
537                final ExtendedOperationFactory factory = extendedResponseFactories
538                    .get( modelRequest.getRequestName() );
539
540                try
541                {
542                    final ExtendedResponse resp = factory.newResponse( berValue );
543                    
544                    return new javax.naming.ldap.ExtendedResponse()
545                    {
546                        private static final long serialVersionUID = -7686354122066100703L;
547
548
549                        @Override
550                        public String getID()
551                        {
552                            return oid;
553                        }
554
555
556                        @Override
557                        public byte[] getEncodedValue()
558                        {
559                            Asn1Buffer asn1Buffer = new Asn1Buffer();
560                            
561                            factory.encodeValue( asn1Buffer, resp );
562                            
563                            return asn1Buffer.getBytes().array();
564                        }
565                    };
566                }
567                catch ( DecoderException de )
568                {
569                    NamingException ne = new NamingException( I18n.err( I18n.ERR_05402_UNABLE_TO_ENCODE_RESPONSE_VALUE,
570                        Strings.dumpBytes( berValue ) ) );
571                    ne.setRootCause( de );
572                    throw ne;
573                }
574            }
575        };
576    }
577
578
579    /**
580     * {@inheritDoc}
581     */
582    @Override
583    public boolean isExtendedRequestRegistered( String oid )
584    {
585        return extendedRequestFactories.containsKey( oid );
586    }
587
588
589    /**
590     * {@inheritDoc}
591     */
592    @Override
593    public boolean isExtendedResponseRegistered( String oid )
594    {
595        return extendedResponseFactories.containsKey( oid );
596    }
597
598
599    /**
600     * {@inheritDoc}
601     */
602    @Override
603    public boolean isIntermediateResponseRegistered( String oid )
604    {
605        return intermediateResponseFactories.containsKey( oid );
606    }
607
608
609    /**
610     * {@inheritDoc}
611     */
612    @Override
613    public Map<String, ControlFactory<? extends Control>> getRequestControlFactories()
614    {
615        return requestControlFactories;
616    }
617
618
619    /**
620     * {@inheritDoc}
621     */
622    @Override
623    public Map<String, ControlFactory<? extends Control>> getResponseControlFactories()
624    {
625        return responseControlFactories;
626    }
627
628
629    /**
630     * @param requestControlFactories the request controlFactories to set
631     */
632    public void setRequestControlFactories( Map<String, ControlFactory<? extends Control>> requestControlFactories )
633    {
634        this.requestControlFactories = requestControlFactories;
635    }
636
637
638    /**
639     * @param responseControlFactories the response controlFactories to set
640     */
641    public void setResponseControlFactories( Map<String, ControlFactory<? extends Control>> responseControlFactories )
642    {
643        this.responseControlFactories = responseControlFactories;
644    }
645
646
647    /**
648     * @return the extendedRequestFactories
649     */
650    public Map<String, ExtendedOperationFactory> getExtendedRequestFactories()
651    {
652        return extendedRequestFactories;
653    }
654
655
656    /**
657     * @return the extendedResponseFactories
658     */
659    @Override
660    public Map<String, ExtendedOperationFactory> getExtendedResponseFactories()
661    {
662        return extendedResponseFactories;
663    }
664
665
666    /**
667     * @return the intermediateResponseFactories
668     */
669    public Map<String, IntermediateOperationFactory> getIntermediateResponseFactories()
670    {
671        return intermediateResponseFactories;
672    }
673
674
675    /**
676     * @param extendedOperationFactories the extendedOperationFactories to set
677     */
678    public void setExtendedRequestFactories( Map<String, ExtendedOperationFactory> extendedOperationFactories )
679    {
680        this.extendedRequestFactories = extendedOperationFactories;
681    }
682
683
684    /**
685     * @param extendedOperationFactories the extendedOperationFactories to set
686     */
687    public void setExtendedResponseFactories( Map<String, ExtendedOperationFactory> extendedOperationFactories )
688    {
689        this.extendedResponseFactories = extendedOperationFactories;
690    }
691
692
693    /**
694     * @param intermediateResponseFactories the intermediateResponseFactories to set
695     */
696    public void setIntermediateResponseFactories( Map<String, IntermediateOperationFactory> intermediateResponseFactories )
697    {
698        this.intermediateResponseFactories = intermediateResponseFactories;
699    }
700
701
702    /**
703     * @param protocolCodecFactory the protocolCodecFactory to set
704     */
705    public void setProtocolCodecFactory( ProtocolCodecFactory protocolCodecFactory )
706    {
707        this.protocolCodecFactory = protocolCodecFactory;
708    }
709    
710    
711    public String toString()
712    {
713        StringBuilder sb = new StringBuilder();
714        
715        sb.append( "Request controls       :\n" );
716        
717        for ( Map.Entry<String, ControlFactory<?>> element : requestControlFactories .entrySet() )
718        {
719            sb.append( "    " );
720            sb.append( element.getValue().getClass().getSimpleName() );
721            sb.append( "[" ).append( element.getKey() ).append( "]\n" );
722        }
723        
724        sb.append( "Response controls      :\n" );
725        
726        for ( Map.Entry<String, ControlFactory<?>> element : responseControlFactories .entrySet() )
727        {
728            sb.append( "    " );
729            sb.append( element.getValue().getClass().getSimpleName() );
730            sb.append( "[" ).append( element.getKey() ).append( "]\n" );
731        }
732        
733        sb.append( "Extended requests      :\n" );
734        
735        for ( Map.Entry<String, ExtendedOperationFactory> element : extendedRequestFactories .entrySet() )
736        {
737            sb.append( "    " );
738            sb.append( element.getValue().getClass().getSimpleName() );
739            sb.append( "[" ).append( element.getKey() ).append( "]\n" );
740        }
741        
742        sb.append( "Extended responses     :\n" );
743        
744        for ( Map.Entry<String, ExtendedOperationFactory> element : extendedResponseFactories.entrySet() )
745        {
746            sb.append( "    " );
747            sb.append( element.getValue().getClass().getSimpleName() );
748            sb.append( "[" ).append( element.getKey() ).append( "]\n" );
749        }
750        
751        sb.append( "Intermediate responses :\n" );
752        
753        for ( Map.Entry<String, IntermediateOperationFactory> element : intermediateResponseFactories .entrySet() )
754        {
755            sb.append( "    " );
756            sb.append( element.getValue().getClass().getSimpleName() );
757            sb.append( "[" ).append( element.getKey() ).append( "]\n" );
758        }
759
760        return sb.toString();
761    }
762}