0001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
0002: * This code is licensed under the GPL 2.0 license, availible at the root
0003: * application directory.
0004: */
0005: package org.vfny.geoserver.global;
0006:
0007: import java.io.File;
0008: import java.io.FileInputStream;
0009: import java.io.FileNotFoundException;
0010: import java.io.FileOutputStream;
0011: import java.io.IOException;
0012: import java.io.InputStream;
0013: import java.io.OutputStream;
0014: import java.nio.charset.Charset;
0015: import java.util.ArrayList;
0016: import java.util.Enumeration;
0017: import java.util.Iterator;
0018: import java.util.List;
0019: import java.util.Properties;
0020: import java.util.Set;
0021: import java.util.logging.Level;
0022:
0023: import javax.imageio.ImageIO;
0024: import javax.media.jai.JAI;
0025: import javax.media.jai.RecyclingTileFactory;
0026: import javax.servlet.ServletContext;
0027:
0028: import org.apache.log4j.Appender;
0029: import org.apache.log4j.LogManager;
0030: import org.apache.log4j.PropertyConfigurator;
0031: import org.apache.log4j.helpers.LogLog;
0032: import org.geotools.data.DataStoreFactorySpi;
0033: import org.springframework.beans.factory.DisposableBean;
0034: import org.vfny.geoserver.global.dto.ContactDTO;
0035: import org.vfny.geoserver.global.dto.GeoServerDTO;
0036: import org.vfny.geoserver.util.Requests;
0037:
0038: import com.sun.media.jai.util.SunTileCache;
0039:
0040: /**
0041: * Complete configuration set for the whole server
0042: *
0043: * @author Gabriel Roldan
0044: * @author dzwiers
0045: * @version $Id: GeoServer.java 8406 2008-02-14 19:49:39Z saul.farber $
0046: */
0047: public class GeoServer extends GlobalLayerSupertype implements
0048: DisposableBean {
0049: /**
0050: * Simple listener interface.
0051: *
0052: * JD: this is a temporary substitute until we have a decent config system.
0053: */
0054: public interface Listener {
0055: /**
0056: * Callback fired when application state has changed.
0057: */
0058: void changed();
0059:
0060: }
0061:
0062: /**
0063: * For finding the instance of this class to use from the web container
0064: *
0065: * <p>
0066: * ServletContext sc = ... GeoServer gs =
0067: * (GeoServer)sc.getAttribute(GeoServer.WEB_CONTAINER_KEY);
0068: * </p>
0069: */
0070: public static final String WEB_CONTAINER_KEY = "GeoServer";
0071: private String title;
0072: private int maxFeatures = Integer.MAX_VALUE;
0073: private boolean verbose = true;
0074: private int numDecimals = 4;
0075: private Charset charSet = Charset.forName("UTF-8");
0076: private final JAI jaiDef = JAI.getDefaultInstance();
0077: private SunTileCache jaiCache;
0078: private String adminUserName = "admin";
0079: private String adminPassword;
0080: private String schemaBaseUrl;
0081: private String proxyBaseUrl;
0082: private String contactPerson;
0083: private String contactOrganization;
0084: private String contactPosition;
0085: private String addressType;
0086: private String address;
0087: private String addressCity;
0088: private String addressState;
0089: private String addressPostalCode;
0090: private String addressCountry;
0091: private String contactVoice;
0092: private String contactFacsimile;
0093: private String contactEmail;
0094: private String onlineResource;
0095: private double memoryCapacity;
0096: private double memoryThreshold;
0097: private int tileThreads;
0098: private int tilePriority;
0099: private String tileCache;
0100: private Boolean recycling;
0101: private Boolean imageIOCache;
0102: private Boolean JPEGnativeAcc;
0103: private Boolean PNGnativeAcc;
0104:
0105: /** Should we throw the stack traces back in responses? */
0106: private boolean verboseExceptions = false;
0107:
0108: private String log4jConfigFile;
0109: private boolean suppressStdOutLogging = false;
0110: private String logLocation = null;
0111:
0112: private List listeners;
0113: private Config config;
0114:
0115: private int updateSequence;
0116:
0117: /** Used by tests to use programmatic configuration of the logging level */
0118: private static boolean suppressLoggingConfiguration;
0119:
0120: /**
0121: * Default constructor only to facilitate unit testing mock ups; real
0122: * uses shall create an instance through {@link #GeoServer(Config)}.
0123: */
0124: public GeoServer() {
0125: //do nothing
0126: }
0127:
0128: /**
0129: * Creates a GeoServer instance and loads its configuration.
0130: *
0131: * @throws ConfigurationException
0132: */
0133: public GeoServer(Config config) throws ConfigurationException {
0134: LOGGER.fine("Creating GeoServer");
0135: load(config.getGeoServer());
0136: this .config = config;
0137:
0138: listeners = new ArrayList();
0139: }
0140:
0141: /**
0142: * Adds a listener to be notified of state change.
0143: */
0144: public void addListener(Listener listener) {
0145: listeners.add(listener);
0146: }
0147:
0148: /**
0149: * Removes a listener.
0150: */
0151: public void removeListener(Listener listener) {
0152: listeners.remove(listener);
0153: }
0154:
0155: /**
0156: * Notifies all listeners of a change.
0157: */
0158: public void fireChange() {
0159: for (Iterator l = listeners.iterator(); l.hasNext();) {
0160: Listener listener = (Listener) l.next();
0161: try {
0162: listener.changed();
0163: } catch (Throwable t) {
0164: LOGGER
0165: .warning("listener threw exception, turn logging to FINE to view stack trace");
0166: LOGGER.log(Level.FINE, t.getLocalizedMessage(), t);
0167: }
0168: }
0169: }
0170:
0171: /**
0172: * getAddress purpose.
0173: *
0174: * <p>
0175: * Returns the contact Address.
0176: * </p>
0177: *
0178: * @return String the contact Address.
0179: */
0180: public String getAddress() {
0181: return notNull(address);
0182: }
0183:
0184: /**
0185: * getAddressCity purpose.
0186: *
0187: * <p>
0188: * Returns the contact City.
0189: * </p>
0190: *
0191: * @return String the contact City.
0192: */
0193: public String getAddressCity() {
0194: return notNull(addressCity);
0195: }
0196:
0197: /**
0198: * getAddressCountry purpose.
0199: *
0200: * <p>
0201: * Returns the contact Country.
0202: * </p>
0203: *
0204: * @return String the contact Country.
0205: */
0206: public String getAddressCountry() {
0207: return notNull(addressCountry);
0208: }
0209:
0210: /**
0211: * getAddressPostalCode purpose.
0212: *
0213: * <p>
0214: * Returns the contact PostalCode.
0215: * </p>
0216: *
0217: * @return String the contact PostalCode.
0218: */
0219: public String getAddressPostalCode() {
0220: return notNull(addressPostalCode);
0221: }
0222:
0223: /**
0224: * getAddressState purpose.
0225: *
0226: * <p>
0227: * Returns the contact State.
0228: * </p>
0229: *
0230: * @return String the contact State.
0231: */
0232: public String getAddressState() {
0233: return notNull(addressState);
0234: }
0235:
0236: /**
0237: * getAddressType purpose.
0238: *
0239: * <p>
0240: * Returns the contact Address Type.
0241: * </p>
0242: *
0243: * @return String the contact Address Type.
0244: */
0245: public String getAddressType() {
0246: return notNull(addressType);
0247: }
0248:
0249: /**
0250: * getCharSet purpose.
0251: *
0252: * <p>
0253: * Returns the default charset for this server instance.
0254: * </p>
0255: *
0256: * @return Charset the default charset for this server instance.
0257: */
0258: public Charset getCharSet() {
0259: if (charSet != null) {
0260: return charSet;
0261: }
0262:
0263: return Charset.forName("UTF-8");
0264: }
0265:
0266: /**
0267: * getContactEmail purpose.
0268: *
0269: * <p>
0270: * Returns the contact Email.
0271: * </p>
0272: *
0273: * @return String the contact Email.
0274: */
0275: public String getContactEmail() {
0276: return notNull(contactEmail);
0277: }
0278:
0279: /**
0280: * getContactFacsimile purpose.
0281: *
0282: * <p>
0283: * Returns the contact Facsimile.
0284: * </p>
0285: *
0286: * @return String the contact Facsimile.
0287: */
0288: public String getContactFacsimile() {
0289: return notNull(contactFacsimile);
0290: }
0291:
0292: /**
0293: * getContactOrganization purpose.
0294: *
0295: * <p>
0296: * Returns the contact Organization.
0297: * </p>
0298: *
0299: * @return String the contact Organization.
0300: */
0301: public String getContactOrganization() {
0302: return notNull(contactOrganization);
0303: }
0304:
0305: /**
0306: * getContactPerson purpose.
0307: *
0308: * <p>
0309: * Returns the contact Person.
0310: * </p>
0311: *
0312: * @return String the contact Person.
0313: */
0314: public String getContactPerson() {
0315: return notNull(contactPerson);
0316: }
0317:
0318: /**
0319: * getContactPosition purpose.
0320: *
0321: * <p>
0322: * Returns the contact Position.
0323: * </p>
0324: *
0325: * @return String the contact Position.
0326: */
0327: public String getContactPosition() {
0328: return notNull(contactPosition);
0329: }
0330:
0331: /**
0332: * getContactVoice purpose.
0333: *
0334: * <p>
0335: * Returns the contact Phone.
0336: * </p>
0337: *
0338: * @return String the contact Phone.
0339: */
0340: public String getContactVoice() {
0341: return notNull(contactVoice);
0342: }
0343:
0344: /**
0345: * getOnlineResource purpose.
0346: *
0347: * <p>
0348: * Returns the online Resource.
0349: * </p>
0350: *
0351: * @return String the online Resource.
0352: */
0353: public String getOnlineResource() {
0354: return notNull(onlineResource);
0355: }
0356:
0357: /**
0358: * getLoggingLevel purpose.
0359: *
0360: * <p>
0361: * Returns the Logging Level.
0362: * </p>
0363: *
0364: * @return String the Logging Level.
0365: */
0366: public String getLog4jConfigFile() {
0367: return log4jConfigFile;
0368: }
0369:
0370: /**
0371: * getMaxFeatures purpose.
0372: *
0373: * <p>
0374: * Returns the max number of features supported.
0375: * </p>
0376: *
0377: * @return String the max number of features supported.
0378: */
0379: public int getMaxFeatures() {
0380: return maxFeatures;
0381: }
0382:
0383: /**
0384: * getMimeType purpose.
0385: *
0386: * <p>
0387: * Returns the server default mimetype.
0388: * </p>
0389: *
0390: * @return String the server default mimetype.
0391: */
0392: public String getMimeType() {
0393: return "text/xml; charset=" + getCharSet().name();
0394: }
0395:
0396: /**
0397: * getNumDecimals purpose.
0398: *
0399: * <p>
0400: * The default number of decimals allowed in the data.
0401: * </p>
0402: *
0403: * @return int the default number of decimals allowed in the data.
0404: */
0405: public int getNumDecimals() {
0406: return numDecimals;
0407: }
0408:
0409: /**
0410: * getSchemaBaseUrl purpose.
0411: *
0412: * <p>
0413: * The Schema Base URL for this instance. This should generally be a local
0414: * reference, as GeoServer by default puts up the schemas that it needs
0415: * and references them. It could be used to specify an alternate site for
0416: * the schemas, however, for example if a user didn't want their servlet
0417: * container hit every time someone did a validation, they could instead
0418: * store it on another machine. I don't really know if this is useful to
0419: * anyone...
0420: * </p>
0421: *
0422: * @return String the Schema Base URL for this instance.
0423: *
0424: * @task TODO: Right now this is broken, and I'm not quite sure if there's
0425: * an elegant way to have this return the local schemas. Perhaps we
0426: * should just have it return 'local', and then the users of this
0427: * method can do the local referencing themselves. For now no one
0428: * is using this method, perhaps we should just leave it out for
0429: * 1.2.0, as it's very obscure. I think I only added it originally
0430: * because I didn't want to go through the busy work of cleaning up
0431: * and figuring out how to copy over the ogc schemas.
0432: */
0433: public String getSchemaBaseUrl() {
0434: return schemaBaseUrl;
0435: }
0436:
0437: /**
0438: * Used when Geoserver is running behind a reverse-proxy so that url
0439: * in getCapabilities documents are fine
0440: * @return
0441: */
0442: public String getProxyBaseUrl() {
0443: return proxyBaseUrl;
0444: }
0445:
0446: /**
0447: * @return the updateSequence
0448: */
0449: public int getUpdateSequence() {
0450: return updateSequence;
0451: }
0452:
0453: /**
0454: * @param updateSequence the updateSequence to set
0455: */
0456: public void setUpdateSequence(int updateSequence) {
0457: this .updateSequence = updateSequence;
0458: }
0459:
0460: /**
0461: * whether xml documents should be pretty formatted
0462: *
0463: * @return true when verbose
0464: */
0465: public boolean isVerbose() {
0466: return verbose;
0467: }
0468:
0469: /**
0470: * <p>
0471: * Loads the GeoServerDTO into the current instance as a GeoServer object
0472: * </p>
0473: *
0474: * @param dto
0475: * @throws ConfigurationException
0476: */
0477: public void load(GeoServerDTO dto) throws ConfigurationException {
0478: if (dto != null) {
0479: address = dto.getContact().getAddress();
0480: addressCity = dto.getContact().getAddressCity();
0481: addressCountry = dto.getContact().getAddressCountry();
0482: addressPostalCode = dto.getContact().getAddressPostalCode();
0483: addressState = dto.getContact().getAddressState();
0484: addressType = dto.getContact().getAddressType();
0485: charSet = dto.getCharSet();
0486: contactEmail = dto.getContact().getContactEmail();
0487: contactFacsimile = dto.getContact().getContactFacsimile();
0488: contactOrganization = dto.getContact()
0489: .getContactOrganization();
0490: contactPerson = dto.getContact().getContactPerson();
0491: contactPosition = dto.getContact().getContactPosition();
0492: contactVoice = dto.getContact().getContactVoice();
0493:
0494: log4jConfigFile = dto.getLog4jConfigFile();
0495: suppressStdOutLogging = dto.getSuppressStdOutLogging();
0496: logLocation = dto.getLogLocation();
0497: try {
0498: if (!suppressLoggingConfiguration)
0499: configureGeoServerLogging(log4jConfigFile,
0500: suppressStdOutLogging, logLocation);
0501: } catch (IOException ioe) {
0502: if (LOGGER.isLoggable(Level.FINE)) {
0503: LOGGER.log(Level.FINE, "", ioe);
0504: }
0505: throw new ConfigurationException("", ioe);
0506: }
0507:
0508: memoryCapacity = dto.getJaiMemoryCapacity();
0509: memoryThreshold = dto.getJaiMemoryThreshold();
0510: tileThreads = dto.getJaiTileThreads();
0511: tilePriority = dto.getJaiTilePriority();
0512: tileCache = dto.getTileCache();
0513: recycling = dto.getJaiRecycling();
0514: imageIOCache = dto.getImageIOCache();
0515: JPEGnativeAcc = dto.getJaiJPEGNative();
0516: PNGnativeAcc = dto.getJaiPNGNative();
0517:
0518: initJAI(memoryCapacity, memoryThreshold, recycling,
0519: imageIOCache);
0520:
0521: maxFeatures = dto.getMaxFeatures();
0522: numDecimals = dto.getNumDecimals();
0523: onlineResource = dto.getContact().getOnlineResource();
0524: schemaBaseUrl = dto.getSchemaBaseUrl();
0525: proxyBaseUrl = dto.getProxyBaseUrl();
0526: verbose = dto.isVerbose();
0527: adminUserName = dto.getAdminUserName();
0528: adminPassword = dto.getAdminPassword();
0529: verboseExceptions = dto.isVerboseExceptions();
0530: updateSequence = dto.getUpdateSequence();
0531: } else {
0532: throw new ConfigurationException(
0533: "load(GeoServerDTO) expected a non-null value");
0534: }
0535: }
0536:
0537: /**
0538: *
0539: * load purpose.
0540: *
0541: * <p>
0542: * Loads the GeoServerDTO into the current instance as a GeoServer object.
0543: * As GeoServer moves to Spring, we want to move away from storing state
0544: * in the servlet context, so this method is deprecated.
0545: * </p>
0546: *
0547: * @param dto GeoServerDTO
0548: *
0549: * @throws ConfigurationException If an error occurs
0550: *
0551: * @deprecated use {@link #load(GeoServerDTO)}
0552: */
0553: public final void load(GeoServerDTO dto, ServletContext context)
0554: throws ConfigurationException {
0555: load(dto);
0556: }
0557:
0558: /**
0559: * Convenience method for determining the actual location on the local file
0560: * system of the log file based an arbirtrary path. Relative paths are
0561: * appended to the geoserver data directory.
0562: *
0563: * @param location The log file path, this can be an absolute or relative
0564: * path.
0565: * @param context The servlet context
0566: *
0567: * @return The file containing the absolute path to the log file.
0568: * @throws IOException
0569: */
0570: public static File getLogLocation(String logLocation)
0571: throws IOException {
0572: File f = new File(logLocation);
0573:
0574: if (f.exists()) {
0575: if (f.isDirectory()) {
0576: //attach a file to the end of the directory
0577: if (!logLocation.endsWith(File.separator)) {
0578: logLocation += File.separator;
0579: }
0580:
0581: logLocation += "geoserver.log";
0582: }
0583: } else {
0584: //could be a relative path
0585: if (!f.isAbsolute()) {
0586: //append to data dir
0587: File data = GeoserverDataDirectory
0588: .getGeoserverDataDirectory();
0589: f = new File(data, f.getPath());
0590: }
0591:
0592: //make sure parent directory exists
0593: if ((f.getParentFile() != null)
0594: && !f.getParentFile().exists()) {
0595: f.getParentFile().mkdirs();
0596: }
0597:
0598: f.createNewFile();
0599: }
0600:
0601: return f;
0602: }
0603:
0604: public static void configureGeoServerLogging(
0605: String log4jConfigFileStr, boolean suppressStdOutLogging,
0606: String logFileName) throws IOException,
0607: ConfigurationException {
0608:
0609: //to initialize logging we need to do a couple of things:
0610: // 1) Figure out whether the user has 'overridden' some configuration settings
0611: // in the logging system (not using log4j in commons-logging.properties or perhaps
0612: // has set up their own 'custom' log4j.properties file.
0613: // 2) If they *have*, then we don't worry about configuring logging
0614: // 3) If they haven't, then we configure logging to use the log4j config file
0615: // specified, and remove console appenders if the suppressstdoutlogging is true.
0616: LOGGER
0617: .fine("CONFIGURING GEOSERVER LOGGING -------------------------");
0618:
0619: if (log4jConfigFileStr == null) {
0620: log4jConfigFileStr = "DEFAULT_LOGGING.properties";
0621: LOGGER
0622: .warning("No log4jConfigFile defined in services.xml: using 'DEFAULT_LOGGING.properties'");
0623: }
0624:
0625: File log4jConfigFile = GeoserverDataDirectory
0626: .findConfigFile("logs" + File.separator
0627: + log4jConfigFileStr);
0628:
0629: if (log4jConfigFile == null) {
0630: //hmm, well, we don't have a log4j config file and this could be due to the fact
0631: //that this is a data-dir upgrade. We can count on the DEFAULT_LOGGING.properties file
0632: //being present on the classpath, so we'll upgrade their data_dir and then use the
0633: //default DEFAULT_LOGGING.properties configuration.
0634: LOGGER
0635: .warning("log4jConfigFile '"
0636: + log4jConfigFileStr
0637: + "' couldn't be found in the data dir, so GeoServer will "
0638: + "install the various logging config file into the data dir, and then try to find it again.");
0639:
0640: //this forces the data_dir/logs directory to be present (if it wasn't already)
0641: File lcdir = GeoserverDataDirectory
0642: .findCreateConfigDir("logs");
0643:
0644: //now we copy in the various logging config files from the base repo location on the classpath
0645: final String[] lcfiles = new String[] {
0646: "DEFAULT_LOGGING.properties",
0647: "VERBOSE_LOGGING.properties",
0648: "PRODUCTION_LOGGING.properties",
0649: "GEOTOOLS_DEVELOPER_LOGGING.properties",
0650: "GEOSERVER_DEVELOPER_LOGGING.properties" };
0651:
0652: for (int i = 0; i < lcfiles.length; i++) {
0653: File target = new File(lcdir.getAbsolutePath(),
0654: lcfiles[i]);
0655: if (!target.exists()) {
0656: copyResourceToFile(lcfiles[i], target);
0657: }
0658: }
0659:
0660: //ok, the possibly-new 'logs' directory is in-place, with all the various configs there.
0661: // Is the originally configured log4jconfigfile there now?
0662: log4jConfigFile = GeoserverDataDirectory
0663: .findConfigFile("logs" + File.separator
0664: + log4jConfigFileStr);
0665: if (log4jConfigFile == null) {
0666: LOGGER
0667: .warning("Still couldn't find log4jConfigFile '"
0668: + log4jConfigFileStr
0669: + "'. Using DEFAULT_LOGGING.properties instead.");
0670: }
0671:
0672: log4jConfigFile = GeoserverDataDirectory
0673: .findConfigFile("logs" + File.separator
0674: + "DEFAULT_LOGGING.properties");
0675: }
0676:
0677: if (log4jConfigFile == null || !log4jConfigFile.exists()) {
0678: throw new ConfigurationException(
0679: "Unable to load logging configuration '"
0680: + log4jConfigFileStr
0681: + "'. In addition, an attempt "
0682: + "was made to create the 'logs' directory in your data dir, and to use the DEFAULT_LOGGING configuration, but"
0683: + "this failed as well. Is your data dir writeable?");
0684: }
0685:
0686: // reconfiguring log4j logger levels by resetting and loading a new set of configuration properties
0687: InputStream loggingConfigStream = new FileInputStream(
0688: log4jConfigFile);
0689: if (loggingConfigStream == null) {
0690: LOGGER.warning("Couldn't open Log4J configuration file '"
0691: + log4jConfigFile.getAbsolutePath());
0692: return;
0693: } else {
0694: LOGGER.fine("GeoServer logging profile '"
0695: + log4jConfigFile.getName() + "' enabled.");
0696: }
0697:
0698: configureGeoServerLogging(loggingConfigStream,
0699: suppressStdOutLogging, false, logFileName);
0700: }
0701:
0702: public static void configureGeoServerLogging(
0703: InputStream loggingConfigStream,
0704: boolean suppressStdOutLogging, boolean suppressFileLogging,
0705: String logFileName) throws FileNotFoundException,
0706: IOException, ConfigurationException {
0707: Properties lprops = new Properties();
0708: lprops.load(loggingConfigStream);
0709: LogManager.resetConfiguration();
0710: // LogLog.setQuietMode(true);
0711: PropertyConfigurator.configure(lprops);
0712: // LogLog.setQuietMode(false);
0713:
0714: // configuring the log4j file logger
0715: if (!suppressFileLogging) {
0716: Appender gslf = org.apache.log4j.Logger.getRootLogger()
0717: .getAppender("geoserverlogfile");
0718: if (gslf instanceof org.apache.log4j.RollingFileAppender) {
0719: if (logFileName == null) {
0720: logFileName = new File(GeoserverDataDirectory
0721: .findCreateConfigDir("logs"),
0722: "geoserver.log").getAbsolutePath();
0723: } else {
0724: if (!new File(logFileName).isAbsolute()) {
0725: logFileName = new File(GeoserverDataDirectory
0726: .getGeoserverDataDirectory(),
0727: logFileName).getAbsolutePath();
0728: LOGGER
0729: .fine("Non-absolute pathname detected for logfile. Setting logfile relative to data dir.");
0730: }
0731: }
0732: lprops.setProperty(
0733: "log4j.appender.geoserverlogfile.File",
0734: logFileName);
0735: PropertyConfigurator.configure(lprops);
0736: LOGGER.fine("Logging output to file '" + logFileName
0737: + "'");
0738: } else if (gslf != null) {
0739: LOGGER
0740: .warning("'log4j.appender.geoserverlogfile' appender is defined, but isn't a RollingFileAppender. GeoServer won't control the file-based logging.");
0741: } else {
0742: LOGGER
0743: .warning("'log4j.appender.geoserverlogfile' appender isn't defined. GeoServer won't control the file-based logging.");
0744: }
0745: }
0746:
0747: // ... and the std output logging too
0748: if (suppressStdOutLogging) {
0749: LOGGER
0750: .warning("Suppressing StdOut logging. If you want to see GeoServer logs, be sure to look in '"
0751: + logFileName + "'");
0752: Enumeration allAppenders = org.apache.log4j.Logger
0753: .getRootLogger().getAllAppenders();
0754: Appender curApp;
0755: while (allAppenders.hasMoreElements()) {
0756: curApp = (Appender) allAppenders.nextElement();
0757: if (curApp instanceof org.apache.log4j.ConsoleAppender) {
0758: org.apache.log4j.Logger.getRootLogger()
0759: .removeAppender(curApp);
0760: }
0761: }
0762: }
0763: LOGGER
0764: .fine("FINISHED CONFIGURING GEOSERVER LOGGING -------------------------");
0765: }
0766:
0767: private static void copyResourceToFile(String resource, File target)
0768: throws ConfigurationException {
0769: InputStream is = null;
0770: OutputStream os = null;
0771: byte[] buffer = new byte[4096];
0772: int read;
0773: try {
0774: is = Thread.currentThread().getContextClassLoader()
0775: .getResourceAsStream(resource);
0776: os = new FileOutputStream(target);
0777: while ((read = is.read(buffer)) > 0)
0778: os.write(buffer, 0, read);
0779: } catch (FileNotFoundException targetException) {
0780: throw new ConfigurationException(
0781: "Can't write to file "
0782: + target.getAbsolutePath()
0783: + ". Check write permissions on target folder for user "
0784: + System.getProperty("user.name"));
0785: } catch (IOException e) {
0786: LOGGER.log(Level.WARNING,
0787: "Error trying to copy logging configuration file",
0788: e);
0789: } finally {
0790: try {
0791: if (is != null) {
0792: is.close();
0793: }
0794: if (os != null) {
0795: os.close();
0796: }
0797: } catch (IOException e) {
0798: // we tried...
0799: }
0800: }
0801: }
0802:
0803: public void initJAI(final double memCapacity,
0804: final double memoryThreshold, final Boolean recycling,
0805: final Boolean ImageIOCache) {
0806: // setting JAI wide hints
0807: jaiDef.setRenderingHint(JAI.KEY_CACHED_TILE_RECYCLING_ENABLED,
0808: recycling);
0809:
0810: // tile factory and recycler
0811: final RecyclingTileFactory recyclingFactory = new RecyclingTileFactory();
0812: jaiDef.setRenderingHint(JAI.KEY_TILE_FACTORY, recyclingFactory);
0813: jaiDef
0814: .setRenderingHint(JAI.KEY_TILE_RECYCLER,
0815: recyclingFactory);
0816:
0817: // Setting up Cache Capacity
0818: jaiCache = (SunTileCache) jaiDef.getTileCache();
0819:
0820: long jaiMemory = (long) (memCapacity * Runtime.getRuntime()
0821: .maxMemory());
0822: jaiCache.setMemoryCapacity(jaiMemory);
0823:
0824: // Setting up Cahce Threshold
0825: jaiCache.setMemoryThreshold((float) memoryThreshold);
0826:
0827: jaiDef.getTileScheduler().setParallelism(tileThreads);
0828: jaiDef.getTileScheduler().setPrefetchParallelism(tileThreads);
0829: jaiDef.getTileScheduler().setPriority(tilePriority);
0830: jaiDef.getTileScheduler().setPrefetchPriority(tilePriority);
0831:
0832: // ImageIO Caching
0833: ImageIO.setUseCache(ImageIOCache.booleanValue());
0834: }
0835:
0836: /**
0837: * toDTO purpose.
0838: *
0839: * <p>
0840: * This method is package visible only, and returns a reference to the
0841: * GeoServerDTO. This method is unsafe, and should only be used with
0842: * extreme caution.
0843: * </p>
0844: *
0845: * @return DTO the generated object
0846: */
0847: public Object toDTO() {
0848: GeoServerDTO dto = new GeoServerDTO();
0849: dto.setCharSet(charSet);
0850: dto.setLog4jConfigFile(log4jConfigFile);
0851: dto.setMaxFeatures(maxFeatures);
0852: dto.setNumDecimals(numDecimals);
0853: dto.setSchemaBaseUrl(schemaBaseUrl);
0854: dto.setProxyBaseUrl(proxyBaseUrl);
0855: dto.setVerbose(verbose);
0856: dto.setAdminUserName(adminUserName);
0857: dto.setAdminPassword(adminPassword);
0858: dto.setVerboseExceptions(verboseExceptions);
0859: dto.setSuppressStdOutLogging(suppressStdOutLogging);
0860: dto.setLogLocation(logLocation);
0861: dto.setJaiMemoryCapacity(memoryCapacity);
0862: dto.setJaiMemoryThreshold(memoryThreshold);
0863: dto.setJaiTileThreads(tileThreads);
0864: dto.setJaiTilePriority(tilePriority);
0865: dto.setTileCache(tileCache);
0866: dto.setJaiRecycling(recycling);
0867: dto.setImageIOCache(imageIOCache);
0868: dto.setJaiJPEGNative(JPEGnativeAcc);
0869: dto.setJaiPNGNative(PNGnativeAcc);
0870: dto.setUpdateSequence(updateSequence);
0871:
0872: ContactDTO cdto = new ContactDTO();
0873: dto.setContact(cdto);
0874:
0875: cdto.setAddress(address);
0876: cdto.setAddressCity(addressCity);
0877: cdto.setAddressCountry(addressCountry);
0878: cdto.setAddressPostalCode(addressPostalCode);
0879: cdto.setAddressState(addressState);
0880: cdto.setAddressType(addressType);
0881: cdto.setContactEmail(contactEmail);
0882: cdto.setContactFacsimile(contactFacsimile);
0883: cdto.setContactOrganization(contactOrganization);
0884: cdto.setContactPerson(contactPerson);
0885: cdto.setContactPosition(contactPosition);
0886: cdto.setContactVoice(contactVoice);
0887: cdto.setOnlineResource(onlineResource);
0888:
0889: return dto;
0890: }
0891:
0892: /**
0893: * DOCUMENT ME!
0894: *
0895: * @return Returns the title.
0896: */
0897: public String getTitle() {
0898: return title;
0899: }
0900:
0901: /**
0902: * DOCUMENT ME!
0903: *
0904: * @param title The title to set.
0905: */
0906: public void setTitle(String title) {
0907: this .title = title;
0908: }
0909:
0910: /**
0911: * Property representing the contact party (person, position or
0912: * organization).
0913: *
0914: * <p>
0915: * This is a derived property.
0916: * </p>
0917: *
0918: * @return Contact party (person, position or organization), null if
0919: * unknown
0920: */
0921: public String getContactParty() {
0922: if ((getContactPerson() != null)
0923: && (getContactPerson().length() != 0)) {
0924: return getContactPerson(); // ie Chris Holmes
0925: }
0926:
0927: if ((getContactPosition() != null)
0928: && (getContactPosition().length() != 0)) {
0929: return getContactPosition(); // ie Lead Developer
0930: }
0931:
0932: if ((getContactOrganization() != null)
0933: && (getContactOrganization().length() != 0)) {
0934: return getContactOrganization(); // ie TOPP
0935: }
0936:
0937: return null;
0938: }
0939:
0940: public String getAdminUserName() {
0941: return adminUserName;
0942: }
0943:
0944: public String getAdminPassword() {
0945: return adminPassword;
0946: }
0947:
0948: public String toString() {
0949: StringBuffer geoserver = new StringBuffer("[GeoServer: \n");
0950: geoserver.append(" maxFeatures - " + maxFeatures);
0951: geoserver.append("\n verbose - " + verbose);
0952: geoserver.append("\n numDecimals - " + numDecimals);
0953: geoserver.append("\n charSet - " + charSet);
0954: geoserver.append("\n log4jConfigFile - " + log4jConfigFile);
0955: geoserver.append("\n adminUserName - " + adminUserName);
0956: geoserver.append("\n adminPassword - " + adminPassword);
0957:
0958: return geoserver.toString();
0959: }
0960:
0961: /**
0962: * Should we display stackTraces or not? (And give them a nice
0963: * little message instead?)
0964: *
0965: * @return Returns the showStackTraces.
0966: */
0967: public boolean isVerboseExceptions() {
0968: return verboseExceptions;
0969: }
0970:
0971: /**
0972: * If set to true, response exceptions will throw their stack trace
0973: * back to the end user.
0974: *
0975: * @param showStackTraces The showStackTraces to set.
0976: */
0977: public void setVerboseExceptions(boolean showStackTraces) {
0978: this .verboseExceptions = showStackTraces;
0979: }
0980:
0981: /**
0982: * Returns the location of where the server ouputs logs. Note that this may
0983: * not reference an actual physical location on disk.
0984: * Call {@link GeoServer#getLogLocation(String, ServletContext)} to map this
0985: * string to a file on disk.
0986: *
0987: */
0988: public String getLogLocation() {
0989: return logLocation;
0990: }
0991:
0992: /**
0993: * @param logLocation The string representation of the path on disk in which
0994: * the server logs to.
0995: */
0996: public void setLogLocation(String logLocation) {
0997: this .logLocation = logLocation;
0998: }
0999:
1000: /**
1001: * @return True if the server is logging to file, otherwise false.
1002: */
1003: public boolean getSuppressStdOutLogging() {
1004: return suppressStdOutLogging;
1005: }
1006:
1007: /**
1008: * Toggles server logging to file.
1009: */
1010: public void setSuppressStdOutLogging(boolean loggingToFile) {
1011: this .suppressStdOutLogging = loggingToFile;
1012: }
1013:
1014: public JAI getJAIDefault() {
1015: return jaiDef;
1016: }
1017:
1018: public SunTileCache getJaiCache() {
1019: return jaiCache;
1020: }
1021:
1022: public double getMemoryCapacity() {
1023: return memoryCapacity;
1024: }
1025:
1026: public Boolean getRecycling() {
1027: return recycling;
1028: }
1029:
1030: public Boolean getJPEGNativeAcceleration() {
1031: return JPEGnativeAcc;
1032: }
1033:
1034: public Boolean getPNGNativeAcceleration() {
1035: return PNGnativeAcc;
1036: }
1037:
1038: public double getMemoryThreshold() {
1039: return memoryThreshold;
1040: }
1041:
1042: /**
1043: * @return Returns the imageIOCache.
1044: */
1045: public Boolean getImageIOCache() {
1046: return imageIOCache;
1047: }
1048:
1049: public int getTilePriority() {
1050: return tilePriority;
1051: }
1052:
1053: public int getTileThreads() {
1054: return tileThreads;
1055: }
1056:
1057: /**
1058: * Used when GeoServer is running beheind tile caching server.
1059: * <p>
1060: * This value should be used when writing out a url which is a getmap
1061: * request to reference the tile caching server and not GeoServer itself.
1062: * </p>
1063: *<p>
1064: * This value can be:
1065: * <ol>
1066: * <li>A fully qualified host name + path (URL)
1067: * <li>A partial path which is interpreted as relative to the host running
1068: * GeoServer
1069: * <li><code>null</code>
1070: * </ol>
1071: * </p>
1072: * <p>
1073: *
1074: * </p>
1075: * @see Requests#getTileCacheBaseUrl(javax.servlet.http.HttpServletRequest, GeoServer)
1076: */
1077: public String getTileCache() {
1078: return tileCache;
1079: }
1080:
1081: public void setTileCache(String tileCache) {
1082: this .tileCache = tileCache;
1083: }
1084:
1085: /**
1086: * Implements {@link DisposableBean#destroy()} to release resources being held
1087: * by the server at server shutdown, such as JDBC connection pools and ArcSDE
1088: * connection pools.
1089: * <p>
1090: * Note this process would greately benefit if {@link DataStoreFactorySpi} API
1091: * had some sort of resource releasing method, so we could just traverse
1092: * the available datastore factories asking them to release any resource
1093: * needed.
1094: * </p>
1095: */
1096: public void destroy() throws Exception {
1097: final Data catalog = (Data) config.getApplictionContext()
1098: .getBean("data");
1099: final Set dataStores = catalog.getDataStores();
1100: LOGGER.info("Disposing DataStores at GeoServer shutdown...");
1101: for (Iterator it = dataStores.iterator(); it.hasNext();) {
1102: DataStoreInfo dataStoreInfo = (DataStoreInfo) it.next();
1103: LOGGER.fine("Disposing " + dataStoreInfo.getId());
1104: try {
1105: dataStoreInfo.dispose();
1106: } catch (RuntimeException e) {
1107: LOGGER.log(Level.WARNING,
1108: "Caught exception while disposing datastore "
1109: + dataStoreInfo.getId(), e);
1110: }
1111: }
1112: LOGGER.info("Done disposing datastores.");
1113:
1114: /*
1115: * HACK: we must get a standard API way for releasing resources...
1116: * UPDATE: now we do have a standard API to release resources, though ArcSDE does not
1117: * properly implements DataStore.dispose() yet.
1118: */
1119: try {
1120: Class sdepfClass = Class
1121: .forName("org.geotools.arcsde.pool.ArcSDEConnectionPoolFactory");
1122:
1123: LOGGER.fine("SDE datasource found, releasing resources");
1124:
1125: java.lang.reflect.Method m = sdepfClass.getMethod(
1126: "getInstance", new Class[0]);
1127: Object pfInstance = m.invoke(sdepfClass, new Object[0]);
1128:
1129: LOGGER.fine("got sde connection pool factory instance: "
1130: + pfInstance);
1131:
1132: java.lang.reflect.Method closeMethod = pfInstance
1133: .getClass().getMethod("clear", new Class[0]);
1134:
1135: closeMethod.invoke(pfInstance, new Object[0]);
1136: LOGGER
1137: .info("Asking ArcSDE datasource to release connections.");
1138: } catch (ClassNotFoundException cnfe) {
1139: LOGGER.fine("No ArcSDE datasource found.");
1140: } catch (Exception ex) {
1141: ex.printStackTrace();
1142: }
1143: }
1144:
1145: /**
1146: * Call this method if you want GeoServer not to configure the logging subsystem as instructed
1147: * in the configuration file. To be used mainly in unit testing where we want to control
1148: * logging programmatically.
1149: */
1150: public static void suppressLoggingConfiguration() {
1151: suppressLoggingConfiguration = true;
1152: }
1153: }
|