View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.stdio;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.MuleException;
11  import org.mule.api.MuleMessage;
12  import org.mule.api.endpoint.ImmutableEndpoint;
13  import org.mule.api.lifecycle.InitialisationException;
14  import org.mule.api.transport.Connector;
15  import org.mule.config.i18n.MessageFactory;
16  import org.mule.util.StringUtils;
17  
18  import java.io.InputStream;
19  import java.io.OutputStream;
20  
21  /**
22   * <code>PromptStdioConnector</code> connects to the System streams in and out by
23   * default and add some basic fuctionality for writing out prompt messages.
24   */
25  public class PromptStdioConnector extends StdioConnector
26  {   
27      private String promptMessage;
28      private String promptMessageCode = null;
29      private String resourceBundle = null;
30      private String outputMessage;
31      private String outputMessageCode = null;
32      private long messageDelayTime = 3000;
33      private boolean firstTime = true;
34  
35      public PromptStdioConnector(MuleContext context)
36      {
37          super(context);
38          
39          inputStream = System.in;
40          outputStream = System.out;
41      }
42  
43      protected void doInitialise() throws InitialisationException
44      {
45          // We need to use the same classloder that creates and initalizes this
46          // connector when looking for resources
47          StdioMessageFactory stdioMessageFactory = new StdioMessageFactory(Thread.currentThread()
48              .getContextClassLoader());
49  
50          // Load messages from resource bundle if resourceBundle and
51          // promptMessageCode are both set
52          if (StringUtils.isNotBlank(resourceBundle) && StringUtils.isNotBlank(promptMessageCode))
53          {
54              promptMessage = stdioMessageFactory.getString(resourceBundle, promptMessageCode);
55          }
56          if (StringUtils.isNotBlank(resourceBundle) && StringUtils.isNotBlank(outputMessageCode))
57          {
58              outputMessage = stdioMessageFactory.getString(resourceBundle, outputMessageCode);
59          }
60      }
61  
62      protected void doDispose()
63      {
64          // Override as a no-op.
65          // The reason is System.in/out shouldn't be closed.
66          // It is valid for them to remain open (consider, e.g. tail -F).
67          // Trying to close System.in will result in I/O block, and
68          // available() will always return 0 bytes for System.in.
69  
70          // There is a scheme to get a ref to System.in via NIO,
71          // e.g. :
72          // FileInputStream fis = new FileInputStream(FileDescriptor.in);
73          // InputStream is = Channels.newInputStream(fis.getChannel);
74          //
75          // It is then possible to register a watchdog thread for the caller
76          // which will interrupt this (now wrapped with NIO) read() call.
77  
78          // Well, it isn't absolutely required for the reasons stated above,
79          // just following the KISS principle.
80      }
81  
82      protected void doConnect() throws Exception
83      {
84          // template method
85      }
86  
87      protected void doDisconnect() throws Exception
88      {
89          // template method
90      }
91  
92      public InputStream getInputStream()
93      {
94          return inputStream;
95      }
96  
97      public void doStart()
98      {
99          firstTime = false;
100     }
101 
102     public OutputStream getOutputStream()
103     {
104         return outputStream;
105     }
106 
107     /**
108      * @return Returns the promptMessage.
109      */
110     public String getPromptMessage()
111     {
112         return promptMessage;
113     }
114 
115     /**
116      * @param promptMessage The promptMessage to set.
117      */
118     public void setPromptMessage(String promptMessage)
119     {
120         this.promptMessage = promptMessage;
121     }
122 
123     /**
124      * @return Returns the promptMessageCode.
125      */
126     public String getPromptMessageCode()
127     {
128         return promptMessageCode;
129     }
130 
131     /**
132      * @param promptMessageCode The promptMessageCode to set.
133      */
134     public void setPromptMessageCode(String promptMessageCode)
135     {
136         this.promptMessageCode = promptMessageCode;
137     }
138 
139     /**
140      * @return Returns the resourceBundle.
141      */
142     public String getResourceBundle()
143     {
144         return resourceBundle;
145     }
146 
147     /**
148      * @param resourceBundle The resourceBundle to read the message from. This property is 
149      * only needed in conjunction with promptMessageCode or outputMessageCode.
150      */
151     public void setResourceBundle(String resourceBundle)
152     {
153         this.resourceBundle = resourceBundle;
154     }
155 
156     /**
157      * @return Returns the outputMessage.
158      */
159     public String getOutputMessage()
160     {
161         return outputMessage;
162     }
163 
164     /**
165      * @param outputMessage The outputMessage to set.
166      */
167     public void setOutputMessage(String outputMessage)
168     {
169         this.outputMessage = outputMessage;
170     }
171 
172     /**
173      * @return Returns the outputMessageCode.
174      */
175     public String getOutputMessageCode()
176     {
177         return outputMessageCode;
178     }
179 
180     /**
181      * @param outputMessageCode The outputMessageCode to set.
182      */
183     public void setOutputMessageCode(String outputMessageCode)
184     {
185         this.outputMessageCode = outputMessageCode;
186     }
187 
188     public Connector getConnector()
189     {
190         return this;
191     }
192 
193     public long getMessageDelayTime()
194     {
195         if (firstTime)
196         {
197             return messageDelayTime + 4000;
198         }
199         else
200         {
201             return messageDelayTime;
202         }
203     }
204 
205     public void setMessageDelayTime(long messageDelayTime)
206     {
207         this.messageDelayTime = messageDelayTime;
208     }
209 
210 
211     public OutputStream getOutputStream(ImmutableEndpoint endpoint, MuleMessage message) throws MuleException
212     {
213         OutputStream out;
214         String streamName = endpoint.getEndpointURI().getAddress();
215 
216         if (STREAM_SYSTEM_OUT.equalsIgnoreCase(streamName))
217         {
218             out = System.out;
219         }
220         else if (STREAM_SYSTEM_ERR.equalsIgnoreCase(streamName))
221         {
222             out = System.err;
223         }
224         else
225         {
226             out = getOutputStream();
227         }
228         return out;
229     }
230     
231     /**
232      * {@link PromptStdioConnector} needs a way to access other modules' messages.
233      * The default way to access messages is by using {@link MessageFactory} which
234      * itself is not meant to be used directly. In order not to soften this
235      * requiement this private subclass offers access to {@link MessageFactory}'s
236      * methods.
237      */
238     private static class StdioMessageFactory extends MessageFactory
239     {
240         private ClassLoader resourceClassLoader;
241 
242         public StdioMessageFactory(ClassLoader classLoader)
243         {
244             super();
245             resourceClassLoader = classLoader;
246         }
247 
248         protected String getString(String bundlePath, String code)
249         {
250             return super.getString(bundlePath, Integer.parseInt(code));
251         }
252 
253         @Override
254         protected ClassLoader getClassLoader()
255         {
256             return resourceClassLoader;
257         }
258     }
259 }