Пример #1
0
public final class Search extends TagImpl {

  private static final String[] EMPTY = new String[0];

  private static final int SUGGESTIONS_ALWAYS = Integer.MAX_VALUE;
  private static final int SUGGESTIONS_NEVER = -1;

  private static final lucee.runtime.type.Collection.Key FOUND = KeyImpl.intern("found");
  private static final lucee.runtime.type.Collection.Key SEARCHED = KeyImpl.intern("searched");
  private static final lucee.runtime.type.Collection.Key KEYWORDS = KeyImpl.intern("keywords");
  private static final lucee.runtime.type.Collection.Key KEYWORD_SCORE =
      KeyImpl.intern("keywordScore");

  /** Specifies the criteria type for the search. */
  private short type = SearchCollection.SEARCH_TYPE_SIMPLE;

  /**
   * Specifies the maximum number of entries for index queries. If omitted, all rows are returned.
   */
  private int maxrows = -1;

  /** Specifies the criteria for the search following the syntactic rules specified by type. */
  private String criteria = "";

  /** Specifies the first row number to be retrieved. Default is 1. */
  private int startrow = 1;

  /**
   * The logical collection name that is the target of the search operation or an external
   * collection * with fully qualified path.
   */
  private SearchCollection[] collections;

  /** A name for the search query. */
  private String name;

  private String[] category = EMPTY;
  private String categoryTree = "";
  private String status;
  private int suggestions = SUGGESTIONS_NEVER;
  private int contextPassages = 0;
  private int contextBytes = 300;
  private String contextHighlightBegin = "<b>";
  private String contextHighlightEnd = "</b>";
  private String previousCriteria;

  // private int spellCheckMaxLevel=10;
  // private String result=null;

  @Override
  public void release() {
    super.release();
    type = SearchCollection.SEARCH_TYPE_SIMPLE;
    maxrows = -1;
    criteria = "";
    startrow = 1;
    collections = null;

    category = EMPTY;
    categoryTree = "";
    status = null;
    suggestions = SUGGESTIONS_NEVER;
    contextPassages = 0;
    contextBytes = 300;
    contextHighlightBegin = "<b>";
    contextHighlightEnd = "</b>";
    previousCriteria = null;

    // spellCheckMaxLevel=10;
    // result=null;

  }

  /**
   * set the value type Specifies the criteria type for the search.
   *
   * @param type value to set
   * @throws ApplicationException
   */
  public void setType(String type) throws ApplicationException {
    if (type == null) return;
    type = type.toLowerCase().trim();
    if (type.equals("simple")) this.type = SearchCollection.SEARCH_TYPE_SIMPLE;
    else if (type.equals("explicit")) this.type = SearchCollection.SEARCH_TYPE_EXPLICIT;
    else
      throw new ApplicationException(
          "attribute type of tag search has an invalid value, valid values are [simple,explicit] now is ["
              + type
              + "]");
  }

  /**
   * set the value maxrows Specifies the maximum number of entries for index queries. If omitted,
   * all rows are returned.
   *
   * @param maxrows value to set
   */
  public void setMaxrows(double maxrows) {
    this.maxrows = (int) maxrows;
  }

  /**
   * set the value criteria Specifies the criteria for the search following the syntactic rules
   * specified by type.
   *
   * @param criteria value to set
   */
  public void setCriteria(String criteria) {
    this.criteria = criteria;
  }

  /**
   * set the value startrow Specifies the first row number to be retrieved. Default is 1.
   *
   * @param startrow value to set
   */
  public void setStartrow(double startrow) {
    this.startrow = (int) startrow;
  }

  /**
   * set the value collection The logical collection name that is the target of the search operation
   * or an external collection with fully qualified path.
   *
   * @param collection value to set
   * @throws PageException
   */
  public void setCollection(String collection) throws PageException {
    String[] collNames =
        ListUtil.toStringArrayTrim(ListUtil.listToArrayRemoveEmpty(collection, ','));
    collections = new SearchCollection[collNames.length];
    SearchEngine se = pageContext.getConfig().getSearchEngine(pageContext);
    try {
      for (int i = 0; i < collections.length; i++) {
        collections[i] = se.getCollectionByName(collNames[i]);
      }
    } catch (SearchException e) {
      collections = null;
      throw Caster.toPageException(e);
    }
  }

  /**
   * set the value language
   *
   * @param language value to set
   */
  public void setLanguage(String language) {
    DeprecatedUtil.tagAttribute(pageContext, "Search", "language");
  }

  /**
   * set the value external
   *
   * @param external value to set
   * @throws ApplicationException
   */
  public void setExternal(boolean external) throws ApplicationException {
    DeprecatedUtil.tagAttribute(pageContext, "Search", "external");
  }

  /**
   * set the value name A name for the search query.
   *
   * @param name value to set
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * @param category the category to set
   * @throws ApplicationException
   */
  public void setCategory(String listCategories) {
    if (StringUtil.isEmpty(listCategories)) return;
    this.category = ListUtil.trimItems(ListUtil.listToStringArray(listCategories, ','));
  }

