Welcome to my site!
How to set URI and Authentication Credentials to Partner Link Dynamically! PDF Print E-mail
Written by T.A. Nguyen   
Monday, 24 February 2014 11:31

Oracle BPEL is a pain to deal with when trying to have a full control of the header and authentication.  This will show you a simple way to set the HTTPAdapter URI and Credential for the Basic Authentication.  You can set the URI dynamically using the endpointURI as it will allow you to over write it with a variable at run time.  The same we like to also get the username/password to also be passing in at runtime.  Oracle have a set of headers properties that you can set, but statically are:

  • oracle.webservices.auth.username
  • oracle.webservices.auth.password

But the problem, come when you attempt to over write these values with your own at run-time the BPEL simple ignore it.

 

To solve this problem, we will have to force the HTTPAdapter to use the good old javax.xml.ws.security.auth.username and password method of passing the basic credential, because the oracle BPEL will not reject the toProperty setting in the bpel.  Here are the three headers properties that allow data to be replace at run time just before invoke.

  • endpointURI
  • javax.xml.ws.security.auth.username
  • javax.xml.ws.security.auth.password

 

[composite.xml]

 

[yourbpel.bpel]

Bookmark with:

Deli.cio.us    Digg    reddit    Facebook    StumbleUpon    Newsvine
Last Updated on Tuesday, 25 February 2014 12:47
 
A JDBC ConnectionManager that load the right driver base on it URL PDF Print E-mail
Written by T.A. Nguyen   
Thursday, 24 October 2013 09:24

A simple class to automatically load the correct (most popular) JDBC driver base on the connection url.  If you have other driver url and driver that I missed here, please send it via comments or email to me and I will update this class.

 

package org.cnci.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.HashMap;
import java.util.Map;

/*
 * ConnectionManager
 * @author: T.A. Nguyen
 * 
 * Simple solution to automatically load the JDBC driver based on the 
 * connection url.  You can arrange the order and move the most used
 * to the top to provide sequential search.
 */
public class ConnectionManager {
	private static final Map DRVS = new HashMap();
	
	static {
		DRVS.put("jdbc:oracle:thin",            "oracle.jdbc.OracleDriver");
		DRVS.put("jdbc:sqlserver:",             "com.microsoft.sqlserver.jdbc.SQLServerDriver");
		DRVS.put("jdbc:weblogic:sqlserver:",    "weblogic.jdbc.sqlserver.SQLServerDriver");
		DRVS.put("jdbc:microsoft:sqlserver:",   "com.microsoft.jdbc.sqlserver.SQLServerDriver");
		DRVS.put("jdbc:db2:",                   "com.ibm.db2.jcc.DB2Driver");
		DRVS.put("jdbc:informix-sqli:",         "com.informix.jdbc.IfxDriver");
		DRVS.put("jdbc:mysql:",                 "com.mysql.jdbc.Driver");
		DRVS.put("jdbc:netezza:",               "org.netezza.Driver");
		DRVS.put("jdbc:oracle:oci",             "oracle.jdbc.OracleDriver");
		DRVS.put("jdbc:sybase:Tds:",            "com.sybase.jdbc4.jdbc.SybDriver");
		DRVS.put("jdbc:teradata:",              "com.ncr.teradata.TeraDriver");
		DRVS.put("jdbc:jtds:",                  "net.sourceforge.jtds.jdbc.Driver");
		DRVS.put("jdbc:postgresql:",            "org.postgresql.Driver");
		DRVS.put("jdbc:Cache:",                 "com.intersys.jdbc.CacheDriver");
		DRVS.put("jdbc:FrontBase:",             "com.frontbase.jdbc.FBJDriver");
		DRVS.put("jdbc:h2:",                    "org.h2.Driver");
		DRVS.put("jdbc:hsqldb:hsql:",           "org.hsqldb.jdbcDriver");
		//DRVS.put("jdbc:derby:",               "org.apache.derby.jdbc.ClientDriver"); // Automatically loaded by JVM
		DRVS.put("jdbc:mimer:",                 "com.mimer.jdbc.Driver");
		DRVS.put("jdbc:pervasive:",             "com.pervasive.jdbc.v2.Driver");
		DRVS.put("jdbc:sqlite:",                "org.sqlite.JDBC");
		DRVS.put("jdbc:as400:",                 "com.ibm.as400.access.AS400JDBCDriver");
		DRVS.put("jdbc:mckoi:",                 "com.mckoi.JDBCDriver");
		DRVS.put("jdbc:firebirdsql:",           "org.firebirdsql.jdbc.FBDriver");
		DRVS.put("jdbc:rmi:",                   "RmiJdbc.RJDriver");
		DRVS.put("jdbc:ids",                    "ids.sql.IDSDriver");
		DRVS.put("jdbc:idb:",                   "org.enhydra.instantdb.jdbc.idbDriver"); // InstantDB
		DRVS.put("jdbc:interbase:",             "interbase.interclient.Driver");
		DRVS.put("jdbc:HypersonicSQL:",         "org.hsql.jdbcDriver");
		DRVS.put("jdbc:weblogic:mssqlserver4:", "weblogic.jdbc.mssqlserver4.Driver");
		DRVS.put("jdbc:pointbase:",             "com.pointbase.jdbc.jdbcUniversalDriver");
		DRVS.put("jdbc:cloudscape:",            "COM.cloudscape.core.JDBCDriver");
		DRVS.put("jdbc:JTurbo:",                "com.ashna.jturbo.driver.Driver");
		DRVS.put("jdbc:inetdae:",               "com.inet.tds.TdsDriver");
		DRVS.put("jdbc:cognos:",                "cs.jdbc.driver.CompositeDriver");
		DRVS.put("jdbc:neon:",                  "com.neon.jdbc.Driver");
	}
	

