All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gravitee.node.license.LicenseService Maven / Gradle / Ivy

There is a newer version: 7.0.0-alpha.13
Show newest version
/**
 * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
 *
 * 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.
 */
package io.gravitee.node.license;

import io.gravitee.common.service.AbstractService;
import io.gravitee.node.api.Node;
import io.gravitee.node.license.license3j.License3JLicense;
import io.gravitee.node.license.management.NodeLicenseManagementEndpoint;
import io.gravitee.node.management.http.endpoint.ManagementEndpointManager;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax0.license3j.Feature;
import javax0.license3j.License;
import javax0.license3j.io.LicenseReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

/**
 * @author David BRASSELY (david.brassely at graviteesource.com)
 * @author GraviteeSource Team
 */
public class LicenseService extends AbstractService {

    private final Logger logger = LoggerFactory.getLogger(LicenseService.class);

    private static final String GRAVITEE_HOME_PROPERTY = "gravitee.home";
    private static final String GRAVITEE_LICENSE_PROPERTY = "gravitee.license";
    private static final String GRAVITEE_LICENSE_KEY = "license.key";
    private static final String LICENSE_EXPIRE_AT = "expiryDate";

    private static final long DAY_AS_LONG = 24 * 60 * 60 * 1000L;

    @Autowired
    private ManagementEndpointManager managementEndpointManager;

    @Autowired
    private Node node;

    @Autowired
    private Environment environment;

    @Autowired
    private io.gravitee.node.api.license.NodeLicenseService nodeLicenseService;