  /**
   * @param categoryTree the categoryTree to set
   * @throws ApplicationException
   */
  public void setCategorytree(String categoryTree) {
    if (StringUtil.isEmpty(categoryTree)) return;
    categoryTree = categoryTree.replace('\\', '/').trim();
    if (StringUtil.startsWith(categoryTree, '/')) categoryTree = categoryTree.substring(1);
    if (!StringUtil.endsWith(categoryTree, '/') && categoryTree.length() > 0) categoryTree += "/";
    this.categoryTree = categoryTree;
  }

  /**
   * @param contextBytes the contextBytes to set
   * @throws ApplicationException
   */
  public void setContextbytes(double contextBytes) throws ApplicationException {
    this.contextBytes = (int) contextBytes;
  }

  /**
   * @param contextHighlightBegin the contextHighlightBegin to set
   * @throws ApplicationException
   */
  public void setContexthighlightbegin(String contextHighlightBegin) {
    this.contextHighlightBegin = contextHighlightBegin;
  }

  /**
   * @param contextHighlightEnd the contextHighlightEnd to set
   * @throws ApplicationException
   */
  public void setContexthighlightend(String contextHighlightEnd) {
    this.contextHighlightEnd = contextHighlightEnd;
  }

  /**
   * @param contextPassages the contextPassages to set
   * @throws ApplicationException
   */
  public void setContextpassages(double contextPassages) throws ApplicationException {
    this.contextPassages = (int) contextPassages;
  }

  /**
   * @param previousCriteria the previousCriteria to set
   * @throws ApplicationException
   */
  public void setPreviouscriteria(String previousCriteria) throws ApplicationException {
    this.previousCriteria = previousCriteria;
    throw new ApplicationException(
        "attribute previousCriteria for tag search is not supported yet");
    // TODO impl tag attribute
  }

  /**
   * @param status the status to set
   * @throws ApplicationException
   */
  public void setStatus(String status) {
    if (!StringUtil.isEmpty(status)) this.status = status;
  }

  /**
   * @param suggestions the suggestions to set
   * @throws ApplicationException
   */
  public void setSuggestions(String suggestions) throws PageException {
    if (StringUtil.isEmpty(suggestions)) return;
    suggestions = suggestions.trim().toLowerCase();
    if ("always".equals(suggestions)) this.suggestions = SUGGESTIONS_ALWAYS;
    else if ("never".equals(suggestions)) this.suggestions = SUGGESTIONS_NEVER;
    else if (Decision.isNumber(suggestions)) {
      this.suggestions = Caster.toIntValue(suggestions);
    } else
      throw new ApplicationException(
          "attribute suggestions has an invalid value ["
              + suggestions
              + "], valid values are [always,never,<positive numeric value>]");
  }

  @Override
  public int doStartTag() throws PageException {
    // SerialNumber sn = pageContext.getConfig().getSerialNumber();
    // if(sn.getVersion()==SerialNumber.VERSION_COMMUNITY)
    //    throw new SecurityException("no access to this functionality with the
    // "+sn.getStringVersion()+" version of Lucee");
    final String v = "VARCHAR", d = "DOUBLE";
    String[] cols =
        new String[] {
          "title",
          "url",
          "summary",
          "score",
          "recordssearched",
          "key",
          "custom1",
          "custom2",
          "custom3",
          "custom4",
          "categoryTree",
          "category",
          "context",
          "size",
          "rank",
          "author",
          "type",
          "collection"
        };

    // TODO support context
    String[] types = new String[] {v, v, v, d, d, v, v, v, v, v, v, v, v, d, d, v, v, v};
    SearchData data =
        pageContext.getConfig().getSearchEngine(pageContext).createSearchData(suggestions);
    SuggestionItem item =
        null; // this is already here to make sure the classloader load this sinstance

    lucee.runtime.type.Query qry = new QueryImpl(cols, types, 0, "query");

    SearchCollection collection;
    long time = System.currentTimeMillis();
    AddionalAttrs.setAddionalAttrs(
        contextBytes, contextPassages, contextHighlightBegin, contextHighlightEnd);
    try {
      for (int i = 0; i < collections.length; i++) {
        collection = collections[i];
        startrow =
            collection.search(
                data,
                qry,
                criteria,
                collection.getLanguage(),
                type,
                startrow,
                maxrows,
                categoryTree,
                category);

        if (maxrows >= 0 && qry.getRecordcount() >= maxrows) break;
      }
      pageContext.setVariable(name, qry);
    } catch (SearchException se) {
      throw Caster.toPageException(se);
    } finally {
      AddionalAttrs.removeAddionalAttrs();
    }

    time = System.currentTimeMillis() - time;
    Double recSearched = new Double(data.getRecordsSearched());
    int len = qry.getRecordcount();
    for (int i = 1; i <= len; i++) {
      qry.setAt("recordssearched", i, recSearched);
    }

    // status
    if (status != null) {
      Struct sct = new StructImpl();
      pageContext.setVariable(status, sct);
      sct.set(FOUND, new Double(qry.getRecordcount()));
      sct.set(SEARCHED, recSearched);
      sct.set(KeyConstants._time, new Double(time));

      // TODO impl this values

      Map s = data.getSuggestion();
      if (s.size() > 0) {
        String key;

        Iterator it = s.keySet().iterator();
        Struct keywords = new StructImpl();
        Struct keywordScore = new StructImpl();
        sct.set(KEYWORDS, keywords);
        sct.set(KEYWORD_SCORE, keywordScore);
        Object obj;

        while (it.hasNext()) {
          key = (String) it.next();

          // the problem is a conflict between the SuggestionItem version from core and extension
          obj = s.get(key);
          if (obj instanceof SuggestionItem) {
            item = (SuggestionItem) obj;
            keywords.set(key, item.getKeywords());
            keywordScore.set(key, item.getKeywordScore());
          } else {
            Class clazz = obj.getClass();
            try {
              keywords.set(
                  key, clazz.getMethod("getKeywords", new Class[0]).invoke(obj, new Object[0]));
              keywordScore.set(
                  key, clazz.getMethod("getKeywordScore", new Class[0]).invoke(obj, new Object[0]));
            } catch (Exception e) {
            }
          }
        }

        String query = data.getSuggestionQuery();
        if (query != null) {
          String html = StringUtil.replace(query, "<suggestion>", "<b>", false);
          html = StringUtil.replace(html, "</suggestion>", "</b>", false);
          sct.set("suggestedQueryHTML", html);

          String plain = StringUtil.replace(query, "<suggestion>", "", false);
          plain = StringUtil.replace(plain, "</suggestion>", "", false);
          sct.set("suggestedQuery", plain);
        }
      }

      // if(suggestions!=SUGGESTIONS_NEVER)sct.set("suggestedQuery", "");
      // sct.set("keywords", "");
      // sct.set("keywordScore", "");

    }

    return SKIP_BODY;
  }

  @Override
  public int doEndTag() {
    return EVAL_PAGE;
  }
}
Пример #2
0
/** Lets users implement File Transfer Protocol (FTP) operations. */
public final class Ftp extends TagImpl {

  private static final String ASCCI_EXT_LIST = "txt;htm;html;cfm;cfml;shtm;shtml;css;asp;asa";

  private static final Key SUCCEEDED = KeyImpl.intern("succeeded");
  private static final Key ERROR_CODE = KeyImpl.intern("errorCode");
  private static final Key ERROR_TEXT = KeyImpl.intern("errorText");
  private static final Key RETURN_VALUE = KeyImpl.intern("returnValue");
  private static final Key CFFTP = KeyImpl.intern("cfftp");
  /*private static final Key  = KeyImpl.getInstance();
  private static final Key  = KeyImpl.getInstance();
  private static final Key  = KeyImpl.getInstance();
  private static final Key  = KeyImpl.getInstance();
  private static final Key  = KeyImpl.getInstance();
  private static final Key  = KeyImpl.getInstance();*/

  private FTPPoolImpl pool;

  private String action;
  private String username;
  private String password;
  private String server;
  private int timeout = 30;
  private int port = 21;
  private String connectionName;
  private int retrycount = 1;
  private int count = 0;
  private boolean stoponerror = true;
  private boolean passive;
  private String name;
  private String directory;
  private String ASCIIExtensionList = ASCCI_EXT_LIST;
  private short transferMode = FTPConstant.TRANSFER_MODE_AUTO;
  private String remotefile;
  private String localfile;
  private boolean failifexists = true;
  private String existing;
  private String _new;
  private String item;
  private String result;

  private String proxyserver;
  private int proxyport = 80;
  private String proxyuser;
  private String proxypassword = "";
  private String fingerprint;
  private boolean secure;

  private boolean recursive;

  // private Struct cfftp=new StructImpl();

  @Override
  public void release() {
    super.release();
    this.pool = null;

    this.action = null;
    this.username = null;
    this.password = null;
    this.server = null;
    this.timeout = 30;
    this.port = 21;
    this.connectionName = null;
    this.proxyserver = null;
    this.proxyport = 80;
    this.proxyuser = null;
    this.proxypassword = "";
    this.retrycount = 1;
    this.count = 0;
    this.stoponerror = true;
    this.passive = false;
    this.name = null;
    this.directory = null;
    this.ASCIIExtensionList = ASCCI_EXT_LIST;
    this.transferMode = FTPConstant.TRANSFER_MODE_AUTO;
    this.remotefile = null;
    this.localfile = null;
    this.failifexists = true;
    this.existing = null;
    this._new = null;
    this.item = null;
    this.result = null;

    fingerprint = null;
    secure = false;
    recursive = false;
  }

  /**
   * sets the attribute action
   *
   * @param action
   */
  public void setAction(String action) {
    this.action = action.trim().toLowerCase();
  }

  @Override
  public int doStartTag() {
    return SKIP_BODY;
  }