	private ConnectionManager() {
	}

	public static String getDriver(String url) {
		if (url != null) {
			for (String prefix : DRVS.keySet()) {
				if (url.startsWith(prefix)) {
					return DRVS.get(prefix);
				}
			}
		}
		return null;
	}

	public static Connection getConnection(String url, String user, String password) throws Exception {
		Connection conn = null;
		if (url != null) {
			Class.forName(getDriver(url));
			if (user != null && password != null) {
				conn = DriverManager.getConnection(url, user, password);
			} else {
				conn = DriverManager.getConnection(url);
			}
		}
		return conn;
	}
}


Bookmark with:

Deli.cio.us    Digg    reddit    Facebook    StumbleUpon    Newsvine
Last Updated on Tuesday, 25 February 2014 12:49
 
TinyWebServer.java an embedded sun httpserver implementation since JRE 1.6 less than 300 lines! PDF Print E-mail
Written by T.A. Nguyen   
Sunday, 06 May 2012 00:03

TinyWebServer.java is a Java implementation based on the embedded sun httpserver which available since JRE 1.6. (in rt.jar) and is less than 300 lines of codes.


During my development work, I started out using the JETTY a small and lightweight web engine, but I need to attached a few jars to my projects.  My automated build/deployment also need to pack these libraries as well since my test cases depend on it.

 

Since version 1.6 of the sun JRE, the rt.jar come with the embeded version (limitted functionality) that work well, and just what I needed to set up my stub web services to ensure reliable test run.  I also use this "TinyWebServer.java" for small demo to my student as well.  So, here are the codes for it.  Next post I will share with you on how I use it in my junit test to set up and run my stub web services.

 

Remember, if you get errors in your eclipse project, simply remove the "System Library" and add it back, this will remove the security policy from the eclipse build.

 

package org.cnci.util;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URL;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.xml.ws.Endpoint;

import org.apache.log4j.Logger;

import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;

/**
 * TinyWebServer
 * 
 * @author T.A. Nguyen
 * 
 *  Tiny Embedded (Stub) Web Server, intended for testing only.  This implementation
 *  will take advantage of the "sun" httpserver package that shipped with the JRE 
 *  since release 1.6.
 *  
 *  Provide an easy creation of a TinyWebServer that can be start up and host as
 *  an embedded (secure) web server.  Just pass in the URL as you intended to access
 *  and it will attempt to host in that url, whether it is secure or not.  This 
 *  will make it an ideal to configure and setup as a unit test stub server.
 * 
 *  By default, Eclipse will add restriction to com.sun.net.httpserver.*
 *  package access, just need to remove the JRE library and add it back
 *  then it will allow you to use it.
 * 
 *  Usage: 
 *  
 *  TinyWebServer server = new TinyWebServer("https://*:8080"); 
 *  AddressBookSoap myBook = new AddressBookSoapImpl(); // your service implementation here 
 *  server.addSoapService("/ws/myBook", myBook);
 *  PersonnelSoap myHR = new StubPersonnelSoapImpl(); // my other web service, local stub 
 *  server.addSoapService("/ws/myHR", myHR); 
 *  server.start();
 *  
 */
