View Javadoc

1   /*
2    * Copyright 2007 Tim Peierls
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.directwebremoting.guice;
17  
18  import com.google.inject.Key;
19  import com.google.inject.Injector;
20  import com.google.inject.Provider;
21  
22  import java.util.Collection;
23  import java.util.Map;
24  
25  import org.directwebremoting.dwrp.DefaultConverterManager;
26  import org.directwebremoting.extend.*;
27  import org.directwebremoting.util.Logger;
28  
29  import static org.directwebremoting.guice.DwrGuiceUtil.getInjector;
30  import static org.directwebremoting.guice.DwrGuiceUtil.getServletContext;
31  
32  /**
33   * Extends an existing converter manager with an injected list of converters
34   * specified at Guice bind-time. Only to be used in conjection with
35   * {@link DwrGuiceServlet}.
36   * @author Tim Peierls [tim at peierls dot net]
37   */
38  public class InternalConverterManager implements ConverterManager 
39  {
40      /**
41       * Retrieves an underlying converter manager from thread-local state
42       * to which this class delegates {@link ConverterManager} calls.
43       */
44      public InternalConverterManager()
45      {
46          this.converterManager = getConverterManager();
47          addConverters();
48      }
49  
50  
51      public void addConverterType(String id, String className)
52      {
53          converterManager.addConverterType(id, className);
54      }
55  
56      public void addConverter(String match, String type, Map params) throws IllegalArgumentException, InstantiationException, IllegalAccessException
57      {
58          converterManager.addConverter(match, type, params);
59      }
60  
61      public void addConverter(String match, Converter converter) throws IllegalArgumentException
62      {
63          converterManager.addConverter(match, converter);
64      }
65  
66      public Collection getConverterMatchStrings()
67      {
68          return converterManager.getConverterMatchStrings();
69      }
70  
71      public Converter getConverterByMatchString(String match)
72      {
73          return converterManager.getConverterByMatchString(match);
74      }
75  
76      public boolean isConvertable(Class paramType)
77      {
78          return converterManager.isConvertable(paramType);
79      }
80  
81      public Object convertInbound(Class paramType, InboundVariable iv, InboundContext inctx, TypeHintContext incc) throws MarshallException
82      {
83          return converterManager.convertInbound(paramType, iv, inctx, incc);
84      }
85  
86      public OutboundVariable convertOutbound(Object object, OutboundContext outctx) throws MarshallException
87      {
88          return converterManager.convertOutbound(object, outctx);
89      }
90  
91      public void setExtraTypeInfo(TypeHintContext thc, Class type)
92      {
93          converterManager.setExtraTypeInfo(thc, type);
94      }
95  
96      public Class getExtraTypeInfo(TypeHintContext thc)
97      {
98          return converterManager.getExtraTypeInfo(thc);
99      }
100 
101     public void setConverters(Map converters)
102     {
103         converterManager.setConverters(converters);
104     }
105 
106     
107     private final ConverterManager converterManager;
108 
109     private void addConverters()
110     {
111         Injector injector = getInjector();
112         for (Key<?> key : injector.getBindings().keySet())
113         {
114             Class<?> atype = key.getAnnotationType();
115             if (atype != null && Converting.class.isAssignableFrom(atype))
116             {
117                 Converting ann = Converting.class.cast(key.getAnnotation());
118                 
119                 String match = ann.match();
120                 Class type = ann.type();
121                 Class impl = ann.impl();
122                 
123                 if ("".equals(match))
124                 {
125                     // Use the type name as a match string
126                     match = type.getName();
127                 }
128 
129                 Provider<Converter> provider = null;
130                 Class cvtType;
131                 
132                 if (impl.equals(Void.class))
133                 {
134                     // No impl specified, so there should be a Converter
135                     // for this key.
136                     
137                     provider = injector.getProvider((Key<Converter>) key);
138                     cvtType = type;
139                 }
140                 else
141                 {
142                     // Impl class specified, so the Converter for key is
143                     // bogus (the injected constructor InternalConverter
144                     // is just to keep Guice happy); see the two-arg 
145                     // bindConversion method in AbstractDwrModule.
146                     
147                     try 
148                     {
149                         // First try looking for a Converter for impl in the bindings.
150                      
151                         Key<Converter> ikey = Key.get(Converter.class, new ConvertingImpl(impl));
152                         provider = injector.getProvider(ikey);
153                     }
154                     catch (RuntimeException e)
155                     {
156                         // Ignore any trouble we have looking things up.
157                     }
158                     
159                     if (provider == null)
160                     {
161                         // It wasn't in the bindings, so use a Provider that
162                         // looks in the underlying ConverterManager.
163                     
164                         final String implMatch = impl.getName();
165                         provider = new Provider<Converter>()
166                         {
167                             public Converter get()
168                             {
169                                 return getConverterByMatchString(implMatch);
170                             }
171                         };
172                     }
173                     
174                     cvtType = impl;
175                 }
176                 addConverter(match, new InternalConverter(cvtType, provider));
177             }
178         }
179     }
180     
181     /**
182      * Stores a type name in a thread-local variable for later retrieval by
183      * {@code getConverterManager}.
184      */
185     static void setTypeName(String name)
186     {
187         typeName.set(name);
188     }
189     
190     private static ConverterManager getConverterManager()
191     {
192         String name = typeName.get();
193         try
194         {
195             Class<? extends ConverterManager> cls = 
196                 (Class<? extends ConverterManager>) Class.forName(name);
197             return cls.newInstance();
198         }
199         catch (Exception e)
200         {
201             if (name != null && !"".equals(name)) {
202                 log.warn("Couldn't make ConverterManager from type: " + name);
203             }
204             return new DefaultConverterManager();
205         }
206     }
207 
208 
209     /**
210      * Place to stash a type name for retrieval in same thread.
211      */
212     private static final ThreadLocal<String> typeName = new ThreadLocal<String>();
213 
214 
215     /**
216      * The log stream
217      */
218     private static final Logger log = Logger.getLogger(InternalConverterManager.class);
219 }