    // prettier-ignore
    private final byte [] key = new byte[] {
            (byte)0x52,
            (byte)0x53, (byte)0x41, (byte)0x00, (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x22, (byte)0x30,
            (byte)0x0D, (byte)0x06, (byte)0x09, (byte)0x2A, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xF7,
            (byte)0x0D, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x03, (byte)0x82,
            (byte)0x01, (byte)0x0F, (byte)0x00, (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x0A, (byte)0x02,
            (byte)0x82, (byte)0x01, (byte)0x01, (byte)0x00, (byte)0xD8, (byte)0x59, (byte)0xEC, (byte)0xB6,
            (byte)0x27, (byte)0xF2, (byte)0x20, (byte)0x2F, (byte)0x7A, (byte)0x39, (byte)0x86, (byte)0x2B,
            (byte)0x62, (byte)0xB6, (byte)0xEA, (byte)0x5B, (byte)0xE4, (byte)0x80, (byte)0xA0, (byte)0x32,
            (byte)0x35, (byte)0xB3, (byte)0xC8, (byte)0xD5, (byte)0x4E, (byte)0xB7, (byte)0xA1, (byte)0xFE,
            (byte)0x15, (byte)0x84, (byte)0xC7, (byte)0x75, (byte)0x66, (byte)0x8F, (byte)0x48, (byte)0xF1,
            (byte)0xD6, (byte)0x30, (byte)0x7B, (byte)0x39, (byte)0xB7, (byte)0xD7, (byte)0x48, (byte)0x4F,
            (byte)0xAF, (byte)0x38, (byte)0xC6, (byte)0xB9, (byte)0xBC, (byte)0x3C, (byte)0xEB, (byte)0xED,
            (byte)0xB3, (byte)0x03, (byte)0xEE, (byte)0x1B, (byte)0x9D, (byte)0x85, (byte)0x8B, (byte)0xFC,
            (byte)0x93, (byte)0x56, (byte)0x5C, (byte)0x09, (byte)0x91, (byte)0x41, (byte)0x22, (byte)0xE7,
            (byte)0x4C, (byte)0xF6, (byte)0x94, (byte)0x9D, (byte)0xC5, (byte)0x71, (byte)0x3C, (byte)0x2D,
            (byte)0xE4, (byte)0x4C, (byte)0x0E, (byte)0xD5, (byte)0x3D, (byte)0x6F, (byte)0xD7, (byte)0x20,
            (byte)0x6C, (byte)0xD8, (byte)0xDD, (byte)0x12, (byte)0x4D, (byte)0xEA, (byte)0x55, (byte)0xDD,
            (byte)0x26, (byte)0xA3, (byte)0x25, (byte)0xE3, (byte)0x83, (byte)0x9C, (byte)0x92, (byte)0x15,
            (byte)0xC6, (byte)0x45, (byte)0xFE, (byte)0x9A, (byte)0x0F, (byte)0x47, (byte)0x86, (byte)0x45,
            (byte)0x04, (byte)0xB6, (byte)0x44, (byte)0xFC, (byte)0x01, (byte)0x86, (byte)0x2A, (byte)0xF6,
            (byte)0xE2, (byte)0xFD, (byte)0x37, (byte)0xF1, (byte)0xBB, (byte)0x70, (byte)0xAD, (byte)0x15,
            (byte)0xE0, (byte)0x7C, (byte)0xB3, (byte)0x94, (byte)0xDE, (byte)0x2F, (byte)0xD2, (byte)0xC4,
            (byte)0x4F, (byte)0xCB, (byte)0xA7, (byte)0x31, (byte)0x87, (byte)0x66, (byte)0xD0, (byte)0xF7,
            (byte)0x5D, (byte)0x09, (byte)0x84, (byte)0x4B, (byte)0xB7, (byte)0x4B, (byte)0xE6, (byte)0x1C,
            (byte)0xA7, (byte)0xD9, (byte)0xBE, (byte)0x9E, (byte)0xAD, (byte)0xE7, (byte)0x03, (byte)0xCC,
            (byte)0xAB, (byte)0x04, (byte)0x4D, (byte)0xDF, (byte)0x92, (byte)0x8E, (byte)0xC5, (byte)0xA1,
            (byte)0x04, (byte)0xD0, (byte)0x7F, (byte)0x89, (byte)0x71, (byte)0x2D, (byte)0x6D, (byte)0x8F,
            (byte)0xCC, (byte)0x1E, (byte)0x25, (byte)0x5E, (byte)0x66, (byte)0xBF, (byte)0xA9, (byte)0xF8,
            (byte)0x8C, (byte)0xEF, (byte)0x8E, (byte)0x6A, (byte)0xFA, (byte)0xCE, (byte)0xFA, (byte)0x79,
            (byte)0xA0, (byte)0xDE, (byte)0x72, (byte)0xCB, (byte)0xBC, (byte)0x90, (byte)0x8E, (byte)0x29,
            (byte)0x4C, (byte)0x40, (byte)0x05, (byte)0x61, (byte)0x5A, (byte)0x44, (byte)0x0D, (byte)0xC7,
            (byte)0x46, (byte)0x65, (byte)0xA4, (byte)0x2A, (byte)0xD3, (byte)0xAA, (byte)0xEC, (byte)0x83,
            (byte)0x78, (byte)0x52, (byte)0x04, (byte)0x2B, (byte)0xB4, (byte)0x15, (byte)0xE3, (byte)0x66,
            (byte)0xD3, (byte)0xB5, (byte)0xE5, (byte)0xE7, (byte)0x04, (byte)0xB7, (byte)0xDB, (byte)0xFC,
            (byte)0x46, (byte)0x32, (byte)0xC5, (byte)0x71, (byte)0x93, (byte)0xDD, (byte)0xE3, (byte)0x07,
            (byte)0xD0, (byte)0xCD, (byte)0xD8, (byte)0x0E, (byte)0xDE, (byte)0xC1, (byte)0xA8, (byte)0xA6,
            (byte)0x0F, (byte)0x45, (byte)0x8F, (byte)0x06, (byte)0x51, (byte)0xFE, (byte)0x72, (byte)0xB7,
            (byte)0x61, (byte)0xCA, (byte)0x29, (byte)0x67, (byte)0x02, (byte)0x03, (byte)0x01, (byte)0x00,
            (byte)0x01,
    };

    private LicenseReader reader;
    private License license;

    private Timer checkerTimer;
    private LicenseChecker checkerTask;
    private LicenseWatcher licenseWatcher;

    public io.gravitee.node.api.license.License getLicense() {
        return this.license == null ? null : new License3JLicense(this.license);
    }

    @Override
    protected void doStart() throws Exception {
        super.doStart();

        this.loadLicense();
        this.startLicenseChecker();
        this.startLicenseWatcher();

        managementEndpointManager.register(new NodeLicenseManagementEndpoint(node));
    }

    private void startLicenseChecker() {
        checkerTimer = new Timer("gravitee-license-checker");

        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.add(Calendar.DATE, 1);
        c.set(Calendar.HOUR, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);

        checkerTask = new LicenseChecker();
        checkerTimer.schedule(checkerTask, c.getTime(), DAY_AS_LONG);
    }

    private void startLicenseWatcher() {
        String licenseKey = environment.getProperty(GRAVITEE_LICENSE_KEY);
        if (!StringUtils.hasLength(licenseKey)) {
            licenseWatcher = new LicenseWatcher(new File(getLicenseFile()));
            licenseWatcher.setName("gravitee-license-watcher");
            licenseWatcher.start();
        }
    }

    @Override
    protected void doStop() throws Exception {
        super.doStop();
        if (checkerTimer != null) {
            checkerTimer.cancel();
        }

        if (licenseWatcher != null) {
            licenseWatcher.close();
        }

        if (reader != null) {
            reader.close();
        }
    }

    @Override
    protected String name() {
        return "License service";
    }

    public void loadLicense() throws Exception {
        byte[] licenseContent = getLicenseContent();

        if (licenseContent.length > 0) {
            try {
                reader = new LicenseReader(new ByteArrayInputStream(licenseContent));
                license = reader.read();
                this.dump();
                this.verify();
                nodeLicenseService.refresh();
            } catch (IOException ioe) {
                logger.error("License file can not be read", ioe);
            }
        }
    }

    private byte[] getLicenseContent() {
        byte[] licenseContent;

        String licenseKey = environment.getProperty(GRAVITEE_LICENSE_KEY);
        if (StringUtils.hasLength(licenseKey)) {
            try {
                return Base64.getDecoder().decode(licenseKey);
            } catch (Exception ex) {
                logger.error("Can't decode the license key.", ex);
                return new byte[0];
            }
        }

        try {
            licenseContent = Files.readAllBytes(Paths.get(getLicenseFile()));
        } catch (IOException e) {
            logger.info("No license file found. Some plugins may be disabled");
            return new byte[0];
        }

        return licenseContent;
    }

    private String getLicenseFile() {
        String licenseFile = System.getProperty(GRAVITEE_LICENSE_PROPERTY);
        if (licenseFile == null || licenseFile.isEmpty()) {
            licenseFile = System.getProperty(GRAVITEE_HOME_PROPERTY) + File.separator + "license" + File.separator + "license.key";
        }

        return licenseFile;
    }

    private void verify() throws Exception {
        boolean valid = license.isOK(key);

        if (!valid) {
            logger.error("License is not valid. Please contact GraviteeSource to ask for a valid license.");
            stopNode();
        }

        if (license.isExpired()) {
            logger.error("License is expired. Please contact GraviteeSource to ask for a renewed license.");
            stopNode();
        }

        Date expiration = license.get(LICENSE_EXPIRE_AT).getDate();
        long remainingDays = Math.round((expiration.getTime() - System.currentTimeMillis()) / (double) 86400000);

        if (remainingDays <= 30) {
            logger.warn("License will be no longer valid in {} days. Please contact GraviteeSource to renew it.", remainingDays);
        }
    }

    private void dump() {
        logger.info("License information:");
        Map features = license.getFeatures();
        features.forEach((name, feature) -> logger.info("\t{}: {}", name, feature.valueString()));
    }

    private class LicenseChecker extends TimerTask {

        @Override
        public void run() {
            try {
                LicenseService.this.verify();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private class LicenseWatcher extends Thread {

        private final File file;
        private final AtomicBoolean stop = new AtomicBoolean(false);

        LicenseWatcher(File file) {
            this.file = file;
        }

        @Override
        public void run() {
            logger.debug("Watching license for next changes: {}", file.getAbsolutePath());

            try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
                Path path = file.toPath().getParent();
                path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
                while (!stop.get()) {
                    WatchKey watchKey;
                    try {
                        watchKey = watcher.poll(25, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        return;
                    }
                    if (watchKey == null) {
                        Thread.yield();
                        continue;
                    }

                    for (WatchEvent event : watchKey.pollEvents()) {
                        WatchEvent.Kind kind = event.kind();

                        @SuppressWarnings("unchecked")
                        WatchEvent ev = (WatchEvent) event;
                        Path filename = ev.context();

                        if (kind == StandardWatchEventKinds.OVERFLOW) {
                            Thread.yield();
                            continue;
                        } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY && filename.toString().equals(file.getName())) {
                            LicenseService.this.loadLicense();
                        }
                        boolean valid = watchKey.reset();
                        if (!valid) {
                            break;
                        }
                    }
                    Thread.yield();
                }
            } catch (Throwable e) {
                // Log or rethrow the error
            }
        }

        void close() {
            stop.set(true);
        }
    }

    private void stopNode() throws Exception {
        node.stop();
        System.exit(0);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy