/**
 * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package org.eclipse.smarthome.core.internal.common;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.function.Consumer;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.common.SafeCaller;
import org.eclipse.smarthome.core.common.SafeCallerBuilder;

/**
 * Builder implementation to create safe-call wrapper objects.
 *
 * @author Simon Kaufmann - initial contribution and API.
 *
 * @param <T>
 */
@NonNullByDefault
public class SafeCallerBuilderImpl<T> implements SafeCallerBuilder<T> {

    private final T target;
    private final Class<?>[] interfaceTypes;
    private long timeout;
    private Object identifier;
    @Nullable
    private Consumer<Throwable> exceptionHandler;
    @Nullable
    private Runnable timeoutHandler;
    private boolean async;
    private final SafeCallManager manager;

    public SafeCallerBuilderImpl(T target, Class<?>[] classes, SafeCallManager manager) {
        this.target = target;
        this.interfaceTypes = classes;
        this.manager = manager;
        this.timeout = SafeCaller.DEFAULT_TIMEOUT;
        this.identifier = target;
        this.async = false;
    }

    @SuppressWarnings("unchecked")
    @Override
    public T build() {
        InvocationHandler handler;
        if (async) {
            handler = new InvocationHandlerAsync<T>(manager, target, identifier, timeout, exceptionHandler,
                    timeoutHandler);
        } else {
            handler = new InvocationHandlerSync<T>(manager, target, identifier, timeout, exceptionHandler,
                    timeoutHandler);
        }
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), interfaceTypes, handler);
    }

    @Override
    public SafeCallerBuilder<T> withTimeout(long timeout) {
        this.timeout = timeout;
        return this;
    }

    @Override
    public SafeCallerBuilder<T> withIdentifier(Object identifier) {
        this.identifier = identifier;
        return this;
    }

    @Override
    public SafeCallerBuilder<T> onException(Consumer<Throwable> exceptionHandler) {
        this.exceptionHandler = exceptionHandler;
        return this;
    }

    @Override
    public SafeCallerBuilder<T> onTimeout(Runnable timeoutHandler) {
        this.timeoutHandler = timeoutHandler;
        return this;
    }

    @Override
    public SafeCallerBuilder<T> withAsync() {
        this.async = true;
        return this;
    }

}

@@ -536,16 +536,94 @@
 .Proxy;%0A
+import java.security.AccessController;%0Aimport java.security.PrivilegedAction;%0A
 import j
@@ -1796,24 +1796,103 @@
 T build() %7B%0A
+        return AccessController.doPrivileged((PrivilegedAction%3CT%3E) () -%3E %7B%0A    
         Invo
@@ -1922,16 +1922,20 @@
         
+    
 if (asyn
@@ -1935,24 +1935,28 @@
 f (async) %7B%0A
+    
             
@@ -2063,32 +2063,36 @@
                 
+    
 timeoutHandler);
@@ -2100,16 +2100,20 @@
         
+  
+  
 %7D else %7B
@@ -2113,16 +2113,20 @@
  else %7B%0A
+    
         
@@ -2236,32 +2236,36 @@
                 
+    
 timeoutHandler);
@@ -2273,16 +2273,20 @@
         
+    
 %7D%0A      
@@ -2283,24 +2283,28 @@
   %7D%0A        
+ 
+   
 return (T) P
@@ -2383,24 +2383,36 @@
 , handler);%0A
+        %7D);%0A
     %7D%0A%0A    @

switch security context when creating dynamic proxy in SafeCaller (#5290)