public class TinyWebServer {
  private static final String SSL_VERSION = "SSLv3";
  private static Logger log = Logger.getLogger(TinyWebServer.class);
  private static final String KEYSTORE_FILENAME = "localhost.server.keystore.jks";
  private static final String KEYSTORE_PASSWORD = "cnci.org";
  private static final int RETRY_TIME = 5;
  private static final int SLEEP_TIME = 500;
  private static final String DEFAULT_PROTOCOL = "https";
  private static final String DEFAULT_HOST = "localhost";
  private static final int DEFAULT_PORT = 8080;

  private HttpServer server = null;
  private Map services = new HashMap();
  private List endpoints = new ArrayList();
  private String url;      // same as the client access url
  private String protocol; // http/https
  private String host;     // ip or host name
  private int port;

  /**
   * Simple, just pass the url as the client expected, 
   * make sure that the host is of a local host name or ip 
   * otherwise it will default to "localhost"
   * 
   * https://*:8080 will listen to all local addresses on port 8080 via https (secure) protocol
   * https://localhost:8080 will listen to 127.0.0.1:8080 via https (secure) protocol
   * http://*:8080 will listen to all local addresses on port 8080 via http protocol 
   * 
   * @param host
   * @param port
   */
  public TinyWebServer(String url) {
    this.url = url;
  }
  
  public TinyWebServer(String host, int port) {
    this.host = host;
    this.port = port;
  }

  public void addSoapService(String context, Object service) throws Exception {
    if (context == null || context.trim().length() <= 0) {
      throw new Exception("Context uri is a required parameter.");
    }
    if (service == null) {
      throw new Exception("Soap service handler is a required parameter.");
    }
    if (services.containsKey(context)) {
      throw new Exception("Context uri is already exist.");
    }
    services.put(context, service);
    createEndpoint(getServer().createContext(context), service);
  }

  public void addHttpService(String context, HttpHandler handler, Executor executor) throws Exception {
    if (context == null || context.trim().length() <= 0) {
      throw new Exception("Context uri is a required parameter.");
    }
    if (handler == null) {
      throw new Exception("HttpHandler service handler is a required parameter.");
    }
    if (services.containsKey(context)) {
      throw new Exception("Context uri is already exist.");
    }
    services.put(context, handler);
    server.createContext(context, handler);
    if (executor == null) executor = Executors.newCachedThreadPool();
    // Executors.newFixedThreadPool(5)
    server.setExecutor(executor); // null for Default executor
  }

  public void start() throws Exception {
    HttpServer s = getServer();
    Exception e = null;
    boolean started = false;
    int count = 0;

    while (!started && count  0) {
          port = u.getPort();
        }
        if (u.getProtocol() != null) {
          protocol = u.getProtocol();
        }
      } catch (Exception e) {
        // ignore
      }
    }
    if (protocol == null) protocol = DEFAULT_PROTOCOL;
    if (host == null) host = DEFAULT_HOST;
    if (port <= 0) port = DEFAULT_PORT;
  }
  
  private boolean isSsl() {
    return DEFAULT_PROTOCOL.equalsIgnoreCase(getProtocol());
  }
  
  private String getHost() {
    if (host == null && url != null) validateUrl();
    return host;
  }
  
  private int getPort() {
    if (host == null && url != null) validateUrl();
    return port;
  }
  
  private String getProtocol() {
    if (host == null && url != null) validateUrl();
    return protocol;
  }
}

 

Source Codes

Bookmark with:

Deli.cio.us    Digg    reddit    Facebook    StumbleUpon    Newsvine
Last Updated on Tuesday, 25 February 2014 12:49
 
