Search in sources :

Example 36 with ClassDefinition

use of lucee.runtime.db.ClassDefinition in project Lucee by lucee.

the class XMLConfigWebFactory method loadTag.

/*
	 * private static void loadLabel(ConfigServerImpl configServer, ConfigImpl
	 * config, Document doc) { // do only for web config if(configServer!=null
	 * && config instanceof ConfigWebImpl) { ConfigWebImpl cs=(ConfigWebImpl)
	 * config; String hash=SystemUtil.hash(cs.getServletContext());
	 * config.setLabel(hash);
	 * 
	 * Map<String, String> labels = configServer.getLabels(); if(labels!=null) {
	 * String label = labels.get(hash); if(!StringUtil.isEmpty(label)) {
	 * print.o("label:"+label); config.setLabel(label);
	 * config.getFactory().setLabel(label); } } } }
	 */
private static void loadTag(ConfigServerImpl configServer, ConfigImpl config, Document doc) {
    Element parent = getChildByName(doc.getDocumentElement(), "tags");
    {
        Element[] tags = getChildren(parent, "tag");
        Element tag;
        ClassDefinition cd;
        String nss, ns, n;
        if (tags != null) {
            for (int i = 0; i < tags.length; i++) {
                tag = tags[i];
                ns = getAttr(tag, "namespace");
                nss = getAttr(tag, "namespace-seperator");
                n = getAttr(tag, "name");
                cd = getClassDefinition(tag, "", config.getIdentification());
                config.addTag(ns, nss, n, CFMLEngine.DIALECT_BOTH, cd);
            }
        }
    }
    // set tag default values
    Element[] defaults = getChildren(parent, "default");
    if (!ArrayUtil.isEmpty(defaults)) {
        Element def;
        String tagName, attrName, attrValue;
        Struct tags = new StructImpl(), tag;
        Map<Key, Map<Key, Object>> trg = new HashMap<Key, Map<Key, Object>>();
        for (int i = 0; i < defaults.length; i++) {
            def = defaults[i];
            tagName = getAttr(def, "tag");
            attrName = getAttr(def, "attribute-name");
            attrValue = getAttr(def, "attribute-value");
            if (StringUtil.isEmpty(tagName) || StringUtil.isEmpty(attrName) || StringUtil.isEmpty(attrValue))
                continue;
            tag = (Struct) tags.get(tagName, null);
            if (tag == null) {
                tag = new StructImpl();
                tags.setEL(tagName, tag);
            }
            tag.setEL(attrName, attrValue);
            ApplicationContextSupport.initTagDefaultAttributeValues(config, trg, tags, CFMLEngine.DIALECT_CFML);
            ApplicationContextSupport.initTagDefaultAttributeValues(config, trg, tags, CFMLEngine.DIALECT_LUCEE);
            config.setTagDefaultAttributeValues(trg);
        }
    // initTagDefaultAttributeValues
    }
}
Also used : StructImpl(lucee.runtime.type.StructImpl) LinkedHashMap(java.util.LinkedHashMap) HashMap(java.util.HashMap) Element(org.w3c.dom.Element) ClassDefinition(lucee.runtime.db.ClassDefinition) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap) HashMap(java.util.HashMap) Key(lucee.runtime.type.Collection.Key) lucee.aprint(lucee.aprint) Struct(lucee.runtime.type.Struct)

Example 37 with ClassDefinition

use of lucee.runtime.db.ClassDefinition in project Lucee by lucee.

the class XMLConfigWebFactory method loadVideo.

