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.directory.shared.ldap.message;
021
022
023 import org.apache.directory.shared.asn1.Asn1Object;
024 import org.apache.directory.shared.asn1.codec.DecoderException;
025 import org.apache.directory.shared.asn1.codec.stateful.DecoderCallback;
026 import org.apache.directory.shared.asn1.codec.stateful.DecoderMonitor;
027 import org.apache.directory.shared.asn1.codec.stateful.StatefulDecoder;
028 import org.apache.directory.shared.ldap.codec.ResponseCarryingException;
029 import org.apache.directory.shared.ldap.codec.TwixTransformer;
030 import org.apache.directory.shared.ldap.message.spi.BinaryAttributeDetector;
031 import org.apache.directory.shared.ldap.message.spi.Provider;
032 import org.apache.directory.shared.ldap.message.spi.ProviderDecoder;
033
034 import java.io.InputStream;
035 import java.util.Hashtable;
036
037
038 /**
039 * Decodes a BER encoded LDAPv3 message envelope from an input stream
040 * demarshaling it into a Message instance using a BER library provider.
041 *
042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043 * @version $Rev: 725712 $
044 */
045 public final class MessageDecoder implements ProviderDecoder
046 {
047 /** the ASN.1 provider */
048 private final Provider provider;
049
050 /** the ASN.1 provider's decoder */
051 private final ProviderDecoder decoder;
052
053 /** the Message decode operation callback */
054 private DecoderCallback cb;
055
056
057 /**
058 * Creates a MessageDecoder using default properties for enabling a BER
059 * library provider.
060 *
061 * @param binaryAttributeDetector detects whether or not an attribute is binary
062 * @throws MessageException if there is a problem creating this decoder.
063 */
064 public MessageDecoder( BinaryAttributeDetector binaryAttributeDetector ) throws MessageException
065 {
066 this( binaryAttributeDetector, Integer.MAX_VALUE );
067 }
068
069
070 /**
071 * Creates a MessageDecoder using default properties for enabling a BER
072 * library provider.
073 *
074 * @param binaryAttributeDetector detects whether or not an attribute is binary
075 * @param maxPDUSize the maximum size a PDU can be
076 * @throws MessageException if there is a problem creating this decoder.
077 */
078 public MessageDecoder( BinaryAttributeDetector binaryAttributeDetector, int maxPDUSize ) throws MessageException
079 {
080 // We need to get the encoder class name
081 Hashtable<Object, Object> providerEnv = Provider.getEnvironment();
082
083 this.provider = Provider.getProvider( providerEnv );
084 this.decoder = this.provider.getDecoder( binaryAttributeDetector, maxPDUSize );
085 this.decoder.setCallback( new DecoderCallback()
086 {
087 public void decodeOccurred( StatefulDecoder decoder, Object decoded )
088 {
089 if ( decoded instanceof Asn1Object )
090 {
091 cb.decodeOccurred( decoder, TwixTransformer.transform( decoded ) );
092 }
093 else
094 {
095 cb.decodeOccurred( decoder, decoded );
096 }
097 }
098 } );
099 }
100
101
102 /**
103 * Reads and decodes a BER encoded LDAPv3 ASN.1 message envelope structure
104 * from an input stream to build a fully populated Message object instance.
105 *
106 * @param lock
107 * lock object used to exclusively read from the input stream
108 * @param in
109 * the input stream to read PDU data from.
110 * @return the populated Message representing the PDU envelope.
111 * @throws MessageException
112 * if there is a problem decoding.
113 */
114 public Object decode( final Object lock, final InputStream in ) throws MessageException
115 {
116 Object providerEnvelope;
117
118 try
119 {
120 if ( lock == null )
121 {
122 // Complain here somehow first then do the following w/o synch!
123
124 // Call provider decoder to demarshall PDU into berlib specific form
125 providerEnvelope = decoder.decode( lock, in );
126 }
127 else
128 {
129 synchronized ( lock )
130 {
131 // Same as above but a synchronized read using valid lock object
132 providerEnvelope = decoder.decode( lock, in );
133 lock.notifyAll();
134 }
135 }
136 }
137 catch ( Exception e )
138 {
139 throw ( MessageException ) e;
140 }
141
142 // Call on transformer to convert stub based PDU into Message based PDU
143 return TwixTransformer.transform( providerEnvelope );
144 }
145
146
147 /**
148 * Decodes a chunk of stream data returning any resultant decoded PDU via a
149 * callback.
150 *
151 * @param chunk
152 * the chunk to decode
153 * @throws MessageException
154 * if there are failures while decoding the chunk
155 */
156 public void decode( Object chunk ) throws MessageException
157 {
158 try
159 {
160 decoder.decode( chunk );
161 }
162 catch ( DecoderException e )
163 {
164 // transform the DecoderException message to a MessageException
165 if ( e instanceof ResponseCarryingException )
166 {
167 ResponseCarryingMessageException rcme = new ResponseCarryingMessageException( e.getMessage() );
168 rcme.setResponse( ((ResponseCarryingException)e).getResponse() );
169
170 throw rcme;
171 }
172 else
173 {
174 // TODO : this is certainly not the way we should handle such an exception !
175 throw new ResponseCarryingMessageException( e.getMessage() );
176 }
177 }
178 }
179
180
181 /**
182 * Sets the callback used to deliver completly decoded PDU's.
183 *
184 * @param cb
185 * the callback to use for decoded PDU delivery
186 */
187 public void setCallback( DecoderCallback cb )
188 {
189 this.cb = cb;
190 }
191
192
193 /**
194 * Sets the monitor for this MessageDecoder which receives callbacks for
195 * noteworthy events during decoding.
196 *
197 * @param monitor
198 * the monitor to receive notifications via callback events
199 */
200 public void setDecoderMonitor( DecoderMonitor monitor )
201 {
202 decoder.setDecoderMonitor( monitor );
203 }
204
205
206 public Provider getProvider()
207 {
208 return this.provider;
209 }
210 }