DailyFileAppender.java a Solution to a log4j Daily Appender with a Retention! PDF Print E-mail
Written by T.A. Nguyen   
Saturday, 28 April 2012 23:36

I use the Log4j DailyRollingFileAppender over many years and many projects but always end up controlling the retention via a cron jobs.  That's work well, but what if we can control the retention in the log4j configuration file?  Wouldn't that be great?  Well, that came up and I wanted to share my solution with you:

 

package org.cnci.util;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.log4j.FileAppender;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;

/**
 * Provide AS IS, no warranty!
 * 
 * Special log appender for log4j. Adds the current date (ie. year-mon) to the
 * end of the file name, so that rolling on to the next log is simply a case of
 * starting a new one - no renaming of old logs.
 * 
 * ONLY support: yyyy (yearly) | yyyy-MM (monthly) | yyyy-MM-dd (daily) |
 * yyyy-ww (weekly) yyyy-MM-dd-a (midnight, midday) | yyyy-MM-dd-HH (hourly) |
 * yyyy-MM-dd-HH-mm (every minute)
 * 
 * An example log4j.properties (one log per day, retains 30 days of logs)
 * 
 * log4j.rootCategory=INFO, DAILY
 * log4j.appender.DAILY=org.cnci.util.DailyFileAppender
 * log4j.appender.DAILY.File=log/MyApplication.log
 * log4j.appender.DAILY.DatePattern=yyyy-MM-dd log4j.appender.DAILY.MaxLogs=30
 * log4j.appender.DAILY.layout=org.apache.log4j.PatternLayout
 * log4j.appender.DAILY.layout.ConversionPattern=%d %-5p %c @ %m%n
 * 
 * @author T.A. Nguyen
 **/
public class DailyFileAppender extends FileAppender {
  private static final String DEFAULT_DATE_PATTERN = "'.'yyyy-MM-dd";
  private static final String[] SUPPORTED_DATE_PATTERNS = {
    "yyyy" /** (yearly) **/, 
    "yyyy-MM" /** (monthly) **/, 
    "yyyy-MM-dd" /** (daily) **/,
    "yyyy-ww" /** (weekly) **/,
    "yyyy-MM-dd-a" /** (midnight, midday) **/,
    "yyyy-MM-dd-HH" /** (hourly) **/,
    "yyyy-MM-dd-HH-mm" /** (every minute) **/
  };

  /**
   * Used internally and contains the name of the date derived from current
   * system date.
   **/
  private Date now = new Date();
  private String datePattern;
  private SimpleDateFormat formater;
  private int maxLogs = 0;
  private String scheduledFileName;

  /**
   * Default constructor. This is required as the appender class is dynamically
   * loaded.
   **/
  public DailyFileAppender() {
    super();
  }

  /**
   * computing for the scheduledFileName and set the now
   **/
  @Override
  public void activateOptions() {
    super.activateOptions();
    if (datePattern != null && fileName != null) {
      now.setTime(System.currentTimeMillis());
      formater = new SimpleDateFormat(datePattern);
      File file = new File(fileName);
      scheduledFileName = fileName + formater.format(new Date(file.lastModified()));
    } else {
      LogLog.error("Either File or DatePattern options are not set for appender [" + name + "].");
    }
  }

  public String getDatePattern() {
    return this.datePattern;
  }

  public void setDatePattern(String datePattern) {
    // make sure the the pattern conform to what we allow.
    this.datePattern = checkPattern(datePattern);
  }

  public int getMaxLogs() {
    return maxLogs;
  }

  public void setMaxLogs(int maxLogs) {
    this.maxLogs = maxLogs;
  }

  @Override
  protected void subAppend(LoggingEvent logEvent) {
    now.setTime(System.currentTimeMillis());

    try {
      rollOver();
    } catch (IOException IOEx) {
      LogLog.error("rollOver() failed!", IOEx);
    }

    super.subAppend(logEvent);
  }

  @Override
  public void setFile(String file) {
    try {
      // Fix the STUPID file path... Windows will use \ and *NIX will use
      // /,
      // so make sure we store the correct file path.
      File f = new File(file);
      file = f.getAbsolutePath();
    } catch (Exception e) {
      // ignore!
    }
    super.setFile(file);
  }

