/* Copyright 2017 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * 2019.12.15-Changed constructor HuaweiApp * 2019.12.15-Changed method initializeApp * Huawei Technologies Co., Ltd. * */ package com.huawei.push.messaging; import com.google.common.collect.ImmutableList; import com.huawei.push.util.ValidatorUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.text.MessageFormat; import java.util.*; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * The entry point of Huawei Java SDK. *

{@link HuaweiApp#initializeApp(HuaweiOption)} initializes the app instance. */ public class HuaweiApp { private static final Logger logger = LoggerFactory.getLogger(HuaweiApp.class); private String appId; private HuaweiOption option; /** Global lock */ private static final Object appsLock = new Object(); /** Store a map of */ private static final Map instances = new HashMap<>(); /** HuaweiMessaging can be added in the pattern of service, whcih is designed for Scalability */ private final Map services = new HashMap<>(); private TokenRefresher tokenRefresher; private volatile ScheduledExecutorService scheduledExecutor; private ThreadManager threadManager; private ThreadManager.HuaweiExecutors executors; private final AtomicBoolean deleted = new AtomicBoolean(); /** lock for synchronizing all internal HuaweiApp state changes */ private final Object lock = new Object(); private HuaweiApp(HuaweiOption option) { ValidatorUtils.checkArgument(option != null, "HuaweiOption must not be null"); this.option = option; this.appId = option.getCredential().getAppId(); this.tokenRefresher = new TokenRefresher(this); this.threadManager = option.getThreadManager(); this.executors = threadManager.getHuaweiExecutors(this); } public HuaweiOption getOption() { return option; } public String getAppId() { return appId; } /** * Returns the instance identified by the unique appId, or throws if it does not exist. * * @param appId represents the id of the {@link HuaweiApp} instance. * @return the {@link HuaweiApp} corresponding to the id. * @throws IllegalStateException if the {@link HuaweiApp} was not initialized, either via {@link * #initializeApp(HuaweiOption)} or {@link #getApps()}. */ public static HuaweiApp getInstance(HuaweiOption option) { String appId = option.getCredential().getAppId(); synchronized (appsLock) { HuaweiApp app = instances.get(appId); if (app != null) { return app; } // String errorMessage = MessageFormat.format("HuaweiApp with id {0} doesn't exist", appId); // throw new IllegalStateException(errorMessage); return initializeApp(option); } } /** * Initializes the {@link HuaweiApp} instance using the given option. * * @throws IllegalStateException if the app instance has already been initialized. */ public static HuaweiApp initializeApp(HuaweiOption option) { String appId = option.getCredential().getAppId(); final HuaweiApp app; synchronized (appsLock) { if (!instances.containsKey(appId)) { ValidatorUtils.checkState(!instances.containsKey(appId), "HuaweiApp with id " + appId + " already exists!"); app = new HuaweiApp(option); instances.put(appId, app); app.startTokenRefresher(); } else { app = getInstance(option); } } return app; } /** Returns a list of all HuaweiApps. */ public static List getApps() { synchronized (appsLock) { return ImmutableList.copyOf(instances.values()); } } /** * Get all appIds which are be stored in the instances */ public static List getAllAppIds() { Set allAppIds = new HashSet<>(); synchronized (appsLock) { for (HuaweiApp app : instances.values()) { allAppIds.add(app.getAppId()); } } List sortedIdList = new ArrayList<>(allAppIds); Collections.sort(sortedIdList); return sortedIdList; } /** * Deletes the {@link HuaweiApp} and all its data. All calls to this {@link HuaweiApp} * instance will throw once it has been called. * *

A no-op if delete was called before. */ public void delete() { synchronized (lock) { boolean valueChanged = deleted.compareAndSet(false /* expected */, true); if (!valueChanged) { return; } try { this.getOption().getHttpClient().close(); this.getOption().getCredential().getHttpClient().close(); } catch (IOException e) { logger.debug("Fail to close httpClient"); } for (HuaweiService service : services.values()) { service.destroy(); } services.clear(); tokenRefresher.stop(); threadManager.releaseHuaweiExecutors(this, executors); if (scheduledExecutor != null) { scheduledExecutor.shutdown(); scheduledExecutor = null; } } synchronized (appsLock) { instances.remove(this.getAppId()); } } /** * Check the app is not deleted, whcic is the premisi of some methods */ private void checkNotDeleted() { String errorMessage = MessageFormat.format("HuaweiApp with id {0} was deleted", getAppId()); ValidatorUtils.checkState(!deleted.get(), errorMessage); } /** * Singleton mode, ensure the scheduleExecutor is singleton */ private ScheduledExecutorService singleScheduledExecutorService() { if (scheduledExecutor == null) { synchronized (lock) { checkNotDeleted(); if (scheduledExecutor == null) { scheduledExecutor = new HuaweiScheduledExecutor(getThreadFactory(), "huawei-scheduled-worker"); } } } return scheduledExecutor; } public ThreadFactory getThreadFactory() { return threadManager.getThreadFactory(); } private ScheduledExecutorService getScheduledExecutorService() { return singleScheduledExecutorService(); } ScheduledFuture schedule(Runnable runnable, long initialDelay, long period) { return getScheduledExecutorService().scheduleWithFixedDelay(runnable, initialDelay, period, TimeUnit.MILLISECONDS); } /** * Add service to the app, such as HuaweiMessaging, other services can be added if needed */ void addService(HuaweiService service) { synchronized (lock) { checkNotDeleted(); ValidatorUtils.checkArgument(!services.containsKey(service.getId()), "service already exists"); services.put(service.getId(), service); } } HuaweiService getService(String id) { synchronized (lock) { return services.get(id); } } /** * Start the scheduled task for refreshing token automatically */ public void startTokenRefresher() { synchronized (lock) { checkNotDeleted(); tokenRefresher.start(); } } /** It is just for test */ public static void clearInstancesForTest() { synchronized (appsLock) { //copy before delete for (HuaweiApp app : ImmutableList.copyOf(instances.values())) { app.delete(); } instances.clear(); } } }