private static void loadVideo(ConfigServerImpl configServer, ConfigImpl config, Document doc) {
    Element video = config instanceof ConfigServerImpl ? getChildByName(doc.getDocumentElement(), "video") : null;
    boolean hasCS = configServer != null;
    ClassDefinition cd = null;
    // video-executer
    if (video != null) {
        cd = getClassDefinition(video, "video-executer-", config.getIdentification());
    }
    if (cd != null && cd.hasClass()) {
        try {
            Class clazz = cd.getClazz();
            if (!Reflector.isInstaneOf(clazz, VideoExecuter.class))
                throw new ApplicationException("class [" + cd + "] does not implement interface [" + VideoExecuter.class.getName() + "]");
            config.setVideoExecuterClass(clazz);
        } catch (Exception e) {
            SystemOut.printDate(e);
        }
    } else if (hasCS)
        config.setVideoExecuterClass(configServer.getVideoExecuterClass());
}
Also used : ApplicationException(lucee.runtime.exp.ApplicationException) VideoExecuter(lucee.runtime.video.VideoExecuter) Element(org.w3c.dom.Element) CFXTagClass(lucee.runtime.cfx.customtag.CFXTagClass) CPPCFXTagClass(lucee.runtime.cfx.customtag.CPPCFXTagClass) JavaCFXTagClass(lucee.runtime.cfx.customtag.JavaCFXTagClass) ClassDefinition(lucee.runtime.db.ClassDefinition) FunctionLibException(lucee.transformer.library.function.FunctionLibException) PageException(lucee.runtime.exp.PageException) InvocationTargetException(java.lang.reflect.InvocationTargetException) SecurityException(lucee.runtime.exp.SecurityException) TagLibException(lucee.transformer.library.tag.TagLibException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) SQLException(java.sql.SQLException) IOException(java.io.IOException) BundleException(org.osgi.framework.BundleException) SAXException(org.xml.sax.SAXException) ClassException(lucee.commons.lang.ClassException) MalformedURLException(java.net.MalformedURLException) ExpressionException(lucee.runtime.exp.ExpressionException) ApplicationException(lucee.runtime.exp.ApplicationException)

Example 38 with ClassDefinition

use of lucee.runtime.db.ClassDefinition in project Lucee by lucee.

the class XMLConfigWebFactory method loadCFX.

/**
 * @param configServer
 * @param config
 * @param doc
 */
private static void loadCFX(ConfigServerImpl configServer, ConfigImpl config, Document doc) {
    boolean hasAccess = ConfigWebUtil.hasAccess(config, SecurityManager.TYPE_CFX_SETTING);
    Map<String, CFXTagClass> map = MapFactory.<String, CFXTagClass>getConcurrentMap();
    if (configServer != null) {
        try {
            if (configServer.getCFXTagPool() != null) {
                Map<String, CFXTagClass> classes = configServer.getCFXTagPool().getClasses();
                Iterator<Entry<String, CFXTagClass>> it = classes.entrySet().iterator();
                Entry<String, CFXTagClass> e;
                while (it.hasNext()) {
                    e = it.next();
                    map.put(e.getKey(), e.getValue().cloneReadOnly());
                }
            }
        } catch (SecurityException e) {
        }
    }
    if (hasAccess) {
        if (configServer == null) {
            System.setProperty("cfx.bin.path", config.getConfigDir().getRealResource("bin").getAbsolutePath());
        }
        // Java CFX Tags
        Element cfxTagsParent = getChildByName(doc.getDocumentElement(), "ext-tags", false, true);
        if (cfxTagsParent == null)
            cfxTagsParent = getChildByName(doc.getDocumentElement(), "cfx-tags", false, true);
        if (cfxTagsParent == null)
            cfxTagsParent = getChildByName(doc.getDocumentElement(), "ext-tags");
        boolean oldStyle = cfxTagsParent.getNodeName().equals("cfx-tags");
        Element[] cfxTags = oldStyle ? getChildren(cfxTagsParent, "cfx-tag") : getChildren(cfxTagsParent, "ext-tag");
        for (int i = 0; i < cfxTags.length; i++) {
            String type = getAttr(cfxTags[i], "type");
            if (type != null) {
                // Java CFX Tags
                if (type.equalsIgnoreCase("java")) {
                    String name = getAttr(cfxTags[i], "name");
                    ClassDefinition cd = getClassDefinition(cfxTags[i], "", config.getIdentification());
                    if (!StringUtil.isEmpty(name) && cd.hasClass()) {
                        map.put(name.toLowerCase(), new JavaCFXTagClass(name, cd));
                    }
                } else // C++ CFX Tags
                if (type.equalsIgnoreCase("cpp")) {
                    String name = getAttr(cfxTags[i], "name");
                    String serverLibrary = getAttr(cfxTags[i], "server-library");
                    String procedure = getAttr(cfxTags[i], "procedure");
                    boolean keepAlive = Caster.toBooleanValue(getAttr(cfxTags[i], "keep-alive"), false);
                    if (!StringUtil.isEmpty(name) && !StringUtil.isEmpty(serverLibrary) && !StringUtil.isEmpty(procedure)) {
                        map.put(name.toLowerCase(), new CPPCFXTagClass(name, serverLibrary, procedure, keepAlive));
                    }
                }
            }
        }
    }
    config.setCFXTagPool(map);
}
Also used : Element(org.w3c.dom.Element) CFXTagClass(lucee.runtime.cfx.customtag.CFXTagClass) CPPCFXTagClass(lucee.runtime.cfx.customtag.CPPCFXTagClass) JavaCFXTagClass(lucee.runtime.cfx.customtag.JavaCFXTagClass) SecurityException(lucee.runtime.exp.SecurityException) ClassDefinition(lucee.runtime.db.ClassDefinition) CPPCFXTagClass(lucee.runtime.cfx.customtag.CPPCFXTagClass) lucee.aprint(lucee.aprint) DumpWriterEntry(lucee.runtime.dump.DumpWriterEntry) Entry(java.util.Map.Entry) GatewayEntry(lucee.runtime.gateway.GatewayEntry) JavaCFXTagClass(lucee.runtime.cfx.customtag.JavaCFXTagClass)

Example 39 with ClassDefinition

use of lucee.runtime.db.ClassDefinition in project Lucee by lucee.

the class XMLConfigAdmin method removeRHExtension.

/**
 * removes an installed extension from the system
 *
 * @param config
 * @param rhe extension to remove
 * @param replacementRH the extension that will replace this extension, so do not remove parts defined in this extension.
 * @throws PageException
 */
private void removeRHExtension(Config config, RHExtension rhe, RHExtension replacementRH, boolean deleteExtension) throws PageException {
    ConfigImpl ci = ((ConfigImpl) config);
    Log logger = ci.getLog("deploy");
    try {
        // remove the bundles
        BundleDefinition[] candidatesToRemove = OSGiUtil.toBundleDefinitions(rhe.getBundles());
        if (replacementRH != null) {
            // spare bundles used in the new extension as well
            Map<String, BundleDefinition> notRemove = toMap(OSGiUtil.toBundleDefinitions(replacementRH.getBundles()));
            List<BundleDefinition> tmp = new ArrayList<OSGiUtil.BundleDefinition>();
            String key;
            for (int i = 0; i < candidatesToRemove.length; i++) {
                key = candidatesToRemove[i].getName() + "|" + candidatesToRemove[i].getVersionAsString();
                if (notRemove.containsKey(key))
                    continue;
                tmp.add(candidatesToRemove[i]);
            }
            candidatesToRemove = tmp.toArray(new BundleDefinition[tmp.size()]);
        }
        XMLConfigAdmin.cleanBundles(rhe, ci, candidatesToRemove);
        // FLD
        // MUST check if others use one of this fld
        removeFLDs(rhe.getFlds());
        // TLD
        // MUST check if others use one of this tld
        removeTLDs(rhe.getTlds());
        // Tag
        removeTags(rhe.getTags());
        // Functions
        removeFunctions(rhe.getFunctions());
        // Event Gateway
        removeEventGateways(rhe.getEventGateways());
        // context
        // MUST check if others use one of this
        removeContext(config, false, rhe.getContexts());
        // web contextS
        // MUST check if others use one of this
        removeWebContexts(config, false, rhe.getWebContexts());
        // applications
        // MUST check if others use one of this
        removeApplications(config, rhe.getWebContexts());
        // plugins
        // MUST check if others use one of this
        removePlugins(config, rhe.getWebContexts());
        // remove cache handler
        if (!ArrayUtil.isEmpty(rhe.getCacheHandlers())) {
            Iterator<Map<String, String>> itl = rhe.getCacheHandlers().iterator();
            Map<String, String> map;
            while (itl.hasNext()) {
                map = itl.next();
                ClassDefinition cd = RHExtension.toClassDefinition(config, map, null);
                String _id = map.get("id");
                if (!StringUtil.isEmpty(_id) && cd != null && cd.hasClass()) {
                    _removeCacheHandler(_id);
                // reload=true;
                }
                logger.info("extension", "remove cache handler [" + cd + "] from extension [" + rhe.getName() + ":" + rhe.getVersion() + "]");
            }
        }
        // remove cache
        if (!ArrayUtil.isEmpty(rhe.getCaches())) {
            Iterator<Map<String, String>> itl = rhe.getCaches().iterator();
            Map<String, String> map;
            while (itl.hasNext()) {
                map = itl.next();
                ClassDefinition cd = RHExtension.toClassDefinition(config, map, null);
                if (cd != null && cd.isBundle()) {
                    _removeCache(cd);
                // reload=true;
                }
                logger.info("extension", "remove cache handler [" + cd + "] from extension [" + rhe.getName() + ":" + rhe.getVersion() + "]");
            }
        }
        // remove Search
        if (!ArrayUtil.isEmpty(rhe.getSearchs())) {
            Iterator<Map<String, String>> itl = rhe.getSearchs().iterator();
            Map<String, String> map;
            while (itl.hasNext()) {
                map = itl.next();
                ClassDefinition cd = RHExtension.toClassDefinition(config, map, null);
                if (cd != null && cd.hasClass()) {
                    _removeSearchEngine();
                // reload=true;
                }
                logger.info("extension", "remove search engine [" + cd + "] from extension [" + rhe.getName() + ":" + rhe.getVersion() + "]");
            }
        }
        // remove resource
        if (!ArrayUtil.isEmpty(rhe.getResources())) {
            Iterator<Map<String, String>> itl = rhe.getResources().iterator();
            Map<String, String> map;
            while (itl.hasNext()) {
                map = itl.next();
                ClassDefinition cd = RHExtension.toClassDefinition(config, map, null);
                String scheme = map.get("scheme");
                if (cd != null && cd.hasClass()) {
                    _removeResourceProvider(scheme);
                }
                logger.info("extension", "remove resource [" + cd + "] from extension [" + rhe.getName() + ":" + rhe.getVersion() + "]");
            }
        }
        // remove AMF
        if (!ArrayUtil.isEmpty(rhe.getAMFs())) {
            Iterator<Map<String, String>> itl = rhe.getAMFs().iterator();
            Map<String, String> map;
            while (itl.hasNext()) {
                map = itl.next();
                ClassDefinition cd = RHExtension.toClassDefinition(config, map, null);
                if (cd != null && cd.hasClass()) {
                    _removeAMFEngine();
                // reload=true;
                }
                logger.info("extension", "remove search engine [" + cd + "] from extension [" + rhe.getName() + ":" + rhe.getVersion() + "]");
            }
        }
        // remove orm
        if (!ArrayUtil.isEmpty(rhe.getOrms())) {
            Iterator<Map<String, String>> itl = rhe.getOrms().iterator();
            Map<String, String> map;
            while (itl.hasNext()) {
                map = itl.next();
                ClassDefinition cd = RHExtension.toClassDefinition(config, map, null);
                if (cd != null && cd.hasClass()) {
                    _removeORMEngine();
                // reload=true;
                }
                logger.info("extension", "remove orm engine [" + cd + "] from extension [" + rhe.getName() + ":" + rhe.getVersion() + "]");
            }
        }
        // remove monitor
        if (!ArrayUtil.isEmpty(rhe.getMonitors())) {
            Iterator<Map<String, String>> itl = rhe.getMonitors().iterator();
            Map<String, String> map;
            String name;
            while (itl.hasNext()) {
                map = itl.next();
                // ClassDefinition cd = RHExtension.toClassDefinition(config,map);
                // if(cd.hasClass()) {
                _removeMonitor(map.get("type"), name = map.get("name"));
                // reload=true;
                // }
                logger.info("extension", "remove monitor [" + name + "] from extension [" + rhe.getName() + ":" + rhe.getVersion() + "]");
            }
        }
        // remove jdbc
        if (!ArrayUtil.isEmpty(rhe.getJdbcs())) {
            Iterator<Map<String, String>> itl = rhe.getJdbcs().iterator();
            Map<String, String> map;
            while (itl.hasNext()) {
                map = itl.next();
                ClassDefinition cd = RHExtension.toClassDefinition(config, map, null);
                if (cd != null && cd.isBundle()) {
                    _removeJDBCDriver(cd);
                }
                logger.info("extension", "remove JDBC Driver [" + cd + "] from extension [" + rhe.getName() + ":" + rhe.getVersion() + "]");
            }
        }
        // remove mapping
        if (!ArrayUtil.isEmpty(rhe.getMappings())) {
            Iterator<Map<String, String>> itl = rhe.getMappings().iterator();
            Map<String, String> map;
            String virtual;
            while (itl.hasNext()) {
                map = itl.next();
                virtual = map.get("virtual");
                _removeMapping(virtual);
                logger.info("extension", "remove Mapping [" + virtual + "]");
            }
        }
        // remove event-gateway-instance
        if (!ArrayUtil.isEmpty(rhe.getEventGatewayInstances())) {
            Iterator<Map<String, Object>> itl = rhe.getEventGatewayInstances().iterator();
            Map<String, Object> map;
            String id;
            while (itl.hasNext()) {
                map = itl.next();
                id = Caster.toString(map.get("id"), null);
                if (!StringUtil.isEmpty(id)) {
                    _removeGatewayEntry(id);
                    logger.info("extension", "remove event gateway entry [" + id + "]");
                }
            }
        }
        // Loop Files
        ZipInputStream zis = new ZipInputStream(IOUtil.toBufferedInputStream(rhe.getExtensionFile().getInputStream()));
        String type = ci instanceof ConfigWeb ? "web" : "server";
        try {
            ZipEntry entry;
            String path;
            String fileName;
            Resource tmp;
            while ((entry = zis.getNextEntry()) != null) {
                path = entry.getName();
                fileName = fileName(entry);
                // archives
                if (!entry.isDirectory() && (startsWith(path, type, "archives") || startsWith(path, type, "mappings"))) {
                    String sub = subFolder(entry);
                    logger.log(Log.LEVEL_INFO, "extension", "remove archive " + sub + " registered as a mapping");
                    tmp = SystemUtil.getTempFile(".lar", false);
                    IOUtil.copy(zis, tmp, false);
                    removeArchive(tmp);
                }
                zis.closeEntry();
            }
        } finally {
            IOUtil.closeEL(zis);
        }
        // now we can delete the extension
        if (deleteExtension)
            rhe.getExtensionFile().delete();
    } catch (Throwable t) {
        ExceptionUtil.rethrowIfNecessary(t);
        // failed to uninstall, so we install it again
        try {
            updateRHExtension(config, rhe.getExtensionFile(), true);
        // RHExtension.install(config, rhe.getExtensionFile());
        } catch (Throwable t2) {
            ExceptionUtil.rethrowIfNecessary(t2);
        }
        throw Caster.toPageException(t);
    }
}
Also used : Log(lucee.commons.io.log.Log) ZipEntry(java.util.zip.ZipEntry) ArrayList(java.util.ArrayList) Resource(lucee.commons.io.res.Resource) ClassDefinition(lucee.runtime.db.ClassDefinition) BundleDefinition(lucee.runtime.osgi.OSGiUtil.BundleDefinition) OSGiUtil(lucee.runtime.osgi.OSGiUtil) ZipInputStream(java.util.zip.ZipInputStream) CreateObject(lucee.runtime.functions.other.CreateObject) Map(java.util.Map) HashMap(java.util.HashMap)

