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.construct;
8   
9   import org.mule.MessageExchangePattern;
10  import org.mule.api.MuleContext;
11  import org.mule.api.MuleException;
12  import org.mule.api.MuleRuntimeException;
13  import org.mule.api.component.Component;
14  import org.mule.api.component.JavaComponent;
15  import org.mule.api.construct.FlowConstructInvalidException;
16  import org.mule.api.context.MuleContextAware;
17  import org.mule.api.endpoint.InboundEndpoint;
18  import org.mule.api.processor.MessageProcessor;
19  import org.mule.api.processor.MessageProcessorBuilder;
20  import org.mule.api.processor.MessageProcessorChainBuilder;
21  import org.mule.api.source.MessageSource;
22  import org.mule.config.i18n.MessageFactory;
23  import org.mule.construct.processor.FlowConstructStatisticsMessageProcessor;
24  import org.mule.interceptor.LoggingInterceptor;
25  import org.mule.interceptor.ProcessingTimeInterceptor;
26  import org.mule.util.ClassUtils;
27  
28  import java.lang.reflect.Method;
29  import java.util.List;
30  
31  import edu.emory.mathcs.backport.java.util.Collections;
32  
33  /**
34   * In-out SOA-style simple service, with no outbound router. Always fully
35   * synchronous.
36   */
37  public class SimpleService extends AbstractFlowConstruct
38  {
39      public enum Type
40      {
41          /**
42           * Expose a JAX-WS annoted component as a web service. The CXF module is
43           * required to have this working.
44           */
45          JAX_WS
46          {
47              @Override
48              public void validate(Component component) throws FlowConstructInvalidException
49              {
50                  if (!(component instanceof JavaComponent))
51                  {
52                      throw new FlowConstructInvalidException(
53                          MessageFactory.createStaticMessage("SimpleService can only expose instances of JAX-WS annotated JavaComponent instances. You provided a: "
54                                                             + component.getClass().getName()));
55                  }
56              }
57  
58              @Override
59              public void configureComponentMessageProcessor(MuleContext muleContext,
60                                                             MessageProcessorChainBuilder builder,
61                                                             Component component)
62              {
63                  builder.chain(newJaxWsComponentMessageProcessor(muleContext, getComponentClass(component)));
64                  builder.chain(component);
65              }
66          },
67  
68          /**
69           * Expose a JAX-RS annoted component as a web service. The Jersey module is
70           * required to have this working.
71           */
72          JAX_RS
73          {
74              @Override
75              public void validate(Component component) throws FlowConstructInvalidException
76              {
77                  if (!(component instanceof JavaComponent))
78                  {
79                      throw new FlowConstructInvalidException(
80                          MessageFactory.createStaticMessage("SimpleService can only expose instances of JAX-RS annotated JavaComponent instances. You provided a: "
81                                                             + component.getClass().getName()));
82                  }
83              }
84  
85              @Override
86              public void configureComponentMessageProcessor(MuleContext muleContext,
87                                                             MessageProcessorChainBuilder builder,
88                                                             Component component)
89              {
90                  builder.chain(newJaxRsComponentWrapper(muleContext, component));
91              }
92          },
93  
94          /**
95           * Pass the inbound messages unaltered to the component.
96           */
97          DIRECT
98          {
99              @Override
100             public void validate(Component component) throws FlowConstructInvalidException
101             {
102                 // NOOP
103             }
104 
105             @Override
106             public void configureComponentMessageProcessor(MuleContext muleContext,
107                                                            MessageProcessorChainBuilder builder,
108                                                            Component component)
109             {
110                 builder.chain(component);
111             }
112         };
113 
114         public abstract void validate(Component component) throws FlowConstructInvalidException;
115 
116         public abstract void configureComponentMessageProcessor(MuleContext muleContext,
117                                                                 MessageProcessorChainBuilder builder,
118                                                                 Component component);
119 
120         public static Type fromString(String string)
121         {
122             String mepString = string.toUpperCase().replace('-', '_');
123             return Type.valueOf(mepString);
124         }
125     }
126 
127     private final Component component;
128     private final Type type;
129 
130     public SimpleService(String name,
131                          MuleContext muleContext,
132                          MessageSource messageSource,
133                          Component component,
134                          Type type) throws MuleException
135     {
136         super(name, muleContext);
137 
138         if (messageSource == null)
139         {
140             throw new FlowConstructInvalidException(
141                 MessageFactory.createStaticMessage("messageSource can't be null on: " + this.toString()));
142         }
143 
144         if (component == null)
145         {
146             throw new FlowConstructInvalidException(
147                 MessageFactory.createStaticMessage("component can't be null on: " + this.toString()));
148         }
149 
150         if (type == null)
151         {
152             throw new FlowConstructInvalidException(
153                 MessageFactory.createStaticMessage("type can't be null on: " + this.toString()));
154         }
155 
156         this.messageSource = messageSource;
157         this.component = component;
158         this.type = type;
159     }
160 
161     public Component getComponent()
162     {
163         return component;
164     }
165 
166     @Override
167     protected void configureMessageProcessors(MessageProcessorChainBuilder builder)
168     {
169         builder.chain(new ProcessingTimeInterceptor());
170         builder.chain(new LoggingInterceptor());
171         builder.chain(new FlowConstructStatisticsMessageProcessor());
172         type.configureComponentMessageProcessor(muleContext, builder, component);
173     }
174 
175     @Override
176     protected void validateConstruct() throws FlowConstructInvalidException
177     {
178         super.validateConstruct();
179 
180         if ((messageSource instanceof InboundEndpoint)
181             && (!((InboundEndpoint) messageSource).getExchangePattern().equals(
182                 MessageExchangePattern.REQUEST_RESPONSE)))
183         {
184             throw new FlowConstructInvalidException(
185                 MessageFactory.createStaticMessage("SimpleService only works with a request-response inbound endpoint."),
186                 this);
187         }
188 
189         type.validate(component);
190     }
191 
192     private static Class<?> getComponentClass(Component component)
193     {
194         if (component instanceof JavaComponent)
195         {
196             return ((JavaComponent) component).getObjectFactory().getObjectClass();
197         }
198 
199         return component.getClass();
200     }
201 
202     private static MessageProcessor newJaxWsComponentMessageProcessor(MuleContext muleContext,
203                                                                       Class<?> componentClass)
204     {
205         try
206         {
207             MessageProcessorBuilder wsmpb = (MessageProcessorBuilder) ClassUtils.instanciateClass("org.mule.module.cxf.builder.WebServiceMessageProcessorBuilder");
208 
209             Method setServiceClassMethod = ClassUtils.getMethod(wsmpb.getClass(), "setServiceClass",
210                 new Class<?>[]{Class.class});
211 
212             setServiceClassMethod.invoke(wsmpb, componentClass);
213 
214             Method setFrontendMethod = ClassUtils.getMethod(wsmpb.getClass(), "setFrontend",
215                 new Class<?>[]{String.class});
216 
217             setFrontendMethod.invoke(wsmpb, "jaxws");
218 
219             ((MuleContextAware) wsmpb).setMuleContext(muleContext);
220 
221             return wsmpb.build();
222         }
223         catch (Exception e)
224         {
225             throw new MuleRuntimeException(
226                 MessageFactory.createStaticMessage("Failed to configure the required web service infrastructure: are you missing the Mule CXF Module?"),
227                 e);
228         }
229     }
230 
231     private static Component newJaxRsComponentWrapper(MuleContext muleContext, Component component)
232     {
233         try
234         {
235             Component jrc = (Component) ClassUtils.instanciateClass("org.mule.module.jersey.JerseyResourcesComponent");
236 
237             Method setComponentsMethod = ClassUtils.getMethod(jrc.getClass(), "setComponents",
238                 new Class<?>[]{List.class});
239 
240             setComponentsMethod.invoke(jrc, Collections.singletonList(component));
241 
242             ((MuleContextAware) jrc).setMuleContext(muleContext);
243 
244             return jrc;
245         }
246         catch (Exception e)
247         {
248             throw new MuleRuntimeException(
249                 MessageFactory.createStaticMessage("Failed to configure the required web service infrastructure: are you missing the Mule Jersey Module?"),
250                 e);
251         }
252     }
253 
254     @Override
255     public String getConstructType()
256     {
257         return "Simple-Service";
258     }
259 }