001: /*
002: * $Id: AbstractTransformer.java 11316 2008-03-11 15:50:38Z dirk.olmes $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.transformer;
012:
013: import org.mule.api.MuleMessage;
014: import org.mule.api.endpoint.ImmutableEndpoint;
015: import org.mule.api.lifecycle.InitialisationException;
016: import org.mule.api.lifecycle.LifecycleTransitionResult;
017: import org.mule.api.transformer.Transformer;
018: import org.mule.api.transformer.TransformerException;
019: import org.mule.api.transport.MessageAdapter;
020: import org.mule.config.i18n.CoreMessages;
021: import org.mule.transport.NullPayload;
022: import org.mule.util.ClassUtils;
023: import org.mule.util.FileUtils;
024: import org.mule.util.StringMessageUtils;
025: import org.mule.util.StringUtils;
026:
027: import java.io.InputStream;
028: import java.util.Collections;
029: import java.util.List;
030:
031: import javax.xml.transform.stream.StreamSource;
032:
033: import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
034:
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037:
038: /**
039: * <code>AbstractTransformer</code> is a base class for all transformers.
040: * Transformations transform one object into another.
041: */
042:
043: public abstract class AbstractTransformer implements Transformer {
044: protected static final int DEFAULT_TRUNCATE_LENGTH = 200;
045:
046: protected final Log logger = LogFactory.getLog(getClass());
047:
048: /**
049: * The return type that will be returned by the {@link #transform} method is
050: * called
051: */
052: protected Class returnClass = Object.class;
053:
054: /**
055: * The name that identifies this transformer. If none is set the class name of
056: * the transformer is used
057: */
058: protected String name = null;
059:
060: /** The endpoint that this transformer instance is configured on */
061: protected ImmutableEndpoint endpoint = null;
062:
063: /**
064: * A list of supported Class types that the source payload passed into this
065: * transformer
066: */
067: protected final List sourceTypes = new CopyOnWriteArrayList();
068:
069: /**
070: * Determines whether the transformer will throw an exception if the message
071: * passed is is not supported or the return tye is incorrect
072: */
073: private boolean ignoreBadInput = false;
074:
075: /** default constructor required for discovery */
076: public AbstractTransformer() {
077: super ();
078: }
079:
080: protected Object checkReturnClass(Object object)
081: throws TransformerException {
082: if (returnClass != null) {
083: if (!returnClass.isInstance(object)) {
084: throw new TransformerException(CoreMessages
085: .transformUnexpectedType(object.getClass(),
086: returnClass), this );
087: }
088: }
089:
090: if (logger.isDebugEnabled()) {
091: logger
092: .debug("The transformed object is of expected type. Type is: "
093: + ClassUtils.getSimpleName(object
094: .getClass()));
095: }
096:
097: return object;
098: }
099:
100: protected void registerSourceType(Class aClass) {
101: if (!sourceTypes.contains(aClass)) {
102: sourceTypes.add(aClass);
103:
104: if (aClass.equals(Object.class)) {
105: logger
106: .debug("java.lang.Object has been added as source type for this transformer, there will be no source type checking performed");
107: }
108: }
109: }
110:
111: protected void unregisterSourceType(Class aClass) {
112: sourceTypes.remove(aClass);
113: }
114:
115: /** @return transformer name */
116: public String getName() {
117: if (name == null) {
118: name = this .generateTransformerName();
119: }
120: return name;
121: }
122:
123: /** @param string */
124: public void setName(String string) {
125: if (string == null) {
126: string = ClassUtils.getSimpleName(this .getClass());
127: }
128:
129: logger.debug("Setting transformer name to: " + string);
130: name = string;
131: }
132:
133: /*
134: * (non-Javadoc)
135: *
136: * @see org.mule.transformer.Transformer#getReturnClass()
137: */
138: public Class getReturnClass() {
139: return returnClass;
140: }
141:
142: /*
143: * (non-Javadoc)
144: *
145: * @see org.mule.transformer.Transformer#setReturnClass(java.lang.String)
146: */
147: public void setReturnClass(Class newClass) {
148: returnClass = newClass;
149: }
150:
151: public boolean isSourceTypeSupported(Class aClass) {
152: return isSourceTypeSupported(aClass, false);
153: }
154:
155: public boolean isSourceTypeSupported(Class aClass,
156: boolean exactMatch) {
157: int numTypes = sourceTypes.size();
158:
159: if (numTypes == 0) {
160: return !exactMatch;
161: }
162:
163: for (int i = 0; i < numTypes; i++) {
164: Class anotherClass = (Class) sourceTypes.get(i);
165: if (exactMatch) {
166: if (anotherClass.equals(aClass)) {
167: return true;
168: }
169: } else if (anotherClass.isAssignableFrom(aClass)) {
170: return true;
171: }
172: }
173:
174: return false;
175: }
176:
177: /**
178: * Transforms the object.
179: *
180: * @param src The source object to transform.
181: * @return The transformed object
182: */
183: public final Object transform(Object src)
184: throws TransformerException {
185: String encoding = null;
186:
187: Object payload = src;
188: MessageAdapter adapter;
189: if (src instanceof MessageAdapter) {
190: encoding = ((MessageAdapter) src).getEncoding();
191: adapter = (MessageAdapter) src;
192: if ((!isSourceTypeSupported(MessageAdapter.class, true)
193: && !isSourceTypeSupported(MuleMessage.class, true) && !(this instanceof AbstractMessageAwareTransformer))) {
194: src = ((MessageAdapter) src).getPayload();
195: payload = adapter.getPayload();
196: }
197: }
198:
199: if (encoding == null && endpoint != null) {
200: encoding = endpoint.getEncoding();
201: } else if (encoding == null) {
202: encoding = FileUtils.DEFAULT_ENCODING;
203: }
204:
205: Class srcCls = src.getClass();
206: if (!isSourceTypeSupported(srcCls)) {
207: if (ignoreBadInput) {
208: logger
209: .debug("Source type is incompatible with this transformer and property 'ignoreBadInput' is set to true, so the transformer chain will continue.");
210: return payload;
211: } else {
212: throw new TransformerException(CoreMessages
213: .transformOnObjectUnsupportedTypeOfEndpoint(
214: this .getName(), payload.getClass(),
215: endpoint), this );
216: }
217: }
218:
219: if (logger.isDebugEnabled()) {
220: logger.debug("Applying transformer " + getName() + " ("
221: + getClass().getName() + ")");
222: logger.debug("Object before transform: "
223: + StringMessageUtils.truncate(StringMessageUtils
224: .toString(payload),
225: DEFAULT_TRUNCATE_LENGTH, false));
226: }
227:
228: Object result;
229: result = doTransform(payload, encoding);
230: // }
231: if (result == null) {
232: result = NullPayload.getInstance();
233: }
234:
235: if (logger.isDebugEnabled()) {
236: logger.debug("Object after transform: "
237: + StringMessageUtils.truncate(StringMessageUtils
238: .toString(result), DEFAULT_TRUNCATE_LENGTH,
239: false));
240: }
241:
242: result = checkReturnClass(result);
243: return result;
244: }
245:
246: protected boolean isConsumed(Class srcCls) {
247: return InputStream.class.isAssignableFrom(srcCls)
248: || StreamSource.class.isAssignableFrom(srcCls);
249: }
250:
251: public ImmutableEndpoint getEndpoint() {
252: return endpoint;
253: }
254:
255: /*
256: * (non-Javadoc)
257: *
258: * @see org.mule.api.transformer.Transformer#setConnector(org.mule.api.transport.Connector)
259: */
260: public void setEndpoint(ImmutableEndpoint endpoint) {
261: this .endpoint = endpoint;
262: }
263:
264: protected abstract Object doTransform(Object src, String encoding)
265: throws TransformerException;
266:
267: /**
268: * Template method were deriving classes can do any initialisation after the
269: * properties have been set on this transformer
270: *
271: * @throws InitialisationException
272: */
273: public LifecycleTransitionResult initialise()
274: throws InitialisationException {
275: return LifecycleTransitionResult.OK;
276: }
277:
278: protected String generateTransformerName() {
279: String name = ClassUtils.getSimpleName(this .getClass());
280: int i = name.indexOf("To");
281: if (i > 0 && returnClass != null) {
282: String target = ClassUtils.getSimpleName(returnClass);
283: if (target.equals("byte[]")) {
284: target = "byteArray";
285: }
286: name = name.substring(0, i + 2)
287: + StringUtils.capitalize(target);
288: }
289: return name;
290: }
291:
292: public List getSourceTypes() {
293: return Collections.unmodifiableList(sourceTypes);
294: }
295:
296: public boolean isIgnoreBadInput() {
297: return ignoreBadInput;
298: }
299:
300: public void setIgnoreBadInput(boolean ignoreBadInput) {
301: this .ignoreBadInput = ignoreBadInput;
302: }
303:
304: // @Override
305: public String toString() {
306: StringBuffer sb = new StringBuffer(80);
307: sb.append(ClassUtils.getSimpleName(this .getClass()));
308: sb.append("{this=").append(
309: Integer.toHexString(System.identityHashCode(this )));
310: sb.append(", name='").append(name).append('\'');
311: sb.append(", ignoreBadInput=").append(ignoreBadInput);
312: sb.append(", returnClass=").append(returnClass);
313: sb.append(", sourceTypes=").append(sourceTypes);
314: sb.append('}');
315: return sb.toString();
316: }
317:
318: public boolean isAcceptNull() {
319: return false;
320: }
321:
322: }
|