1919import com .beust .jcommander .JCommander ;
2020import com .beust .jcommander .Parameter ;
2121import com .beust .jcommander .ParameterException ;
22+ import com .google .auto .value .AutoOneOf ;
2223import com .google .cloud .functions .BackgroundFunction ;
2324import com .google .cloud .functions .HttpFunction ;
2425import com .google .cloud .functions .RawBackgroundFunction ;
@@ -250,34 +251,48 @@ public void startServer() throws Exception {
250251 servletContextHandler .setContextPath ("/" );
251252 server .setHandler (NotFoundHandler .forServlet (servletContextHandler ));
252253
253- Optional < Class <?>> functionClass = loadFunctionClass ();
254+ ClassOrException functionClass = loadFunctionClass ();
254255
255256 HttpServlet servlet ;
256257 if ("http" .equals (functionSignatureType )) {
257- if (functionClass .isPresent ()) {
258- servlet = NewHttpFunctionExecutor .forClass (functionClass .get ());
259- } else {
260- FunctionLoader <HttpCloudFunction > loader = new FunctionLoader <>(
261- functionTarget , functionClassLoader , new HttpFunctionSignatureMatcher ());
262- HttpCloudFunction function = loader .loadUserFunction ();
263- servlet = new HttpFunctionExecutor (function );
258+ switch (functionClass .getKind ()) {
259+ case LOADED_CLASS :
260+ servlet = NewHttpFunctionExecutor .forClass (functionClass .loadedClass ());
261+ break ;
262+ default :
263+ FunctionLoader <HttpCloudFunction > loader =
264+ new FunctionLoader <>(
265+ functionTarget ,
266+ functionClassLoader ,
267+ new HttpFunctionSignatureMatcher (),
268+ functionClass .exception ());
269+ HttpCloudFunction function = loader .loadUserFunction ();
270+ servlet = new HttpFunctionExecutor (function );
264271 }
265272 } else if ("event" .equals (functionSignatureType )) {
266- if (functionClass .isPresent ()) {
267- servlet = NewBackgroundFunctionExecutor .forClass (functionClass .get ());
268- } else {
269- FunctionLoader <BackgroundCloudFunction > loader =
270- new FunctionLoader <>(
271- functionTarget , functionClassLoader , new BackgroundFunctionSignatureMatcher ());
272- BackgroundCloudFunction function = loader .loadUserFunction ();
273- servlet = new BackgroundFunctionExecutor (function );
273+ switch (functionClass .getKind ()) {
274+ case LOADED_CLASS :
275+ servlet = NewBackgroundFunctionExecutor .forClass (functionClass .loadedClass ());
276+ break ;
277+ default :
278+ FunctionLoader <BackgroundCloudFunction > loader =
279+ new FunctionLoader <>(
280+ functionTarget ,
281+ functionClassLoader ,
282+ new BackgroundFunctionSignatureMatcher (),
283+ functionClass .exception ());
284+ BackgroundCloudFunction function = loader .loadUserFunction ();
285+ servlet = new BackgroundFunctionExecutor (function );
274286 }
275287 } else if (functionSignatureType == null ) {
276- if (functionClass .isPresent ()) {
277- servlet = servletForDeducedSignatureType (functionClass .get ());
278- } else {
279- throw new RuntimeException (
280- "Class " + functionTarget + " does not exist or could not be loaded" );
288+ switch (functionClass .getKind ()) {
289+ case LOADED_CLASS :
290+ servlet = servletForDeducedSignatureType (functionClass .loadedClass ());
291+ break ;
292+ default :
293+ throw new RuntimeException (
294+ "Class " + functionTarget + " does not exist or could not be loaded" ,
295+ functionClass .exception ());
281296 }
282297 } else {
283298 String error = String .format (
@@ -294,18 +309,44 @@ public void startServer() throws Exception {
294309 server .join ();
295310 }
296311
297- private Optional <Class <?>> loadFunctionClass () {
312+ @ AutoOneOf (ClassOrException .Kind .class )
313+ abstract static class ClassOrException {
314+ enum Kind {LOADED_CLASS , EXCEPTION }
315+
316+ abstract Kind getKind ();
317+
318+ abstract Class <?> loadedClass ();
319+
320+ abstract ClassNotFoundException exception ();
321+
322+ static ClassOrException of (Class <?> c ) {
323+ return AutoOneOf_Invoker_ClassOrException .loadedClass (c );
324+ }
325+
326+ static ClassOrException of (ClassNotFoundException e ) {
327+ return AutoOneOf_Invoker_ClassOrException .exception (e );
328+ }
329+ }
330+
331+ private ClassOrException loadFunctionClass () {
298332 String target = functionTarget ;
333+ ClassNotFoundException firstException = null ;
299334 while (true ) {
300335 try {
301- return Optional .of (functionClassLoader .loadClass (target ));
336+ return ClassOrException .of (functionClassLoader .loadClass (target ));
302337 } catch (ClassNotFoundException e ) {
338+ if (firstException == null ) {
339+ firstException = e ;
340+ }
303341 // This might be a nested class like com.example.Foo.Bar. That will actually appear as
304342 // com.example.Foo$Bar as far as Class.forName is concerned. So we try to replace every dot
305343 // from the last to the first with a $ in the hope of finding a class we can load.
306344 int lastDot = target .lastIndexOf ('.' );
307345 if (lastDot < 0 ) {
308- return Optional .empty ();
346+ // We're going to try to load the function target using the old form, classname.method.
347+ // But it's at least as likely that the class does exist, but we failed to load it for
348+ // some other reason. So ensure that we keep the exception to show to the user.
349+ return ClassOrException .of (firstException );
309350 }
310351 target = target .substring (0 , lastDot ) + '$' + target .substring (lastDot + 1 );
311352 }
0 commit comments