  void rollOver() throws IOException {
    /** Compute filename, but only if datePattern is specified **/
    if (datePattern == null) {
      // LogLog.error("Missing DatePattern option in rollOver().");
      errorHandler.error("Missing DatePattern option in rollOver().");
      return;
    }
    String datedFilename = fileName + formater.format(now);
    LogLog.debug(fileName + " ==> " + scheduledFileName + " ==> " + datedFilename);
    // It is too early to roll over because we are still within the
    // bounds of the current interval. Rollover will occur once the
    // next interval is reached.
    if (scheduledFileName.equals(datedFilename)) {
      return;
    }

    // close current file, and rename it to datedFilename
    this.closeFile();

    File file = new File(fileName);
    File target = new File(fileName + formater.format(new Date(file.lastModified())));
    if (target.exists()) {
      target.delete();
    }

    boolean result = file.renameTo(target);
    if (result) {
      // LogLog.debug(fileName + " -> " + scheduledFileName);
      scheduledFileName = datedFilename;
    } else {
      LogLog.error("Failed to rename [" + fileName + "] to [" + scheduledFileName + "].");
    }

    try {
      // This will also close the file. This is OK since multiple
      // close operations are safe.
      this.setFile(fileName, true, this.bufferedIO, this.bufferSize);
    } catch (IOException e) {
      // LogLog.error("setFile(" + fileName + ", true) call failed.");
      errorHandler.error("setFile(" + fileName + ", true) call failed.");
    }
    cleanupOldFiles();
  }

  /**
   * The helper function to validate the DatePattern.
   * 
   * @param pattern
   *          The DatePattern to be validated.
   * @return The validated date pattern or default DEFAULT_DATE_PATTERN
   **/
  private String checkPattern(String pattern) {
    if (isSupportedPattern(pattern)) {
      try {
        this.formater = new SimpleDateFormat(pattern);
        return pattern;
      } catch (Exception e) {
        LogLog.error("Invalid DatePattern " + pattern, e);
      }
    }
    return DEFAULT_DATE_PATTERN;
  }

  private boolean isSupportedPattern(String pattern) {
    if (pattern != null) {
      // give 'em that last chance to conform to our supported pattern.
      // Convert " " and ":" into "-" so that file operation will work
      // correctly!
      pattern = pattern.replaceAll(" |:", "-");

      for (String p : SUPPORTED_DATE_PATTERNS) {
        if (pattern.contains(p))
          return true;
      }
    }
    return false;
  }

  /**
   * Now delete those old files if needed.
   * 
   * @param pstrName
   *          The name of the new folder based on current system date.
   * @throws IOException
   **/
  static private boolean deletingFiles = false;

  private static synchronized void setDeletingFiles(boolean isDelete) {
    deletingFiles = isDelete;
  }

  private static synchronized boolean isDeletingFiles() {
    return deletingFiles;
  }

  boolean isExtendedLogFile(String name) {
    if (name == null)
      return false;
    File logMaster = new File(fileName);
    String master = logMaster.getName();
    return name.contains(master);
  }

  void cleanupOldFiles() {
    // If we need to delete log files
    if (maxLogs > 0 && !isDeletingFiles()) {
      // tell other thread, that we will do the clean up
      setDeletingFiles(true);

      try {
        // Array to hold the logs we are going to keep
        File[] logsToKeep = new File[maxLogs];

        // Get a 'master' file handle, and the parent directory from it
        File logMaster = new File(fileName);
        File logDir = logMaster.getParentFile();
        if (logDir.isDirectory()) {
          File[] logFiles = getLogFiles(logDir);
          // more files than expected, time to clean up!
          if (logFiles.length > maxLogs)
            for (File curLog : logFiles) {
              removeOldFile(logsToKeep, curLog);
            }
        }
      } catch (Exception e) {
        // ignore
        // LogLog.error("While deleting old files. " + e.getMessage(), e);
      } finally {
        // we are done with clean up.
        setDeletingFiles(false);
      }
    }
  }