  @Override
  public int doEndTag() throws PageException {
    pool = ((PageContextImpl) pageContext).getFTPPool();
    AFTPClient client = null;

    // retries
    do {
      try {
        if (action.equals("open")) client = actionOpen();
        else if (action.equals("close")) client = actionClose();
        else if (action.equals("changedir")) client = actionChangeDir();
        else if (action.equals("createdir")) client = actionCreateDir();
        else if (action.equals("listdir")) client = actionListDir();
        else if (action.equals("removedir")) client = actionRemoveDir();
        else if (action.equals("getfile")) client = actionGetFile();
        else if (action.equals("putfile")) client = actionPutFile();
        else if (action.equals("rename")) client = actionRename();
        else if (action.equals("remove")) client = actionRemove();
        else if (action.equals("getcurrentdir")) client = actionGetCurrentDir();
        else if (action.equals("getcurrenturl")) client = actionGetCurrentURL();
        else if (action.equals("existsdir")) client = actionExistsDir();
        else if (action.equals("existsfile")) client = actionExistsFile();
        else if (action.equals("exists")) client = actionExists();
        // else if(action.equals("copy")) client=actionCopy();

        else
          throw new ApplicationException(
              "attribute action has an invalid value [" + action + "]",
              "valid values are [open,close,listDir,createDir,removeDir,changeDir,getCurrentDir,"
                  + "getCurrentURL,existsFile,existsDir,exists,getFile,putFile,rename,remove]");

      } catch (IOException ioe) {
        if (count++ < retrycount) continue;
        throw Caster.toPageException(ioe);
      }

      if (client == null || !checkCompletion(client)) break;
    } while (true);

    return EVAL_PAGE;
  }

  /**
   * check if a directory exists or not
   *
   * @return FTPCLient
   * @throws PageException
   * @throws IOException
   */
  private AFTPClient actionExistsDir() throws PageException, IOException {
    required("directory", directory);

    AFTPClient client = getClient();
    boolean res = existsDir(client, directory);
    Struct cfftp = writeCfftp(client);

    cfftp.setEL(RETURN_VALUE, Caster.toBoolean(res));
    cfftp.setEL(SUCCEEDED, Boolean.TRUE);

    stoponerror = false;
    return client;
  }

  /**
   * check if a file exists or not
   *
   * @return FTPCLient
   * @throws IOException
   * @throws PageException
   */
  private AFTPClient actionExistsFile() throws PageException, IOException {
    required("remotefile", remotefile);

    AFTPClient client = getClient();
    FTPFile file = existsFile(client, remotefile, true);

    Struct cfftp = writeCfftp(client);

    cfftp.setEL(RETURN_VALUE, Caster.toBoolean(file != null && file.isFile()));
    cfftp.setEL(SUCCEEDED, Boolean.TRUE);

    stoponerror = false;
    return client;
  }

  /**
   * check if a file or directory exists
   *
   * @return FTPCLient
   * @throws PageException
   * @throws IOException
   */
  private AFTPClient actionExists() throws PageException, IOException {
    required("item", item);

    AFTPClient client = getClient();
    FTPFile file = existsFile(client, item, false);
    Struct cfftp = writeCfftp(client);

    cfftp.setEL(RETURN_VALUE, Caster.toBoolean(file != null));
    cfftp.setEL(SUCCEEDED, Boolean.TRUE);

    return client;
  }

  /* *
   * check if file or directory exists if it exists return FTPFile otherwise null
   * @param client
   * @param strPath
   * @return FTPFile or null
   * @throws IOException
   * @throws PageException
   * /
  private FTPFile exists(FTPClient client, String strPath) throws PageException, IOException {
      strPath=strPath.trim();

      // get parent path
      FTPPath path=new FTPPath(client.printWorkingDirectory(),strPath);
      String name=path.getName();
      print.out("path:"+name);

      // when directory
      FTPFile[] files=null;
      try {
          files = client.listFiles(path.getPath());
      } catch (IOException e) {}

      if(files!=null) {
          for(int i=0;i<files.length;i++) {
              if(files[i].getName().equalsIgnoreCase(name)) {
                  return files[i];
              }
          }

      }
      return null;
  }*/

  private FTPFile existsFile(AFTPClient client, String strPath, boolean isFile)
      throws PageException, IOException {
    strPath = strPath.trim();
    if (strPath.equals("/")) {
      FTPFile file = new FTPFile();
      file.setName("/");
      file.setType(FTPFile.DIRECTORY_TYPE);
      return file;
    }

    // get parent path
    FTPPath path = new FTPPath(client, strPath);
    String p = path.getPath();
    String n = path.getName();

    strPath = p;
    if ("//".equals(p)) strPath = "/";
    if (isFile) strPath += n;

    // when directory
    FTPFile[] files = null;
    try {
      files = client.listFiles(p);
    } catch (IOException e) {
    }

    if (files != null) {
      for (int i = 0; i < files.length; i++) {
        if (files[i].getName().equalsIgnoreCase(n)) {
          return files[i];
        }
      }
    }
    return null;
  }

