1 /**
2  * Copyright (C) 2007 Google Inc.
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 
17 package com.google.inject;
18 
19 import static com.google.inject.Asserts.asModuleChain;
20 import static com.google.inject.Asserts.assertContains;
21 import static com.google.inject.Asserts.assertNotSerializable;
22 import static com.google.inject.Asserts.getDeclaringSourcePart;
23 import static com.google.inject.Asserts.isIncludeStackTraceOff;
24 
25 import com.google.common.collect.Iterables;
26 import com.google.common.collect.Lists;
27 import com.google.inject.name.Named;
28 import com.google.inject.name.Names;
29 import com.google.inject.spi.Message;
30 import com.google.inject.util.Providers;
31 
32 import junit.framework.TestCase;
33 
34 import java.io.IOException;
35 import java.util.Comparator;
36 import java.util.Date;
37 import java.util.List;
38 import java.util.concurrent.Callable;
39 import java.util.logging.Handler;
40 import java.util.logging.LogRecord;
41 import java.util.logging.Logger;
42 
43 /**
44  * @author crazybob@google.com (Bob Lee)
45  */
46 public class BinderTest extends TestCase {
47 
48   private final Logger loggerToWatch = Logger.getLogger(Guice.class.getName());
49 
50   private final List<LogRecord> logRecords = Lists.newArrayList();
51   private final Handler fakeHandler = new Handler() {
52     @Override
53     public void publish(LogRecord logRecord) {
54       logRecords.add(logRecord);
55     }
56     @Override
57     public void flush() {}
58     @Override
59     public void close() throws SecurityException {}
60   };
61 
62   Provider<Foo> fooProvider;
63 
setUp()64   @Override protected void setUp() throws Exception {
65     super.setUp();
66     loggerToWatch.addHandler(fakeHandler);
67   }
68 
tearDown()69   @Override protected void tearDown() throws Exception {
70     loggerToWatch.removeHandler(fakeHandler);
71     super.tearDown();
72   }
73 
testProviderFromBinder()74   public void testProviderFromBinder() {
75     Guice.createInjector(new Module() {
76       public void configure(Binder binder) {
77         fooProvider = binder.getProvider(Foo.class);
78 
79         try {
80           fooProvider.get();
81         } catch (IllegalStateException e) { /* expected */ }
82       }
83     });
84 
85     assertNotNull(fooProvider.get());
86   }
87 
88   static class Foo {}
89 
testMissingBindings()90   public void testMissingBindings() {
91     try {
92       Guice.createInjector(new AbstractModule() {
93         @Override
94         public void configure() {
95           getProvider(Runnable.class);
96           bind(Comparator.class);
97           requireBinding(Key.get(new TypeLiteral<Callable<String>>() {}));
98           bind(Date.class).annotatedWith(Names.named("date"));
99         }
100       });
101     } catch (CreationException e) {
102       assertEquals(4, e.getErrorMessages().size());
103       String segment1 = "No implementation for " + Comparator.class.getName() + " was bound.";
104       String segment2 = "No implementation for java.util.Date annotated with @"
105           + Named.class.getName() + "(value=date) was bound.";
106       String segment3 = "No implementation for java.lang.Runnable was bound.";
107       String segment4 = " No implementation for java.util.concurrent.Callable<java.lang.String> was"
108           + " bound.";
109       String atSegment = "at " + getClass().getName();
110       String sourceFileName = getDeclaringSourcePart(getClass());
111       if (isIncludeStackTraceOff()) {
112         assertContains(e.getMessage(),
113             segment1, atSegment, sourceFileName,
114             segment2, atSegment, sourceFileName,
115             segment3, atSegment, sourceFileName,
116             segment4, atSegment, sourceFileName);
117       } else {
118         assertContains(e.getMessage(),
119             segment3, atSegment, sourceFileName,
120             segment1, atSegment, sourceFileName,
121             segment4, atSegment, sourceFileName,
122             segment2, atSegment, sourceFileName);
123       }
124     }
125   }
126 
testMissingDependency()127   public void testMissingDependency() {
128     try {
129       Guice.createInjector(new AbstractModule() {
130         @Override
131         public void configure() {
132           bind(NeedsRunnable.class);
133         }
134       });
135     } catch (CreationException e) {
136       assertEquals(1, e.getErrorMessages().size());
137       assertContains(e.getMessage(),
138           "No implementation for java.lang.Runnable was bound.",
139           "for field at " + NeedsRunnable.class.getName(), ".runnable(BinderTest.java:",
140           "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
141     }
142   }
143 
144   static class NeedsRunnable {
145     @Inject Runnable runnable;
146   }
147 
testDanglingConstantBinding()148   public void testDanglingConstantBinding() {
149     try {
150       Guice.createInjector(new AbstractModule() {
151         @Override public void configure() {
152           bindConstant();
153         }
154       });
155       fail();
156     } catch (CreationException expected) {
157       assertContains(expected.getMessage(),
158           "1) Missing constant value. Please call to(...).",
159           "at " + getClass().getName());
160     }
161   }
162 
testRecursiveBinding()163   public void testRecursiveBinding() {
164     try {
165       Guice.createInjector(new AbstractModule() {
166         @Override public void configure() {
167           bind(Runnable.class).to(Runnable.class);
168         }
169       });
170       fail();
171     } catch (CreationException expected) {
172       assertContains(expected.getMessage(),
173           "1) Binding points to itself.",
174           "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
175     }
176   }
177 
testBindingNullConstant()178   public void testBindingNullConstant() {
179     try {
180       Guice.createInjector(new AbstractModule() {
181         @Override public void configure() {
182           String none = null;
183           bindConstant().annotatedWith(Names.named("nullOne")).to(none);
184           bind(String.class).annotatedWith(Names.named("nullTwo")).toInstance(none);
185         }
186       });
187       fail();
188     } catch (CreationException expected) {
189       assertContains(expected.getMessage(),
190           "1) Binding to null instances is not allowed. Use toProvider(Providers.of(null))",
191           "2) Binding to null instances is not allowed. Use toProvider(Providers.of(null))");
192     }
193   }
194 
testToStringOnBinderApi()195   public void testToStringOnBinderApi() {
196     try {
197       Guice.createInjector(new AbstractModule() {
198         @Override public void configure() {
199           assertEquals("Binder", binder().toString());
200           assertEquals("Provider<java.lang.Integer>", getProvider(Integer.class).toString());
201           assertEquals("Provider<java.util.List<java.lang.String>>",
202               getProvider(Key.get(new TypeLiteral<List<String>>() {})).toString());
203 
204           assertEquals("BindingBuilder<java.lang.Integer>",
205               bind(Integer.class).toString());
206           assertEquals("BindingBuilder<java.lang.Integer>",
207               bind(Integer.class).annotatedWith(Names.named("a")).toString());
208           assertEquals("ConstantBindingBuilder", bindConstant().toString());
209           assertEquals("ConstantBindingBuilder",
210               bindConstant().annotatedWith(Names.named("b")).toString());
211           assertEquals("AnnotatedElementBuilder",
212               binder().newPrivateBinder().expose(Integer.class).toString());
213         }
214       });
215       fail();
216     } catch (CreationException ignored) {
217     }
218   }
219 
testNothingIsSerializableInBinderApi()220   public void testNothingIsSerializableInBinderApi() {
221     try {
222       Guice.createInjector(new AbstractModule() {
223         @Override public void configure() {
224           try {
225             assertNotSerializable(binder());
226             assertNotSerializable(getProvider(Integer.class));
227             assertNotSerializable(getProvider(Key.get(new TypeLiteral<List<String>>() {})));
228             assertNotSerializable(bind(Integer.class));
229             assertNotSerializable(bind(Integer.class).annotatedWith(Names.named("a")));
230             assertNotSerializable(bindConstant());
231             assertNotSerializable(bindConstant().annotatedWith(Names.named("b")));
232           } catch (IOException e) {
233             fail(e.getMessage());
234           }
235         }
236       });
237       fail();
238     } catch (CreationException ignored) {
239     }
240   }
241 
242   /**
243    * Although {@code String[].class} isn't equal to {@code new
244    * GenericArrayTypeImpl(String.class)}, Guice should treat these two types
245    * interchangeably.
246    */
testArrayTypeCanonicalization()247   public void testArrayTypeCanonicalization() {
248     final String[] strings = new String[] { "A" };
249     final Integer[] integers = new Integer[] { 1 };
250 
251     Injector injector = Guice.createInjector(new AbstractModule() {
252       @Override
253       protected void configure() {
254         bind(String[].class).toInstance(strings);
255         bind(new TypeLiteral<Integer[]>() {}).toInstance(integers);
256       }
257     });
258 
259     assertSame(integers, injector.getInstance(Key.get(new TypeLiteral<Integer[]>() {})));
260     assertSame(integers, injector.getInstance(new Key<Integer[]>() {}));
261     assertSame(integers, injector.getInstance(Integer[].class));
262     assertSame(strings, injector.getInstance(Key.get(new TypeLiteral<String[]>() {})));
263     assertSame(strings, injector.getInstance(new Key<String[]>() {}));
264     assertSame(strings, injector.getInstance(String[].class));
265 
266     try {
267       Guice.createInjector(new AbstractModule() {
268         @Override
269         protected void configure() {
270           bind(String[].class).toInstance(new String[] { "A" });
271           bind(new TypeLiteral<String[]>() {}).toInstance(new String[] { "B" });
272         }
273       });
274       fail();
275     } catch (CreationException expected) {
276       assertContains(expected.getMessage(),
277           "1) A binding to java.lang.String[] was already configured at " + getClass().getName(),
278           "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
279       assertContains(expected.getMessage(), "1 error");
280     }
281 
282     // passes because duplicates are ignored
283     injector = Guice.createInjector(new AbstractModule() {
284       @Override
285       protected void configure() {
286         bind(String[].class).toInstance(strings);
287         bind(new TypeLiteral<String[]>() {}).toInstance(strings);
288       }
289     });
290     assertSame(strings, injector.getInstance(Key.get(new TypeLiteral<String[]>() {})));
291     assertSame(strings, injector.getInstance(new Key<String[]>() {}));
292     assertSame(strings, injector.getInstance(String[].class));
293   }
294 
295   static class ParentModule extends AbstractModule {
configure()296     @Override protected void configure() {
297       install(new FooModule());
298       install(new BarModule());
299     }
300   }
301   static class FooModule extends AbstractModule {
configure()302     @Override protected void configure() {
303       install(new ConstantModule("foo"));
304     }
305   }
306   static class BarModule extends AbstractModule {
configure()307     @Override protected void configure() {
308       install(new ConstantModule("bar"));
309     }
310   }
311   static class ConstantModule extends AbstractModule {
312     private final String constant;
ConstantModule(String constant)313     ConstantModule(String constant) {
314       this.constant = constant;
315     }
configure()316     @Override protected void configure() {
317       bind(String.class).toInstance(constant);
318     }
319   }
320 
321   /**
322    * Binding something to two different things should give an error.
323    */
testSettingBindingTwice()324   public void testSettingBindingTwice() {
325     try {
326       Guice.createInjector(new ParentModule());
327       fail();
328     } catch(CreationException expected) {
329       assertContains(expected.getMessage(),
330         "1) A binding to java.lang.String was already configured at " + ConstantModule.class.getName(),
331         asModuleChain(ParentModule.class, FooModule.class, ConstantModule.class),
332         "at " + ConstantModule.class.getName(), getDeclaringSourcePart(getClass()),
333         asModuleChain(ParentModule.class, BarModule.class, ConstantModule.class));
334       assertContains(expected.getMessage(), "1 error");
335     }
336   }
337 
338   /**
339    * Binding an @ImplementedBy thing to something else should also fail.
340    */
testSettingAtImplementedByTwice()341   public void testSettingAtImplementedByTwice() {
342     try {
343       Guice.createInjector(new AbstractModule() {
344         @Override
345         protected void configure() {
346           bind(HasImplementedBy1.class);
347           bind(HasImplementedBy1.class).toInstance(new HasImplementedBy1() {});
348         }
349       });
350       fail();
351     } catch(CreationException expected) {
352       expected.printStackTrace();
353       assertContains(expected.getMessage(),
354         "1) A binding to " + HasImplementedBy1.class.getName()
355         + " was already configured at " + getClass().getName(),
356         "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
357       assertContains(expected.getMessage(), "1 error");
358     }
359   }
360 
361   /**
362    * See issue 614, Problem One
363    * https://github.com/google/guice/issues/614
364    */
testJitDependencyDoesntBlockOtherExplicitBindings()365   public void testJitDependencyDoesntBlockOtherExplicitBindings() {
366     Injector injector = Guice.createInjector(new AbstractModule() {
367       @Override
368       protected void configure() {
369         bind(HasImplementedByThatNeedsAnotherImplementedBy.class);
370         bind(HasImplementedBy1.class).toInstance(new HasImplementedBy1() {});
371       }
372     });
373     injector.getAllBindings(); // just validate it doesn't throw.
374     // Also validate that we're using the explicit (and not @ImplementedBy) implementation
375     assertFalse(injector.getInstance(HasImplementedBy1.class) instanceof ImplementsHasImplementedBy1);
376   }
377 
378   /**
379    * See issue 614, Problem Two
380    * https://github.com/google/guice/issues/id=614
381    */
testJitDependencyCanUseExplicitDependencies()382   public void testJitDependencyCanUseExplicitDependencies() {
383     Guice.createInjector(new AbstractModule() {
384       @Override
385       protected void configure() {
386         bind(HasImplementedByThatWantsExplicit.class);
387         bind(JustAnInterface.class).toInstance(new JustAnInterface() {});
388       }
389     });
390   }
391 
392   /**
393    * Untargetted bindings should follow @ImplementedBy and @ProvidedBy
394    * annotations if they exist. Otherwise the class should be constructed
395    * directly.
396    */
testUntargettedBinding()397   public void testUntargettedBinding() {
398     Injector injector = Guice.createInjector(new AbstractModule() {
399       @Override
400       protected void configure() {
401         bind(HasProvidedBy1.class);
402         bind(HasImplementedBy1.class);
403         bind(HasProvidedBy2.class);
404         bind(HasImplementedBy2.class);
405         bind(JustAClass.class);
406       }
407     });
408 
409     assertNotNull(injector.getInstance(HasProvidedBy1.class));
410     assertNotNull(injector.getInstance(HasImplementedBy1.class));
411     assertNotSame(HasProvidedBy2.class,
412         injector.getInstance(HasProvidedBy2.class).getClass());
413     assertSame(ExtendsHasImplementedBy2.class,
414         injector.getInstance(HasImplementedBy2.class).getClass());
415     assertSame(JustAClass.class, injector.getInstance(JustAClass.class).getClass());
416   }
417 
testPartialInjectorGetInstance()418   public void testPartialInjectorGetInstance() {
419     Injector injector = Guice.createInjector();
420     try {
421       injector.getInstance(MissingParameter.class);
422       fail();
423     } catch (ConfigurationException expected) {
424       assertContains(expected.getMessage(),
425           "1) Could not find a suitable constructor in " + NoInjectConstructor.class.getName(),
426           "at " + MissingParameter.class.getName() + ".<init>(BinderTest.java:");
427     }
428   }
429 
testUserReportedError()430   public void testUserReportedError() {
431     final Message message = new Message(getClass(), "Whoops!");
432     try {
433       Guice.createInjector(new AbstractModule() {
434         @Override
435         protected void configure() {
436           addError(message);
437         }
438       });
439       fail();
440     } catch (CreationException expected) {
441       assertSame(message, Iterables.getOnlyElement(expected.getErrorMessages()));
442     }
443   }
444 
testUserReportedErrorsAreAlsoLogged()445   public void testUserReportedErrorsAreAlsoLogged() {
446     try {
447       Guice.createInjector(new AbstractModule() {
448         @Override
449         protected void configure() {
450           addError(new Message("Whoops!", new IllegalArgumentException()));
451         }
452       });
453       fail();
454     } catch (CreationException expected) {
455     }
456 
457     LogRecord logRecord = Iterables.getOnlyElement(this.logRecords);
458     assertContains(logRecord.getMessage(),
459         "An exception was caught and reported. Message: java.lang.IllegalArgumentException");
460   }
461 
testBindingToProvider()462   public void testBindingToProvider() {
463     try {
464       Guice.createInjector(new AbstractModule() {
465         @Override
466         protected void configure() {
467           bind(new TypeLiteral<Provider<String>>() {}).toInstance(Providers.of("A"));
468         }
469       });
470       fail();
471     } catch (CreationException expected) {
472       assertContains(expected.getMessage(),
473           "1) Binding to Provider is not allowed.",
474           "at " + BinderTest.class.getName(), getDeclaringSourcePart(getClass()));
475     }
476   }
477 
478   static class OuterCoreModule extends AbstractModule {
configure()479     @Override protected void configure() {
480       install(new InnerCoreModule());
481     }
482   }
483   static class InnerCoreModule extends AbstractModule {
484     final Named red = Names.named("red");
485 
configure()486     @Override protected void configure() {
487       bind(AbstractModule.class).annotatedWith(red)
488       .toProvider(Providers.<AbstractModule>of(null));
489       bind(Binder.class).annotatedWith(red).toProvider(Providers.<Binder>of(null));
490       bind(Binding.class).annotatedWith(red).toProvider(Providers.<Binding>of(null));
491       bind(Injector.class).annotatedWith(red).toProvider(Providers.<Injector>of(null));
492       bind(Key.class).annotatedWith(red).toProvider(Providers.<Key>of(null));
493       bind(Module.class).annotatedWith(red).toProvider(Providers.<Module>of(null));
494       bind(Provider.class).annotatedWith(red).toProvider(Providers.<Provider>of(null));
495       bind(Scope.class).annotatedWith(red).toProvider(Providers.<Scope>of(null));
496       bind(Stage.class).annotatedWith(red).toProvider(Providers.<Stage>of(null));
497       bind(TypeLiteral.class).annotatedWith(red).toProvider(Providers.<TypeLiteral>of(null));
498       bind(new TypeLiteral<Key<String>>() {}).toProvider(Providers.<Key<String>>of(null));
499     }
500   }
testCannotBindToGuiceTypes()501   public void testCannotBindToGuiceTypes() {
502     try {
503       Guice.createInjector(new OuterCoreModule());
504       fail();
505     } catch (CreationException expected) {
506       assertContains(expected.getMessage(),
507           "Binding to core guice framework type is not allowed: AbstractModule.",
508           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
509           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
510 
511           "Binding to core guice framework type is not allowed: Binder.",
512           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
513           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
514 
515           "Binding to core guice framework type is not allowed: Binding.",
516           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
517           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
518 
519           "Binding to core guice framework type is not allowed: Injector.",
520           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
521           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
522 
523           "Binding to core guice framework type is not allowed: Key.",
524           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
525           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
526 
527           "Binding to core guice framework type is not allowed: Module.",
528           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
529           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
530 
531           "Binding to Provider is not allowed.",
532           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
533           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
534 
535           "Binding to core guice framework type is not allowed: Scope.",
536           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
537           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
538 
539           "Binding to core guice framework type is not allowed: Stage.",
540           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
541           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
542 
543           "Binding to core guice framework type is not allowed: TypeLiteral.",
544           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
545           asModuleChain(OuterCoreModule.class, InnerCoreModule.class),
546 
547           "Binding to core guice framework type is not allowed: Key.",
548           "at " + InnerCoreModule.class.getName() + getDeclaringSourcePart(getClass()),
549           asModuleChain(OuterCoreModule.class, InnerCoreModule.class));
550     }
551   }
552 
553   static class MissingParameter {
MissingParameter(NoInjectConstructor noInjectConstructor)554     @Inject MissingParameter(NoInjectConstructor noInjectConstructor) {}
555   }
556 
557   static class NoInjectConstructor {
NoInjectConstructor()558     private NoInjectConstructor() {}
559   }
560 
561   @ProvidedBy(HasProvidedBy1Provider.class)
562   interface HasProvidedBy1 {}
563 
564   static class HasProvidedBy1Provider implements Provider<HasProvidedBy1> {
get()565     public HasProvidedBy1 get() {
566       return new HasProvidedBy1() {};
567     }
568   }
569 
570   @ImplementedBy(ImplementsHasImplementedBy1.class)
571   interface HasImplementedBy1 {}
572 
573   static class ImplementsHasImplementedBy1 implements HasImplementedBy1 {}
574 
575   @ProvidedBy(HasProvidedBy2Provider.class)
576   static class HasProvidedBy2 {}
577 
578   static class HasProvidedBy2Provider implements Provider<HasProvidedBy2> {
get()579     public HasProvidedBy2 get() {
580       return new HasProvidedBy2() {};
581     }
582   }
583 
584   @ImplementedBy(ExtendsHasImplementedBy2.class)
585   static class HasImplementedBy2 {}
586 
587   static class ExtendsHasImplementedBy2 extends HasImplementedBy2 {}
588 
589   static class JustAClass {}
590 
591   @ImplementedBy(ImplementsHasImplementedByThatNeedsAnotherImplementedBy.class)
592   static interface HasImplementedByThatNeedsAnotherImplementedBy {
593   }
594 
595   static class ImplementsHasImplementedByThatNeedsAnotherImplementedBy
596     implements HasImplementedByThatNeedsAnotherImplementedBy {
597     @Inject
ImplementsHasImplementedByThatNeedsAnotherImplementedBy( HasImplementedBy1 h1n1)598     ImplementsHasImplementedByThatNeedsAnotherImplementedBy(
599         HasImplementedBy1 h1n1) {}
600   }
601 
602   @ImplementedBy(ImplementsHasImplementedByThatWantsExplicit.class)
603   static interface HasImplementedByThatWantsExplicit {
604   }
605 
606   static class ImplementsHasImplementedByThatWantsExplicit
607       implements HasImplementedByThatWantsExplicit {
ImplementsHasImplementedByThatWantsExplicit(JustAnInterface jai)608     @Inject ImplementsHasImplementedByThatWantsExplicit(JustAnInterface jai) {}
609   }
610 
611   static interface JustAnInterface {}
612 
613 
614 //  public void testBindInterfaceWithoutImplementation() {
615 //    Guice.createInjector(new AbstractModule() {
616 //      protected void configure() {
617 //        bind(Runnable.class);
618 //      }
619 //    }).getInstance(Runnable.class);
620 //  }
621 
622   enum Roshambo { ROCK, SCISSORS, PAPER }
623 
testInjectRawProvider()624   public void testInjectRawProvider() {
625     try {
626       Guice.createInjector().getInstance(Provider.class);
627       fail();
628     } catch (ConfigurationException expected) {
629       Asserts.assertContains(expected.getMessage(),
630           "1) Cannot inject a Provider that has no type parameter",
631           "while locating " + Provider.class.getName());
632     }
633   }
634 }
635