  private void removeOldFile(File[] logsToKeep, File curLog) {
    String name = curLog.getAbsolutePath();

    // Check that the file is indeed one we want
    // (contains the master file name)
    if (name.indexOf(fileName) >= 0) {
      // Iterate through the array of logs we are
      // keeping
      for (int i = 0; curLog != null && i < logsToKeep.length; i++) {
        // Have we exhausted the 'to keep' array?
        if (logsToKeep[i] == null) {
          // Empty space, retain this log file
          logsToKeep[i] = curLog;
          curLog = null;
        }
        // If the 'kept' file is older than the
        // current one
        else if (logsToKeep[i].getName().compareTo(curLog.getName()) < 0) {
          // Replace tested entry with current
          // file
          File temp = logsToKeep[i];
          logsToKeep[i] = curLog;
          curLog = temp;
        }
      }

      // If we have a 'current' entry at this point,
      // it's a log we don't want
      if (curLog != null) {
        curLog.delete();
      }
    }
  }

  private File[] getLogFiles(File logDir) {
    // Iterate all the files in that directory
    FilenameFilter filter = new FilenameFilter() {
      @Override
      public boolean accept(File dir, String name) {
        return isExtendedLogFile(name);
      }
    };
    File[] logFiles = logDir.listFiles(filter); // only get the
    // extended
    // files
    return logFiles;
  }
}

 

 

And here is the sample log4j.properties

#------------------------------------------------------------------------------
#  Provide AS IS No Warranty!
#  Author: T.A. Nguyen
#
#  Default log4j.properties file.  This should be in the class path
#
#    Possible Log Levels:
#      FATAL, ERROR, WARN, INFO, DEBUG
#
# Production Setting:
#log4j.rootCategory=ERROR, DAILY
# Development and Build Setting:
log4j.rootCategory=DEBUG, DAILY, CONSOLE

#------------------------------------------------------------------------------
#
#  The following properties configure the console (stdout) appender.
#
#------------------------------------------------------------------------------
log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender
#log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n

#------------------------------------------------------------------------------
#
#  The following properties configure the Daily File appender.
#
#------------------------------------------------------------------------------
log4j.appender.DAILY = org.cnci.util.DailyFileAppender
log4j.appender.DAILY.File = log/MyApplication.log
log4j.appender.DAILY.MaxLogs = 30
log4j.appender.DAILY.Append = true
log4j.appender.DAILY.DatePattern = '.'yyy-MM-dd
log4j.appender.DAILY.layout = org.apache.log4j.PatternLayout
log4j.appender.DAILY.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n

# Extracting all MyApplication logging into it own file
#log4j.logger.org.cnci=DEBUG, DAILY
#log4j.additivity.org.cnci=false

Bookmark with:

Deli.cio.us    Digg    reddit    Facebook    StumbleUpon    Newsvine
Last Updated on Tuesday, 25 February 2014 12:49
 
Double dereference for Ant Property PDF Print E-mail
Written by T.A. Nguyen   
Friday, 27 April 2012 05:33

Just random note on solving the double dereference that ANT property will not allow.


 
 
    
    
    
    
    
    
       myApp     : ${org.cnci.firstapp}
       myCompany : ${firstCompany}
       myPackage : ${firstPackage}
       myClass   : ${firstClass}
    
 

 
    
    
    
       
    
 

 
    
    
    
    
    
    
       myApp     : ${org.cnci.secondapp}
       myCompany : ${secondCompany}
       myPackage : ${secondPackage}
       myClass   : ${secondClass}
    
 

 

And the output look like this:

NOT-WORKING-double-dereferences:
     [echo] 
     [echo] 		myApp     : SimpleIRC
     [echo] 		myCompany : cnci
     [echo] 		myPackage : org.cnci.firstapp
     [echo] 		myClass   : ${org.${firstCompany}.firstapp}
     [echo] 		
WORKING-double-dereferences:
     [echo] 
     [echo] 		myApp     : AdvanceIRC
     [echo] 		myCompany : cnci
     [echo] 		myPackage : org.cnci.secondapp
     [echo] 		myClass   : AdvanceIRC
     [echo] 		

Bookmark with:

Deli.cio.us    Digg    reddit    Facebook    StumbleUpon    Newsvine
Last Updated on Friday, 27 April 2012 08:55
 
<< Start < Prev 1 2 3 4 5 6 7 8 9 Next > End >>

Page 1 of 9
T.A. Nguyen, Powered by Joomla! and VNSAB.com