001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.jndi.provider.ldap;
019:
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.List;
025:
026: import javax.naming.ldap.Control;
027:
028: import org.apache.harmony.jndi.provider.ldap.asn1.ASN1Decodable;
029: import org.apache.harmony.jndi.provider.ldap.asn1.ASN1Encodable;
030: import org.apache.harmony.jndi.provider.ldap.asn1.LdapASN1Constant;
031: import org.apache.harmony.jndi.provider.ldap.asn1.ASN1ChoiceWrap.ChosenValue;
032: import org.apache.harmony.security.asn1.ASN1Integer;
033:
034: /**
035: * Ldap Message. Refer to {@link http://www.rfc-editor.org/rfc/rfc2251.txt} for
036: * detailed information
037: *
038: */
039: public class LdapMessage implements ASN1Encodable, ASN1Decodable {
040:
041: /**
042: * operation request which could be encoded using ASN.1 BER
043: */
044: private ASN1Encodable requestOp;
045:
046: /**
047: * operation response operation which could be decoded using ASN.1 BER
048: */
049: private ASN1Decodable responseOp;
050:
051: /**
052: * controls for this message
053: */
054: private Control[] controls;
055:
056: /**
057: * index of the operation, determine which operation is encapsulated in this
058: * message.
059: */
060: private int opIndex;
061:
062: /**
063: * unique request id for each session
064: */
065: private int messageId;
066:
067: private static int nextMessageId = 1;
068:
069: /**
070: * Get next unique message id
071: *
072: * @return the next unique message id
073: */
074: public static synchronized int getNextMessageId() {
075: return nextMessageId++;
076: }
077:
078: /**
079: * Get message id of this message
080: *
081: * @return id of this message
082: */
083: public int getMessageId() {
084: return messageId;
085: }
086:
087: /**
088: * Construct a request message. <code>op</code> may not be
089: * <code>null</code>. <code>controls</code> is <code>null</code> or a
090: * array of zero length means there is no control for this message.
091: *
092: * @param opIndex
093: * request index to indicate which operation is encapsulated
094: * @param op
095: * encodable operation
096: * @param controls
097: * message controls
098: */
099: public LdapMessage(int opIndex, ASN1Encodable op, Control[] controls) {
100: this .opIndex = opIndex;
101: requestOp = op;
102: this .controls = controls;
103: messageId = getNextMessageId();
104: }
105:
106: /**
107: * Construct a response message. <code>op</code> indicate which operation
108: * to be used, and the message would be initialized after calling
109: * <code>decode(byte[])</code> or <code>decode(InputStream)</code>
110: * method.
111: *
112: * @param op
113: * response index to indicate which operation to be encapsulated
114: */
115: public LdapMessage(ASN1Decodable op) {
116: responseOp = op;
117: opIndex = -1;
118: messageId = -1;
119: }
120:
121: /**
122: * Encode this message using ASN.1 Basic Encoding Rules (BER)
123: *
124: * @return the encoded values of this <code>LdapMessage</code> instance
125: */
126: public byte[] encode() {
127: return LdapASN1Constant.LDAPMessage.encode(this );
128: }
129:
130: /**
131: * Decode values from <code>InputStream</code> using ASN.1 BER, and the
132: * decoded values will initialize this <code>LdapMessage</code> instance.
133: *
134: * @param in
135: *
136: * @throws IOException
137: * error occurs when decoding
138: */
139: public void decode(InputStream in) throws IOException {
140: Object[] values = (Object[]) LdapASN1Constant.LDAPMessage
141: .decode(in);
142: decodeValues(values);
143: }
144:
145: /**
146: * Return controls of the message, if there is no control, <code>null</code>
147: * will be returned.
148: *
149: * @return controls of the message
150: */
151: public Control[] getControls() {
152: return controls;
153: }
154:
155: @SuppressWarnings("unchecked")
156: public void decodeValues(Object[] values) {
157: messageId = ASN1Integer.toIntValue(values[0]);
158: if (values[1] == null) {
159: return;
160: }
161:
162: ChosenValue chosen = (ChosenValue) values[1];
163: opIndex = chosen.getIndex();
164: // failed to retrieve responseOp
165: responseOp = getResponseOp();
166: if (responseOp == null) {
167: return;
168: }
169:
170: if (opIndex == LdapASN1Constant.OP_SEARCH_RESULT_DONE
171: || opIndex == LdapASN1Constant.OP_SEARCH_RESULT_ENTRY
172: || opIndex == LdapASN1Constant.OP_SEARCH_RESULT_REF) {
173: /*
174: * we use LdapSearchResult to decode all types of search responses,
175: * so we need index to determine which type of response to decode
176: */
177: responseOp.decodeValues(new Object[] { chosen });
178: } else {
179: responseOp.decodeValues((Object[]) chosen.getValue());
180: }
181:
182: if (values[2] != null) {
183: Collection<Object[]> list = (Collection<Object[]>) values[2];
184: controls = new Control[list.size()];
185: int index = 0;
186: for (Object[] objects : list) {
187: LdapControl lcontrol = new LdapControl();
188: lcontrol.decodeValues(objects);
189: controls[index++] = lcontrol.getControl();
190: }
191: }
192: }
193:
194: public void encodeValues(Object[] values) {
195: values[0] = ASN1Integer.fromIntValue(messageId);
196: // ABANDON, UNBIND and DELETE request are ASN.1 primitive
197: if (opIndex == LdapASN1Constant.OP_ABANDON_REQUEST
198: || opIndex == LdapASN1Constant.OP_DEL_REQUEST
199: || opIndex == LdapASN1Constant.OP_UNBIND_REQUEST) {
200: Object[] objs = new Object[1];
201: requestOp.encodeValues(objs);
202: values[1] = new ChosenValue(opIndex, objs[0]);
203: } else {
204: values[1] = new ChosenValue(opIndex, requestOp);
205: }
206:
207: // encode controls, wrap to LdapControl, so it could be encoded
208: if (controls != null) {
209: List<LdapControl> list = new ArrayList<LdapControl>(
210: controls.length);
211: for (int i = 0; i < controls.length; ++i) {
212: list.add(new LdapControl(controls[i]));
213: }
214: values[2] = list;
215: }
216: }
217:
218: /**
219: * Get index of the operation, determine which operation is encapsulated in
220: * this message. If this <code>LdapMessage</code> instance is not initial,
221: * <code>-1</code> will be returned.
222: *
223: * @return index of the operation encapsulated in the message
224: */
225: public int getOperationIndex() {
226: return opIndex;
227: }
228:
229: public ASN1Decodable getResponseOp() {
230: return responseOp;
231: }
232:
233: public ASN1Encodable getRequestOp() {
234: return requestOp;
235: }
236:
237: }
|