  private boolean existsDir(AFTPClient client, String strPath) throws PageException, IOException {
    strPath = strPath.trim();

    // get parent path
    FTPPath path = new FTPPath(client, strPath);
    String p = path.getPath();
    String n = path.getName();

    strPath = p + "" + n;
    if ("//".equals(p)) strPath = "/" + n;
    if (!strPath.endsWith("/")) strPath += "/";

    return client.directoryExists(directory);
  }

  /**
   * removes a file on the server
   *
   * @return FTPCLient
   * @throws IOException
   * @throws PageException
   */
  private AFTPClient actionRemove() throws IOException, PageException {
    required("item", item);
    AFTPClient client = getClient();
    client.deleteFile(item);
    writeCfftp(client);

    return client;
  }

  /**
   * rename a file on the server
   *
   * @return FTPCLient
   * @throws PageException
   * @throws IOException
   */
  private AFTPClient actionRename() throws PageException, IOException {
    required("existing", existing);
    required("new", _new);

    AFTPClient client = getClient();
    client.rename(existing, _new);
    writeCfftp(client);

    return client;
  }

  /**
   * copy a local file to server
   *
   * @return FTPClient
   * @throws IOException
   * @throws PageException
   */
  private AFTPClient actionPutFile() throws IOException, PageException {
    required("remotefile", remotefile);
    required("localfile", localfile);

    AFTPClient client = getClient();
    Resource local =
        ResourceUtil.toResourceExisting(pageContext, localfile); // new File(localfile);
    //	if(failifexists && local.exists()) throw new ApplicationException("File ["+local+"] already
    // exist, if you want to overwrite, set attribute failIfExists to false");
    InputStream is = null;

    try {
      is = IOUtil.toBufferedInputStream(local.getInputStream());
      client.setFileType(getType(local));
      client.storeFile(remotefile, is);
    } finally {
      IOUtil.closeEL(is);
    }
    writeCfftp(client);

    return client;
  }

  /**
   * gets a file from server and copy it local
   *
   * @return FTPCLient
   * @throws PageException
   * @throws IOException
   */
  private AFTPClient actionGetFile() throws PageException, IOException {
    required("remotefile", remotefile);
    required("localfile", localfile);

    AFTPClient client = getClient();
    Resource local =
        ResourceUtil.toResourceExistingParent(pageContext, localfile); // new File(localfile);
    pageContext.getConfig().getSecurityManager().checkFileLocation(local);
    if (failifexists && local.exists())
      throw new ApplicationException(
          "File ["
              + local
              + "] already exist, if you want to overwrite, set attribute failIfExists to false");
    OutputStream fos = null;
    client.setFileType(getType(local));
    boolean success = false;
    try {
      fos = IOUtil.toBufferedOutputStream(local.getOutputStream());
      success = client.retrieveFile(remotefile, fos);
    } finally {
      IOUtil.closeEL(fos);
      if (!success) local.delete();
    }
    writeCfftp(client);

    return client;
  }

  /**
   * get url of the working directory
   *
   * @return FTPCLient
   * @throws IOException
   * @throws PageException
   */
  private AFTPClient actionGetCurrentURL() throws PageException, IOException {
    AFTPClient client = getClient();
    String pwd = client.printWorkingDirectory();
    Struct cfftp = writeCfftp(client);
    cfftp.setEL(
        "returnValue", client.getPrefix() + "://" + client.getRemoteAddress().getHostName() + pwd);
    return client;
  }

  /**
   * get path from the working directory
   *
   * @return FTPCLient
   * @throws IOException
   * @throws PageException
   */
  private AFTPClient actionGetCurrentDir() throws PageException, IOException {
    AFTPClient client = getClient();
    String pwd = client.printWorkingDirectory();
    Struct cfftp = writeCfftp(client);
    cfftp.setEL("returnValue", pwd);
    return client;
  }

  /**
   * change working directory
   *
   * @return FTPCLient
   * @throws IOException
   * @throws PageException
   */
  private AFTPClient actionChangeDir() throws IOException, PageException {
    required("directory", directory);

    AFTPClient client = getClient();
    client.changeWorkingDirectory(directory);
    writeCfftp(client);
    return client;
  }

  private AFTPClient getClient() throws PageException, IOException {
    return pool.get(_createConnection());
  }

  /**
   * removes a remote directory on server
   *
   * @return FTPCLient
   * @throws IOException
   * @throws PageException
   */
  private AFTPClient actionRemoveDir() throws IOException, PageException {
    required("directory", directory);

    AFTPClient client = getClient();
    if (recursive) {
      removeRecursive(client, directory, FTPFile.DIRECTORY_TYPE);
    } else client.removeDirectory(directory);

    writeCfftp(client);
    return client;
  }

  private static void removeRecursive(AFTPClient client, String path, int type) throws IOException {
    // directory
    if (FTPFile.DIRECTORY_TYPE == type) {
      if (!path.endsWith("/")) path += "/";
      // first we remove the children
      FTPFile[] children = client.listFiles(path);
      for (FTPFile child : children) {
        if (child.getName().equals(".") || child.getName().equals("..")) continue;
        removeRecursive(client, path + child.getName(), child.getType());
      }
      // then the directory itself
      client.removeDirectory(path);

    }
    // file
    else if (FTPFile.FILE_TYPE == type) {
      client.deleteFile(path);
    }
  }

