Is this JAX-WS client call thread safe?

前端 未结 4 648
情歌与酒
情歌与酒 2020-12-14 06:34

Since initialization of the WS client service and port takes ages I like to initialize them once at startup and reuse the same instance of the port. Initalization would look

4条回答
  •  孤街浪徒
    2020-12-14 07:21

    As you see from above answers that JAX-WS client proxies are not thread safe so I just wanted to share my implementation will others to cache the client proxies. I actually faced the same issue and decided to create a spring bean that does the caching of the JAX-WS Client proxies. You can see more details http://programtalk.com/java/using-spring-and-scheduler-to-store/

    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    import javax.annotation.PostConstruct;
    
    import org.apache.commons.lang3.concurrent.BasicThreadFactory;
    import org.apache.logging.log4j.Logger;
    import org.springframework.stereotype.Component;
    
    /**
     * This keeps the cache of MAX_CUNCURRENT_THREADS number of
     * appConnections and tries to shares them equally amongst the threads. All the
     * connections are created right at the start and if an error occurs then the
     * cache is created again.
     *
     */
    /*
     *
     * Are JAX-WS client proxies thread safe? 
    According to the JAX-WS spec, * the client proxies are NOT thread safe. To write portable code, you should * treat them as non-thread safe and synchronize access or use a pool of * instances or similar. * */ @Component public class AppConnectionCache { private static final Logger logger = org.apache.logging.log4j.LogManager.getLogger(AppConnectionCache.class); private final Map connectionCache = new ConcurrentHashMap(); private int cachedConnectionId = 1; private static final int MAX_CUNCURRENT_THREADS = 20; private ScheduledExecutorService scheduler; private boolean forceRecaching = true; // first time cache @PostConstruct public void init() { logger.info("starting appConnectionCache"); logger.info("start caching connections"); ;; BasicThreadFactory factory = new BasicThreadFactory.Builder() .namingPattern("appconnectioncache-scheduler-thread-%d").build(); scheduler = Executors.newScheduledThreadPool(1, factory); scheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { initializeCache(); } }, 0, 10, TimeUnit.MINUTES); } public void destroy() { scheduler.shutdownNow(); } private void initializeCache() { if (!forceRecaching) { return; } try { loadCache(); forceRecaching = false; // this flag is used for initializing logger.info("connections creation finished successfully!"); } catch (MyAppException e) { logger.error("error while initializing the cache"); } } private void loadCache() throws MyAppException { logger.info("create and cache appservice connections"); for (int i = 0; i < MAX_CUNCURRENT_THREADS; i++) { tryConnect(i, true); } } public MyPort getMyPort() throws MyAppException { if (cachedConnectionId++ == MAX_CUNCURRENT_THREADS) { cachedConnectionId = 1; } return tryConnect(cachedConnectionId, forceRecaching); } private MyPort tryConnect(int threadNum, boolean forceConnect) throws MyAppException { boolean connect = true; int tryNum = 0; MyPort app = null; while (connect && !Thread.currentThread().isInterrupted()) { try { app = doConnect(threadNum, forceConnect); connect = false; } catch (Exception e) { tryNum = tryReconnect(tryNum, e); } } return app; } private int tryReconnect(int tryNum, Exception e) throws MyAppException { logger.warn(Thread.currentThread().getName() + " appservice service not available! : " + e); // try 10 times, if if (tryNum++ < 10) { try { logger.warn(Thread.currentThread().getName() + " wait 1 second"); Thread.sleep(1000); } catch (InterruptedException f) { // restore interrupt Thread.currentThread().interrupt(); } } else { logger.warn(" appservice could not connect, number of times tried: " + (tryNum - 1)); this.forceRecaching = true; throw new MyAppException(e); } logger.info(" try reconnect number: " + tryNum); return tryNum; } private MyPort doConnect(int threadNum, boolean forceConnect) throws InterruptedException { MyService service = connectionCache.get(threadNum); if (service == null || forceConnect) { logger.info("app service connects : " + (threadNum + 1) ); service = new MyService(); connectionCache.put(threadNum, service); logger.info("connect done for " + (threadNum + 1)); } return service.getAppPort(); } }

提交回复
热议问题