001: /*
002: * The contents of this file are subject to the Sapient Public License
003: * Version 1.0 (the "License"); you may not use this file except in compliance
004: * with the License. You may obtain a copy of the License at
005: * http://carbon.sf.net/License.html.
006: *
007: * Software distributed under the License is distributed on an "AS IS" basis,
008: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
009: * the specific language governing rights and limitations under the License.
010: *
011: * The Original Code is The Carbon Component Framework.
012: *
013: * The Initial Developer of the Original Code is Sapient Corporation
014: *
015: * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
016: */
017:
018: package org.sape.carbon.services.config.jndi;
019:
020: import java.io.ByteArrayInputStream;
021: import java.io.ByteArrayOutputStream;
022: import java.io.InputStream;
023: import java.io.OutputStream;
024:
025: import javax.naming.Name;
026: import javax.naming.NameNotFoundException;
027: import javax.naming.NamingException;
028: import javax.naming.directory.Attribute;
029: import javax.naming.directory.Attributes;
030: import javax.naming.directory.BasicAttributes;
031: import javax.naming.directory.DirContext;
032: import javax.naming.event.EventContext;
033: import javax.naming.event.NamespaceChangeListener;
034: import javax.naming.event.NamingEvent;
035: import javax.naming.event.NamingExceptionEvent;
036: import javax.naming.event.ObjectChangeListener;
037:
038: import org.sape.carbon.core.config.InvalidConfigurationException;
039: import org.sape.carbon.core.config.format.ConfigurationFormatService;
040: import org.sape.carbon.core.config.node.AbstractConfigurationDocument;
041: import org.sape.carbon.core.config.node.Node;
042: import org.sape.carbon.core.config.node.NodeRemovalException;
043: import org.sape.carbon.core.exception.ExceptionUtility;
044: import org.sape.carbon.core.exception.InvalidParameterException;
045:
046: import org.apache.commons.logging.Log;
047: import org.apache.commons.logging.LogFactory;
048:
049: /**
050: * A node that represents a JNDI context that contains content.
051: * Content is read and writen using a <code>ConfigurationFormatService</code>.
052: * This implementation implements NamespaceChangeListener and
053: * ObjectChangeListener from the javax.naming.event package in order to listen
054: * for changes in the backing datastore.
055: *
056: * Copyright 2003 Sapient
057: * @since carbon 2.0
058: * @author Douglas Voet, March 2003
059: * @version $Revision: 1.11 $($Author: dvoet $ / $Date: 2003/05/05 21:21:10 $)
060: */
061: public class JNDIConfigurationDocument extends
062: AbstractConfigurationDocument implements
063: NamespaceChangeListener, ObjectChangeListener {
064:
065: private Log log = LogFactory.getLog(this .getClass());
066:
067: private DirContext initialContext;
068: private Name nodeContextName;
069: private JNDILinkNodeConfiguration config;
070: private String documentType;
071:
072: /**
073: * Constructs the JNDIConfigurationDocument and registers itself as a
074: * JNDI naming listener
075: *
076: * @param parent the parent node of this node
077: * @param name the name of this node
078: * @param formatter the format service this node should use
079: * @param initialContext the jndi initial context
080: * @param nodeContextName the name of this node's context
081: * @param config configuration used to get the names of the attributes
082: * that hold node name, node and document type, and document content as well
083: * as valid attribute values for node and document type.
084: */
085: public JNDIConfigurationDocument(Node parent, String name,
086: ConfigurationFormatService formatter,
087: DirContext initialContext, Name nodeContextName,
088: JNDILinkNodeConfiguration config, String documentType) {
089:
090: super (parent, name, formatter);
091:
092: // validate parameters
093: if (initialContext == null) {
094: throw new InvalidParameterException(this .getClass(),
095: "initialContext cannot be null in node ["
096: + getAbsoluteName() + "]");
097: }
098: if (nodeContextName == null) {
099: throw new InvalidParameterException(this .getClass(),
100: "nodeContextName cannot be null in node ["
101: + getAbsoluteName() + "]");
102: }
103: if (config == null) {
104: throw new InvalidParameterException(this .getClass(),
105: "config cannot be null in node ["
106: + getAbsoluteName() + "]");
107: }
108: if (documentType == null) {
109: throw new InvalidParameterException(this .getClass(),
110: "documentType cannot be null in node ["
111: + getAbsoluteName() + "]");
112: }
113:
114: this .initialContext = initialContext;
115: this .nodeContextName = nodeContextName;
116: this .config = config;
117: this .documentType = documentType;
118:
119: // register for JNDI events for this context
120: registerNamingListener();
121: }
122:
123: /**
124: * Calls destroySubcontext with this context's name
125: * @throws NodeRemovalException if a NamingException is encountered
126: */
127: protected void destroyBackingData() throws NodeRemovalException {
128: try {
129: this .initialContext.destroySubcontext(this .nodeContextName);
130: } catch (NamingException ne) {
131: throw new NodeRemovalException(this .getClass(), this , ne);
132: }
133: }
134:
135: /**
136: * Gets the content from the JNDI directory and converts it to a stream
137: *
138: * @throws NamingException if content cannot be read from the jndi directory
139: * @throws InvalidConfigurationException if there is no content in this
140: * nodes context
141: */
142: protected InputStream openInputStream() throws NamingException {
143: String[] contentAttirbuteIDs = new String[] { this .config
144: .getDocumentContectAttributeName() };
145:
146: // get the document's content attribute
147: Attributes documentAttributes = this .initialContext
148: .getAttributes(this .nodeContextName,
149: contentAttirbuteIDs);
150:
151: Attribute contentAttribute = documentAttributes.get(config
152: .getDocumentContectAttributeName());
153:
154: if (contentAttribute == null || contentAttribute.get() == null) {
155: throw new InvalidConfigurationException(this .getClass(),
156: getAbsoluteName(), "",
157: "content was missing from JNDI context: ["
158: + this .nodeContextName.toString() + "]");
159: }
160:
161: // get the actual content and convert create a ByteArrayInputStream
162: String content = (String) contentAttribute.get();
163: return new ByteArrayInputStream(content.getBytes());
164: }
165:
166: /**
167: * Returns a new ByteArrayOutputStream that is read when it is closed
168: */
169: protected OutputStream openOutputStream() {
170: return new ByteArrayOutputStream();
171: }
172:
173: /**
174: * Gets the data from the output stream and writes it to the jndi directory
175: * @throws NamingException if there is a problem writing to the directory
176: */
177: protected void closeOutputStream(OutputStream out) throws Exception {
178: ByteArrayOutputStream outputStream = (ByteArrayOutputStream) out;
179:
180: // these constants are defined in order to make the code below readable
181: final String NAME = this .config.getNodeNameAttributeName();
182: final String NODE_TYPE = this .config.getNodeTypeAttributeName();
183: final String DOCUMENT = this .config
184: .getDocumentNodeTypeAttributeValue();
185: final String DOC_TYPE = this .config
186: .getDocumentTypeAttributeName();
187: final String CONTENT = this .config
188: .getDocumentContectAttributeName();
189:
190: try {
191: // Replace the content of the backing context with the string value
192: // of the ByteArrayOutputStream
193: this .initialContext.modifyAttributes(this .nodeContextName,
194: DirContext.REPLACE_ATTRIBUTE, new BasicAttributes(
195: CONTENT, outputStream.toString()));
196:
197: } catch (NameNotFoundException nnfe) {
198: // the name was not found, so create it
199: if (log.isTraceEnabled()) {
200: log.trace("Creating new context ["
201: + this .nodeContextName + "]");
202: }
203:
204: Attributes docAttributes = new BasicAttributes();
205: docAttributes.put(NAME, getName());
206: docAttributes.put(NODE_TYPE, DOCUMENT);
207: docAttributes.put(DOC_TYPE, this .documentType);
208: docAttributes.put(CONTENT, outputStream.toString());
209:
210: initialContext.createSubcontext(nodeContextName,
211: docAttributes);
212:
213: registerNamingListener();
214: }
215:
216: super .closeOutputStream(out);
217: }
218:
219: /**
220: * Checks the backing directory to see if this nodes context exists and
221: * if it does, has the right attributes.
222: */
223: protected boolean backingDataExists() {
224: final String NODE_TYPE = this .config.getNodeTypeAttributeName();
225: final String DOCUMENT = this .config
226: .getDocumentNodeTypeAttributeValue();
227:
228: boolean backingDataExists;
229: try {
230: Attributes documentAttributes = this .initialContext
231: .getAttributes(this .nodeContextName);
232: if (documentAttributes.get(NODE_TYPE) != null
233: && documentAttributes.get(NODE_TYPE).contains(
234: DOCUMENT)) {
235:
236: backingDataExists = true;
237: } else {
238: backingDataExists = false;
239: }
240: } catch (NamingException ne) {
241: backingDataExists = false;
242: }
243:
244: return backingDataExists;
245: }
246:
247: /**
248: * This implementation does not do anything if an object is added.
249: */
250: public void objectAdded(NamingEvent evt) {
251: // don't care
252: }
253:
254: /**
255: * Causes a refresh of this node
256: */
257: public void objectRemoved(NamingEvent evt) {
258: if (log.isTraceEnabled()) {
259: log.trace("Caught objectRemoved naming event on node ["
260: + getAbsoluteName() + "]");
261: }
262: refresh();
263: }
264:
265: /**
266: * Causes a refresh of this node
267: */
268: public void objectRenamed(NamingEvent evt) {
269: if (log.isTraceEnabled()) {
270: log.trace("Caught objectRenamed naming event on node ["
271: + getAbsoluteName() + "]");
272: }
273: refresh();
274: }
275:
276: /**
277: * Causes a refresh of this node
278: */
279: public void objectChanged(NamingEvent evt) {
280: if (log.isTraceEnabled()) {
281: log.trace("Caught objectChanged naming event on node ["
282: + getAbsoluteName() + "]");
283: }
284: refresh();
285: }
286:
287: /**
288: * Logs the fact that a NamingExceptionEvent occured
289: */
290: public void namingExceptionThrown(NamingExceptionEvent evt) {
291: if (log.isInfoEnabled()) {
292: log
293: .info("Notified of NamingExceptionEvent, this node is no "
294: + "longer listening for JNDI events, node name: ["
295: + getAbsoluteName()
296: + "], context name: ["
297: + this .nodeContextName
298: + "], naming exception: "
299: + ExceptionUtility
300: .printStackTracesToString(evt
301: .getException()));
302: }
303: }
304:
305: /**
306: * Registers this node as a NamingListener with the backing context
307: */
308: protected void registerNamingListener() {
309: try {
310: // get the event context
311: EventContext this Context = (EventContext) this .initialContext
312: .lookup(this .nodeContextName);
313:
314: // register for changes in object scope (this context only)
315: this Context.addNamingListener("",
316: EventContext.OBJECT_SCOPE, this );
317:
318: } catch (ClassCastException cce) {
319: if (log.isInfoEnabled()) {
320: log
321: .info("Node was not registered as a JNDI NamingListener because "
322: + "events were not supported by directory, node: ["
323: + getAbsoluteName()
324: + "], context name: ["
325: + this .nodeContextName + "]");
326: }
327: } catch (NamingException ne) {
328: if (log.isDebugEnabled()) {
329: log.debug(
330: "NamingException caught registering NamingListener, node: ["
331: + getAbsoluteName()
332: + "], context name: ["
333: + this .nodeContextName + "]", ne);
334: }
335: }
336: }
337: }
|