  /**
   * create a remote directory
   *
   * @return FTPCLient
   * @throws IOException
   * @throws PageException
   */
  private AFTPClient actionCreateDir() throws IOException, PageException {
    required("directory", directory);

    AFTPClient client = getClient();
    client.makeDirectory(directory);
    writeCfftp(client);
    return client;
  }

  /**
   * List data of a ftp connection
   *
   * @return FTPCLient
   * @throws PageException
   * @throws IOException
   */
  private AFTPClient actionListDir() throws PageException, IOException {
    required("name", name);
    required("directory", directory);

    AFTPClient client = getClient();
    FTPFile[] files = client.listFiles(directory);
    if (files == null) files = new FTPFile[0];

    pageContext.setVariable(
        name, toQuery(files, "ftp", directory, client.getRemoteAddress().getHostName()));
    writeCfftp(client);
    return client;
  }

  public static lucee.runtime.type.Query toQuery(
      FTPFile[] files, String prefix, String directory, String hostName) throws PageException {

    String[] cols =
        new String[] {
          "name",
          "isdirectory",
          "lastmodified",
          "length",
          "mode",
          "path",
          "url",
          "type",
          "raw",
          "attributes"
        };
    String[] types =
        new String[] {
          "VARCHAR", "BOOLEAN", "DATE", "DOUBLE", "VARCHAR", "VARCHAR", "VARCHAR", "VARCHAR",
          "VARCHAR", "VARCHAR"
        };

    lucee.runtime.type.Query query = new QueryImpl(cols, types, 0, "query");

    // translate directory path for display
    if (directory.length() == 0) directory = "/";
    else if (directory.startsWith("./")) directory = directory.substring(1);
    else if (directory.charAt(0) != '/') directory = '/' + directory;
    if (directory.charAt(directory.length() - 1) != '/') directory = directory + '/';

    int row;
    for (int i = 0; i < files.length; i++) {
      FTPFile file = files[i];
      if (file.getName().equals(".") || file.getName().equals("..")) continue;
      row = query.addRow();
      query.setAt("attributes", row, "");
      query.setAt("isdirectory", row, Caster.toBoolean(file.isDirectory()));
      query.setAt("lastmodified", row, new DateTimeImpl(file.getTimestamp()));
      query.setAt("length", row, Caster.toDouble(file.getSize()));
      query.setAt("mode", row, FTPConstant.getPermissionASInteger(file));
      query.setAt("type", row, FTPConstant.getTypeAsString(file.getType()));
      // query.setAt("permission",row,FTPConstant.getPermissionASInteger(file));
      query.setAt("raw", row, file.getRawListing());
      query.setAt("name", row, file.getName());
      query.setAt("path", row, directory + file.getName());
      query.setAt("url", row, prefix + "://" + hostName + "" + directory + file.getName());
    }
    return query;
  }

  /**
   * Opens a FTP Connection
   *
   * @return FTPCLinet
   * @throws IOException
   * @throws PageException
   */
  private AFTPClient actionOpen() throws IOException, PageException {
    required("server", server);
    required("username", username);
    required("password", password);

    AFTPClient client = getClient();
    writeCfftp(client);
    return client;
  }

  /**
   * close a existing ftp connection
   *
   * @return FTPCLient
   * @throws PageException
   */
  private AFTPClient actionClose() throws PageException {
    FTPConnection conn = _createConnection();
    AFTPClient client = pool.remove(conn);

    Struct cfftp = writeCfftp(client);
    cfftp.setEL("succeeded", Caster.toBoolean(client != null));
    return client;
  }

  /**
   * throw a error if the value is empty (null)
   *
   * @param attributeName
   * @param atttributValue
   * @throws ApplicationException
   */
  private void required(String attributeName, String atttributValue) throws ApplicationException {
    if (atttributValue == null)
      throw new ApplicationException(
          "invalid attribute constelation for the tag ftp",
          "attribute [" + attributeName + "] is required, if action is [" + action + "]");
  }

  /**
   * writes cfftp variable
   *
   * @param client
   * @return FTPCLient
   * @throws PageException
   */
  private Struct writeCfftp(AFTPClient client) throws PageException {
    Struct cfftp = new StructImpl();
    if (result == null) pageContext.variablesScope().setEL(CFFTP, cfftp);
    else pageContext.setVariable(result, cfftp);
    if (client == null) {
      cfftp.setEL(SUCCEEDED, Boolean.FALSE);
      cfftp.setEL(ERROR_CODE, new Double(-1));
      cfftp.setEL(ERROR_TEXT, "");
      cfftp.setEL(RETURN_VALUE, "");
      return cfftp;
    }
    int repCode = client.getReplyCode();
    String repStr = client.getReplyString();
    cfftp.setEL(ERROR_CODE, new Double(repCode));
    cfftp.setEL(ERROR_TEXT, repStr);

    cfftp.setEL(SUCCEEDED, Caster.toBoolean(client.isPositiveCompletion()));
    cfftp.setEL(RETURN_VALUE, repStr);
    return cfftp;
  }