Example 40 with ClassDefinition

use of lucee.runtime.db.ClassDefinition in project Lucee by lucee.

the class AppListenerUtil method toDataSource.

public static DataSource toDataSource(Config config, String name, Struct data, Log log) throws PageException {
    String user = Caster.toString(data.get(KeyConstants._username, null), null);
    String pass = Caster.toString(data.get(KeyConstants._password, ""), "");
    if (StringUtil.isEmpty(user)) {
        user = null;
        pass = null;
    } else {
        user = user.trim();
        pass = pass.trim();
    }
    // first check for {class:... , connectionString:...}
    Object oConnStr = data.get(CONNECTION_STRING, null);
    if (oConnStr != null) {
        String className = Caster.toString(data.get(KeyConstants._class));
        if ("com.microsoft.jdbc.sqlserver.SQLServerDriver".equals(className)) {
            className = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
        }
        ClassDefinition cd = new ClassDefinitionImpl(className, Caster.toString(data.get(KeyConstants._bundleName, null), null), Caster.toString(data.get(KeyConstants._bundleVersion, null), null), ThreadLocalPageContext.getConfig().getIdentification());
        try {
            return ApplicationDataSource.getInstance(config, name, cd, Caster.toString(oConnStr), user, pass, Caster.toBooleanValue(data.get(BLOB, null), false), Caster.toBooleanValue(data.get(CLOB, null), false), Caster.toIntValue(data.get(CONNECTION_LIMIT, null), -1), Caster.toIntValue(data.get(CONNECTION_TIMEOUT, null), 1), Caster.toLongValue(data.get(META_CACHE_TIMEOUT, null), 60000L), Caster.toTimeZone(data.get(TIMEZONE, null), null), Caster.toIntValue(data.get(ALLOW, null), DataSource.ALLOW_ALL), Caster.toBooleanValue(data.get(STORAGE, null), false), Caster.toBooleanValue(data.get(READ_ONLY, null), false), log);
        } catch (Exception cnfe) {
            throw Caster.toPageException(cnfe);
        }
    }
    // then for {type:... , host:... , ...}
    String type = Caster.toString(data.get(KeyConstants._type));
    DataSourceDefintion dbt = DBUtil.getDataSourceDefintionForType(type, null);
    if (dbt == null)
        throw new ApplicationException("no datasource type [" + type + "] found");
    try {
        return new DataSourceImpl(config, null, name, dbt.classDefinition, Caster.toString(data.get(KeyConstants._host)), dbt.connectionString, Caster.toString(data.get(DATABASE)), Caster.toIntValue(data.get(KeyConstants._port, null), -1), user, pass, Caster.toIntValue(data.get(CONNECTION_LIMIT, null), -1), Caster.toIntValue(data.get(CONNECTION_TIMEOUT, null), 1), Caster.toLongValue(data.get(META_CACHE_TIMEOUT, null), 60000L), Caster.toBooleanValue(data.get(BLOB, null), false), Caster.toBooleanValue(data.get(CLOB, null), false), DataSource.ALLOW_ALL, Caster.toStruct(data.get(KeyConstants._custom, null), null, false), Caster.toBooleanValue(data.get(READ_ONLY, null), false), true, Caster.toBooleanValue(data.get(STORAGE, null), false), Caster.toTimeZone(data.get(TIMEZONE, null), null), "", ParamSyntax.toParamSyntax(data, ParamSyntax.DEFAULT), Caster.toBooleanValue(data.get("literalTimestampWithTSOffset", null), false), Caster.toBooleanValue(data.get("alwaysSetTimeout", null), false), log);
    } catch (Exception cnfe) {
        throw Caster.toPageException(cnfe);
    }
}
Also used : ClassDefinitionImpl(lucee.transformer.library.ClassDefinitionImpl) ApplicationException(lucee.runtime.exp.ApplicationException) DataSourceDefintion(lucee.runtime.db.DBUtil.DataSourceDefintion) DataSourceImpl(lucee.runtime.db.DataSourceImpl) ClassDefinition(lucee.runtime.db.ClassDefinition) PageException(lucee.runtime.exp.PageException) ApplicationException(lucee.runtime.exp.ApplicationException)

Aggregations

ClassDefinition (lucee.runtime.db.ClassDefinition)41 ClassDefinitionImpl (lucee.transformer.library.ClassDefinitionImpl)24 Element (org.w3c.dom.Element)15 ApplicationException (lucee.runtime.exp.ApplicationException)12 lucee.aprint (lucee.aprint)11 Struct (lucee.runtime.type.Struct)10 CFXTagClass (lucee.runtime.cfx.customtag.CFXTagClass)9 CPPCFXTagClass (lucee.runtime.cfx.customtag.CPPCFXTagClass)9 JavaCFXTagClass (lucee.runtime.cfx.customtag.JavaCFXTagClass)9 PageException (lucee.runtime.exp.PageException)9 BundleException (org.osgi.framework.BundleException)9 IOException (java.io.IOException)8 Map (java.util.Map)8 ClassException (lucee.commons.lang.ClassException)8 SecurityException (lucee.runtime.exp.SecurityException)8 InvocationTargetException (java.lang.reflect.InvocationTargetException)7 MalformedURLException (java.net.MalformedURLException)7 HashMap (java.util.HashMap)7 Entry (java.util.Map.Entry)7 StructImpl (lucee.runtime.type.StructImpl)7