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: package org.apache.cocoon.components.naming;
018:
019: import java.util.HashMap;
020: import java.util.Hashtable;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: import javax.naming.Context;
026: import javax.naming.NamingEnumeration;
027: import javax.naming.NamingException;
028: import javax.naming.directory.Attribute;
029: import javax.naming.directory.Attributes;
030: import javax.naming.directory.BasicAttribute;
031: import javax.naming.directory.BasicAttributes;
032: import javax.naming.directory.DirContext;
033: import javax.naming.directory.InitialDirContext;
034: import javax.naming.directory.SearchResult;
035:
036: import org.apache.avalon.excalibur.pool.Recyclable;
037: import org.apache.avalon.framework.activity.Disposable;
038: import org.apache.avalon.framework.logger.AbstractLogEnabled;
039: import org.apache.avalon.framework.parameters.ParameterException;
040: import org.apache.avalon.framework.parameters.Parameterizable;
041: import org.apache.avalon.framework.parameters.Parameters;
042: import org.apache.cocoon.ProcessingException;
043:
044: /**
045: * The <code>LDAPEntryManager</code> is an Avalon Component for managing Entries in a Javax Naming Directory.
046: * This is the LDAP implementation of the {@link org.apache.cocoon.components.naming.EntryManager EntryManager} interface.
047: * This is designed to be used from FlowScript, it uses Maps instead of NamingEnumerations and Attributes.
048: * @author Jeremy Quinn <a href="http://apache.org/~jeremy">http://apache.org/~jeremy</a>.
049: *
050: * Example configuration (goes in cocoon.xconf)
051: * <pre><tt>
052: * <component role="org.apache.cocoon.component.EntryManager" class="org.apache.cocoon.components.naming.LDAPEntryManager" logger="flow.ldap">
053: * <parameter name="ldap-host" value="ldap://hostname:port"/>
054: * <parameter name="ldap-base" value="dc=example,dc=com"/>
055: * <parameter name="ldap-user" value="username"/>
056: * <parameter name="ldap-pass" value="password"/>
057: * </component>
058: * </tt></pre></p>
059: */
060:
061: public class LDAPEntryManager extends AbstractLogEnabled implements
062: EntryManager, Parameterizable, Disposable, Recyclable {
063:
064: /* congiguration parameter names */
065: protected final static String LDAP_HOST_PARAM = "ldap-host";
066: protected final static String LDAP_USER_PARAM = "ldap-user";
067: protected final static String LDAP_PASS_PARAM = "ldap-pass";
068: protected final static String LDAP_BASE_PARAM = "ldap-base";
069:
070: /* internal state */
071: private boolean disposed = false;
072:
073: /* internal instance variables */
074: protected DirContext context = null;
075: protected Hashtable environment = null;
076:
077: /* default constructor */
078: public LDAPEntryManager() {
079: }
080:
081: /** Avalon, Parameterize this Class */
082: public void parameterize(Parameters params)
083: throws ParameterException {
084: if (getLogger().isDebugEnabled()) {
085: getLogger().debug("LDAPEntryManager parameterizing");
086: }
087: if (this .environment == null) {
088: String host = params.getParameter(LDAP_HOST_PARAM);
089: if (getLogger().isDebugEnabled()) {
090: getLogger().debug("LDAP using host: " + host);
091: }
092: String base = params.getParameter(LDAP_BASE_PARAM);
093: if (getLogger().isDebugEnabled()) {
094: getLogger().debug("LDAP using base: " + base);
095: }
096: String user = params.getParameter(LDAP_USER_PARAM);
097: if (getLogger().isDebugEnabled()) {
098: getLogger().debug("LDAP using user: " + user);
099: }
100: String pass = params.getParameter(LDAP_PASS_PARAM);
101: if (getLogger().isDebugEnabled()) {
102: getLogger().debug("LDAP using pass: " + pass);
103: }
104: this .environment = new Hashtable();
105: this .environment.put(Context.INITIAL_CONTEXT_FACTORY,
106: "com.sun.jndi.ldap.LdapCtxFactory");
107: this .environment.put(Context.PROVIDER_URL, host + "/"
108: + base);
109: if (user != null) {
110: this .environment.put(Context.SECURITY_AUTHENTICATION,
111: "simple");
112: this .environment.put(Context.SECURITY_PRINCIPAL, user);
113: this .environment
114: .put(Context.SECURITY_CREDENTIALS, pass);
115: }
116: }
117: }
118:
119: /* Avalon, Recycle this Class */
120: public final void recycle() {
121: if (getLogger().isDebugEnabled()) {
122: getLogger().debug("LDAPEntryManager recycling");
123: }
124: try {
125: this .context.close();
126: } catch (Exception e) {
127: getLogger().error(
128: "LDAPEntryManager.recycle() :" + e.getMessage());
129: } finally {
130: this .context = null;
131: }
132: }
133:
134: /* Avalon, Dispose of this Class */
135: public final void dispose() {
136: if (getLogger().isDebugEnabled()) {
137: getLogger().debug("LDAPEntryManager disposing");
138: }
139: try {
140: if (context != null) {
141: this .context.close();
142: }
143: } catch (Exception e) {
144: getLogger().error(
145: "LDAPEntryManager.recycle() :" + e.getMessage());
146: } finally {
147: this .context = null;
148: this .environment = null;
149: this .disposed = true;
150: }
151: }
152:
153: /* lazy initialise this class */
154: protected void initialize() throws Exception {
155: if (getLogger().isDebugEnabled()) {
156: getLogger().debug("LDAPEntryManager initialising");
157: }
158: if (null == this .environment) {
159: throw new IllegalStateException(
160: "LDAPEntryManager.initialize() : Not Configured");
161: }
162: if (this .disposed) {
163: throw new IllegalStateException(
164: "LDAPEntryManager.initialize() : Already disposed");
165: }
166: try {
167: this .context = new InitialDirContext(this .environment);
168: if (getLogger().isDebugEnabled()) {
169: getLogger().debug(
170: "LDAPEntryManager new context: "
171: + this .context.getNameInNamespace());
172: }
173: } catch (Exception e) {
174: getLogger().error(
175: "LDAPEntryManager.initialize()" + e.getMessage());
176: return;
177: }
178: }
179:
180: /**
181: * Creates a new Entry
182: *
183: * @param name The name of the Entry to create
184: * @param attributes The Map of Attributes to create it with
185: */
186: public void create(String name, Map attributes)
187: throws ProcessingException {
188: Context newContext = null;
189: try {
190: if (this .context == null)
191: initialize();
192: if (getLogger().isDebugEnabled()) {
193: getLogger().debug(
194: "LDAPEntryManager creating new Context: "
195: + name);
196: }
197: newContext = context.createSubcontext(name,
198: map2Attributes(attributes));
199: } catch (Exception e) {
200: getLogger().error(
201: "LDAPEntryManager.create() :" + e.getMessage());
202: throw new ProcessingException(e);
203: } finally {
204: try {
205: if (newContext != null)
206: newContext.close();
207: } catch (NamingException ne) {
208: throw new ProcessingException(ne);
209: }
210: }
211: }
212:
213: /**
214: * Retrieves a named Entry's Attributes
215: *
216: * @param name The name of the Entry to modify
217: * @return a Map of the Attributes
218: */
219: public Map get(String name) throws ProcessingException {
220: try {
221: if (this .context == null)
222: initialize();
223: if (getLogger().isDebugEnabled()) {
224: getLogger().debug(
225: "LDAPEntryManager retrieving Entry: " + name);
226: }
227: return attributes2Map(context.getAttributes(name));
228: } catch (Exception e) {
229: getLogger().error(
230: "LDAPEntryManager.get() :" + e.getMessage());
231: throw new ProcessingException(e);
232: }
233: }
234:
235: /**
236: * Finds Entries based on matching their Attributes
237: *
238: * @param attributes The Attributes to match
239: * @return a Map of the results, each with a Map of their Attributes
240: */
241: public Map find(Map attributes) throws ProcessingException {
242: return find("", attributes);
243: }
244:
245: /**
246: * Finds Entries based on their Attributes
247: *
248: * @param cntx The sub-context to search
249: * @param attributes The Attributes to match
250: * @return a Map of the results, each with a Map of their Attributes
251: */
252: public Map find(String cntx, Map attributes)
253: throws ProcessingException {
254: try {
255: if (this .context == null)
256: initialize();
257: if (getLogger().isDebugEnabled()) {
258: getLogger().debug(
259: "LDAPEntryManager finding Entries in: " + cntx);
260: }
261: return namingEnumeration2Map(context.search(cntx,
262: map2Attributes(attributes)));
263: } catch (Exception e) {
264: getLogger().error(
265: "LDAPEntryManager.find() :" + e.getMessage());
266: throw new ProcessingException(e);
267: }
268: }
269:
270: /**
271: * Modifies an existing Entry
272: *
273: * @param name The name of the Entry to modify
274: * @param mod_op The modification mode to use
275: * @param attributes The Map of modifications
276: */
277: public void modify(String name, int mod_op, Map attributes)
278: throws ProcessingException {
279: try {
280: if (this .context == null)
281: initialize();
282: if (getLogger().isDebugEnabled()) {
283: getLogger().debug(
284: "LDAPEntryManager modifying Entry: " + name);
285: }
286: context.modifyAttributes(name, mod_op,
287: map2Attributes(attributes));
288: } catch (Exception e) {
289: getLogger().error(
290: "LDAPEntryManager.modify() :" + e.getMessage());
291: throw new ProcessingException(e);
292: }
293: }
294:
295: /**
296: * Deletes an Entry
297: *
298: * @param name The name of the Entry to delete
299: */
300: public void remove(String name) throws ProcessingException {
301: try {
302: if (this .context == null)
303: initialize();
304: if (getLogger().isDebugEnabled()) {
305: getLogger().debug(
306: "LDAPEntryManager destorying Context: " + name);
307: }
308: context.destroySubcontext(name);
309: } catch (Exception e) {
310: getLogger().error(
311: "LDAPEntryManager.deleteSubcontext() :"
312: + e.getMessage());
313: throw new ProcessingException(e);
314: }
315: }
316:
317: /*
318: Converts an Attributes Enumeration into a Map of those Attributes
319: Should be easier to manupulate in FlowScript and display in JXTemplate
320: Keep in mind that because there can be many entries for each Attribute
321: we store each value of the Map as an Array
322: */
323: private Map attributes2Map(Attributes attributes)
324: throws NamingException {
325: Map map = new HashMap();
326: for (NamingEnumeration atts = attributes.getAll(); atts
327: .hasMore();) {
328: Attribute attr = (Attribute) atts.next();
329: String id = attr.getID();
330: List val = new java.util.ArrayList();
331: NamingEnumeration vals = attr.getAll();
332: while (vals.hasMore()) {
333: val.add(vals.next());
334: }
335: map.put(id, val);
336: }
337: return map;
338: }
339:
340: /*
341: Converts a Map into an Enumeration of Attributes
342: Should be easier to provide from FlowScript
343: */
344: private Attributes map2Attributes(Map map) {
345: Attributes attrs = new BasicAttributes(false);
346: Iterator keys = map.keySet().iterator();
347: while (keys.hasNext()) {
348: String key = (String) keys.next();
349: Iterator vals = ((List) map.get(key)).iterator();
350: Attribute attr = new BasicAttribute(key);
351: while (vals.hasNext()) {
352: attr.add(vals.next());
353: }
354: attrs.put(attr);
355: }
356: return attrs;
357: }
358:
359: /*
360: Converts a NamingEnumeration into a Map of those Entries, with Attributes
361: Should be easier to manupulate in FlowScript and display in JXTemplate
362: */
363: private Map namingEnumeration2Map(NamingEnumeration enumeration)
364: throws NamingException {
365: Map map = new HashMap();
366: while (enumeration.hasMore()) {
367: SearchResult sr = (SearchResult) enumeration.next();
368: map.put(sr.getName(), attributes2Map(sr.getAttributes()));
369: }
370: return map;
371: }
372: }
|