  /**
   * check completion status of the client
   *
   * @param client
   * @return FTPCLient
   * @throws ApplicationException
   */
  private boolean checkCompletion(AFTPClient client) throws ApplicationException {
    boolean isPositiveCompletion = client.isPositiveCompletion();
    if (isPositiveCompletion) return false;
    if (count++ < retrycount) return true;
    if (stoponerror) {
      throw new lucee.runtime.exp.FTPException(action, client);
    }

    return false;
  }

  /**
   * get FTP. ... _FILE_TYPE
   *
   * @param file
   * @return type
   */
  private int getType(Resource file) {
    if (transferMode == FTPConstant.TRANSFER_MODE_BINARY) return AFTPClient.FILE_TYPE_BINARY;
    else if (transferMode == FTPConstant.TRANSFER_MODE_ASCCI) return AFTPClient.FILE_TYPE_TEXT;
    else {
      String ext = ResourceUtil.getExtension(file, null);
      if (ext == null
          || ListUtil.listContainsNoCase(ASCIIExtensionList, ext, ";", true, false) == -1)
        return AFTPClient.FILE_TYPE_BINARY;
      return AFTPClient.FILE_TYPE_TEXT;
    }
  }

  /** @return return a new FTP Connection Object */
  private FTPConnection _createConnection() {
    return new FTPConnectionImpl(
        connectionName,
        server,
        username,
        password,
        port,
        timeout,
        transferMode,
        passive,
        proxyserver,
        proxyport,
        proxyuser,
        proxypassword,
        fingerprint,
        stoponerror,
        secure);
  }

  /** @param password The password to set. */
  public void setPassword(String password) {
    this.password = password;
  }
  /** @param username The username to set. */
  public void setUsername(String username) {
    this.username = username;
  }
  /** @param server The server to set. */
  public void setServer(String server) {
    this.server = server;
  }
  /** @param timeout The timeout to set. */
  public void setTimeout(double timeout) {
    this.timeout = (int) timeout;
  }
  /** @param port The port to set. */
  public void setPort(double port) {
    this.port = (int) port;
  }
  /** @param connection The connection to set. */
  public void setConnection(String connection) {
    this.connectionName = connection;
  }
  /** @param proxyserver The proxyserver to set. */
  public void setProxyserver(String proxyserver) {
    this.proxyserver = proxyserver;
  }

  /**
   * set the value proxyport The port number on the proxy server from which the object is requested.
   * Default is 80. When used with resolveURL, the URLs of retrieved documents that specify a port
   * number are automatically resolved to preserve links in the retrieved document.
   *
   * @param proxyport value to set
   */
  public void setProxyport(double proxyport) {
    this.proxyport = (int) proxyport;
  }

  /**
   * set the value username When required by a proxy server, a valid username.
   *
   * @param proxyuser value to set
   */
  public void setProxyuser(String proxyuser) {
    this.proxyuser = proxyuser;
  }

  /**
   * set the value password When required by a proxy server, a valid password.
   *
   * @param proxypassword value to set
   */
  public void setProxypassword(String proxypassword) {
    this.proxypassword = proxypassword;
  }

  /** @param retrycount The retrycount to set. */
  public void setRetrycount(double retrycount) {
    this.retrycount = (int) retrycount;
  }
  /** @param stoponerror The stoponerror to set. */
  public void setStoponerror(boolean stoponerror) {
    this.stoponerror = stoponerror;
  }
  /** @param passive The passive to set. */
  public void setPassive(boolean passive) {
    this.passive = passive;
  }
  /** @param directory The directory to set. */
  public void setDirectory(String directory) {
    this.directory = directory;
  }
  /** @param name The name to set. */
  public void setName(String name) {
    this.name = name;
  }

  public void setRecurse(boolean recursive) {
    this.recursive = recursive;
  }

  /** @param extensionList The aSCIIExtensionList to set. */
  public void setAsciiextensionlist(String extensionList) {
    ASCIIExtensionList = extensionList.toLowerCase().trim();
  }
  /** @param transferMode The transferMode to set. */
  public void setTransfermode(String transferMode) {
    transferMode = transferMode.toLowerCase().trim();
    if (transferMode.equals("binary")) this.transferMode = FTPConstant.TRANSFER_MODE_BINARY;
    else if (transferMode.equals("ascci")) this.transferMode = FTPConstant.TRANSFER_MODE_ASCCI;
    else this.transferMode = FTPConstant.TRANSFER_MODE_AUTO;
  }

  /** @param localfile The localfile to set. */
  public void setLocalfile(String localfile) {
    this.localfile = localfile;
  }
  /** @param remotefile The remotefile to set. */
  public void setRemotefile(String remotefile) {
    this.remotefile = remotefile;
  }
  /** @param failifexists The failifexists to set. */
  public void setFailifexists(boolean failifexists) {
    this.failifexists = failifexists;
  }
  /** @param _new The _new to set. */
  public void setNew(String _new) {
    this._new = _new;
  }
  /** @param existing The existing to set. */
  public void setExisting(String existing) {
    this.existing = existing;
  }
  /** @param item The item to set. */
  public void setItem(String item) {
    this.item = item;
  }

  /** @param result The result to set. */
  public void setResult(String result) {
    this.result = result;
  }

  public void setSecure(boolean secure) {
    this.secure = secure;
  }

  public void setFingerprint(String fingerprint) {
    this.fingerprint = fingerprint;
  }
}
public final class ModernAppListenerException extends PageException {

  private static final Collection.Key ROOT_CAUSE = KeyImpl.intern("rootCause");
  private static final Collection.Key CAUSE = KeyImpl.intern("cause");
  private PageException rootCause;
  private String eventName;

  /**
   * Constructor of the class
   *
   * @param pe
   * @param eventName
   */
  public ModernAppListenerException(PageException pe, String eventName) {
    super(pe.getMessage());
    setStackTrace(pe.getStackTrace());
    this.rootCause = pe;
    this.eventName = eventName;
  }

  @Override
  public void addContext(PageSource pageSource, int line, int column, StackTraceElement ste) {
    rootCause.addContext(pageSource, line, column, ste);
  }

  @Override
  public Struct getAdditional() {
    return rootCause.getAdditional();
  }

  @Override
  public Struct getAddional() {
    return rootCause.getAdditional();
  }

  public Struct getCatchBlock() {
    return getCatchBlock(ThreadLocalPageContext.getConfig());
  }

  @Override
  public Struct getCatchBlock(PageContext pc) {
    return getCatchBlock(pc.getConfig());
  }

  @Override
  public CatchBlock getCatchBlock(Config config) {
    CatchBlock cb = rootCause.getCatchBlock(config);
    Collection cause = (Collection) Duplicator.duplicate(cb, false);
    // rtn.setEL("message", getMessage());
    if (!cb.containsKey(KeyConstants._detail))
      cb.setEL(
          KeyConstants._detail,
          "Exception throwed while invoking function ["
              + eventName
              + "] in application event handler ");
    cb.setEL(ROOT_CAUSE, cause);
    cb.setEL(CAUSE, cause);
    // cb.setEL("stacktrace", getStackTraceAsString());
    // rtn.setEL("tagcontext", new ArrayImpl());
    // rtn.setEL("type", getTypeAsString());
    cb.setEL(KeyConstants._name, eventName);
    return cb;
  }

  @Override
  public String getCustomTypeAsString() {
    return rootCause.getCustomTypeAsString();
  }

  @Override
  public String getDetail() {
    return rootCause.getDetail();
  }

  @Override
  public Struct getErrorBlock(PageContext pc, ErrorPage ep) {
    return rootCause.getErrorBlock(pc, ep);
  }

  @Override
  public String getErrorCode() {
    return rootCause.getErrorCode();
  }

  @Override
  public String getExtendedInfo() {
    return rootCause.getExtendedInfo();
  }

  @Override
  public String getStackTraceAsString() {
    return rootCause.getStackTraceAsString();
  }

  @Override
  public int getTracePointer() {
    return rootCause.getTracePointer();
  }

  @Override
  public String getTypeAsString() {
    return rootCause.getTypeAsString();
  }

  @Override
  public void setDetail(String detail) {
    rootCause.setDetail(detail);
  }

  @Override
  public void setErrorCode(String errorCode) {
    rootCause.setErrorCode(errorCode);
  }

  @Override
  public void setExtendedInfo(String extendedInfo) {
    rootCause.setExtendedInfo(extendedInfo);
  }

  @Override
  public void setTracePointer(int tracePointer) {
    rootCause.setTracePointer(tracePointer);
  }

  @Override
  public boolean typeEqual(String type) {
    return rootCause.equals(type);
  }

  @Override
  public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
    return rootCause.toDumpData(pageContext, maxlevel, dp);
  }

  /** @return the eventName */
  public String getEventName() {
    return eventName;
  }

  public String getLine(Config config) {
    return ((PageExceptionImpl) rootCause).getLine(config);
  }

  @Override
  public Throwable getRootCause() {
    return rootCause.getRootCause();
  }

  @Override
  public StackTraceElement[] getStackTrace() {
    return rootCause.getStackTrace();
  }

  @Override
  public void printStackTrace() {
    rootCause.printStackTrace();
  }

  @Override
  public void printStackTrace(PrintStream s) {
    rootCause.printStackTrace(s);
  }

  @Override
  public void printStackTrace(PrintWriter s) {
    rootCause.printStackTrace(s);
  }

  public PageException getPageException() {
    return rootCause;
  }

  @Override
  public void setExposeMessage(boolean exposeMessage) {
    rootCause.setExposeMessage(exposeMessage);
  }

  @Override
  public boolean getExposeMessage() {
    return rootCause.getExposeMessage();
  }
}