/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jk.server;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.*;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.jk.core.JkHandler;
import org.apache.jk.core.WorkerEnv;
import org.apache.tomcat.util.modeler.Registry;
import com.sun.grizzly.util.IntrospectionUtils;
/** Main class used to startup and configure jk. It manages the conf/jk2.properties file
* and is the target of JMX proxy.
*
* It implements a policy of save-on-change - whenever a property is changed at
* runtime the jk2.properties file will be overriden.
*
* You can edit the config file when tomcat is stoped ( or if you don't use JMX or
* other admin tools ).
*
* The format of jk2.properties:
*
* - TYPE[.LOCALNAME].PROPERTY_NAME=VALUE
*
- Set a property on the associated component. TYPE will be used to
* find the class name and instantiate the component. LOCALNAME allows
* multiple instances. In JMX mode, TYPE and LOCALNAME will form the
* JMX name ( eventually combined with a 'jk2' component )
*
*
- NAME=VALUE
*
- Define global properties to be used in ${} substitutions
*
*
- class.COMPONENT_TYPE=JAVA_CLASS_NAME
*
- Adds a new 'type' of component. We predefine all known types.
*
*
* Instances are created the first time a component name is found. In addition,
* 'handler.list' property will override the list of 'default' components that are
* loaded automatically.
*
* Note that the properties file is just one (simplistic) way to configure jk. We hope
* to see configs based on registry, LDAP, db, etc. ( XML is not necesarily better )
*
* @author Costin Manolache
*/
@SuppressWarnings({"StringContatenationInLoop"})
public class JkMain implements MBeanRegistration
{
WorkerEnv wEnv;
String propFile;
Properties props=new Properties();
Properties modules=new Properties();
boolean modified=false;
boolean started=false;
boolean saveProperties=false;
public JkMain()
{
modules.put("channelSocket", "org.apache.jk.common.ChannelSocket");
modules.put("channelNioSocket", "org.apache.jk.common.ChannelNioSocket");
modules.put("channelUnix", "org.apache.jk.common.ChannelUn");
modules.put("channelJni", "org.apache.jk.common.ChannelJni");
modules.put("apr", "org.apache.jk.apr.AprImpl");
modules.put("mx", "org.apache.jk.common.JkMX");
modules.put("modeler", "org.apache.jk.common.JkModeler");
modules.put("shm", "org.apache.jk.common.Shm");
modules.put("request","org.apache.jk.common.HandlerRequest");
modules.put("container","org.apache.jk.common.HandlerRequest");
modules.put("modjk","org.apache.jk.common.ModJkMX");
}
private static final String DEFAULT_HTTPS="com.sun.net.ssl.internal.www.protocol";
private void initHTTPSUrls() {
try {
// 11657: if only ajp is used, https: redirects need to work ( at least for 1.3+)
String value = System.getProperty("java.protocol.handler.pkgs");
if (value == null) {
value = DEFAULT_HTTPS;
} else if (value.indexOf(DEFAULT_HTTPS) >= 0 ) {
return; // already set
} else {
value += "|" + DEFAULT_HTTPS;
}
System.setProperty("java.protocol.handler.pkgs", value);
} catch(Exception ex ) {
log.log(Level.INFO, "Error adding SSL Protocol Handler",ex);
}
}
// -------------------- Setting --------------------
/** Load a .properties file into and set the values
* into jk2 configuration.
*/
public void setPropertiesFile( String p ) {
propFile=p;
if( started ) {
loadPropertiesFile();
}
}
public String getPropertiesFile() {
return propFile;
}
public void setSaveProperties( boolean b ) {
saveProperties=b;
}
/** Set a name/value as a jk2 property
*/
public void setProperty( String n, String v ) {
if( "jkHome".equals( n ) ) {
setJkHome( v );
}
if( "propertiesFile".equals( n ) ) {
setPropertiesFile( v );
}
props.put( n, v );
if( started ) {
processProperty( n, v );
saveProperties();
}
}
/**
* Retrieve a property.
*/
public Object getProperty(String name) {
String alias = replacements.get(name);
Object result = null;
if(alias != null) {
result = props.get(alias);
}
if(result == null) {
result = props.get(name);
}
return result;
}
/**
* Set the channelClassName
that will used to connect to
* httpd.
*/
public void setChannelClassName(String name) {
props.put( "handler.channel.className",name);
}
public String getChannelClassName() {
return props.getProperty( "handler.channel.className");
}
/**
* Set the workerClassName
that will handle the request.
* ( sort of 'pivot' in axis :-)
*/
public void setWorkerClassName(String name) {
props.put( "handler.container.className",name);
}
public String getWorkerClassName() {
return props.getProperty( "handler.container.className");
}
/** Set the base dir of jk2. ( including WEB-INF if in a webapp ).
* We'll try to guess it from classpath if none is set ( for
* example on command line ), but if in a servlet environment
* you need to use Context.getRealPath or a system property or
* set it expliciltey.
*/
public void setJkHome( String s ) {
getWorkerEnv().setJkHome(s);
}
public String getJkHome() {
return getWorkerEnv().getJkHome();
}
String out;
String err;
File propsF;
public void setOut( String s ) {
out =s;
}
public String getOut() {
return out;
}
public void setErr( String s ) {
err =s;
}
public String getErr() {
return err;
}
// -------------------- Initialization --------------------
public void init() throws IOException
{
long t1=System.currentTimeMillis();
if(null != out) {
PrintStream outS=new PrintStream(new FileOutputStream(out));
System.setOut(outS);
}
if(null != err) {
PrintStream errS=new PrintStream(new FileOutputStream(err));
System.setErr(errS);
}
String home=getWorkerEnv().getJkHome();
if( home==null ) {
// XXX use IntrospectionUtil to find myself
guessHome();
}
home=getWorkerEnv().getJkHome();
if( home==null ) {
log.info( "Can't find home, jk2.properties not loaded");
}
if(log.isLoggable(Level.FINEST))
log.finest("Starting Jk2, base dir= " + home );
loadPropertiesFile();
String initHTTPS = props.getProperty("class.initHTTPS");
if("true".equalsIgnoreCase(initHTTPS)) {
initHTTPSUrls();
}
long t2=System.currentTimeMillis();
initTime=t2-t1;
}
static final String defaultHandlers[]= { "request",
"container",
"channelSocket"};
/*
static String defaultHandlers[]= { "apr",
"shm",
"request",
"container",
"channelSocket",
"channelJni",
"channelUnix"};
*/
public void stop()
{
for( int i=0; i());
jkMain.init();
jkMain.start();
} catch( Exception ex ) {
log.log(Level.WARNING, "Error running",ex);
}
}
// -------------------- Private methods --------------------
private boolean checkPropertiesFile() {
if(propFile == null) {
return false;
}
propsF = new File(propFile);
if(!propsF.isAbsolute()) {
String home = getWorkerEnv().getJkHome();
if( home == null ) {
return false;
}
propsF = new File(home, propFile);
}
return propsF.exists();
}
private void loadPropertiesFile() {
if(!checkPropertiesFile()) {
return;
}
try {
props.load( new FileInputStream(propsF) );
} catch(IOException ex ){
log.log(Level.WARNING, "Unable to load properties from "+propsF,ex);
}
}
public void saveProperties() {
if( !saveProperties) return;
if(propsF == null) {
log.warning("No properties file specified. Unable to save");
return;
}
// Temp - to check if it works
File outFile= new File(propsF.getParentFile(), propsF.getName()+".save");
log.finest("Saving properties " + outFile );
try {
props.store( new FileOutputStream(outFile), "AUTOMATICALLY GENERATED" );
} catch(IOException ex ){
log.log(Level.WARNING, "Unable to save to "+outFile,ex);
}
}
// translate top-level keys ( from coyote or generic ) into component keys
static final Hashtable replacements=new Hashtable();
static {
replacements.put("port","channelSocket.port");
replacements.put("maxThreads", "channelSocket.maxThreads");
replacements.put("minSpareThreads", "channelSocket.minSpareThreads");
replacements.put("maxSpareThreads", "channelSocket.maxSpareThreads");
replacements.put("backlog", "channelSocket.backlog");
replacements.put("tcpNoDelay", "channelSocket.tcpNoDelay");
replacements.put("soTimeout", "channelSocket.soTimeout");
replacements.put("timeout", "channelSocket.timeout");
replacements.put("address", "channelSocket.address");
replacements.put("bufferSize", "channelSocket.bufferSize");
replacements.put("tomcatAuthentication", "request.tomcatAuthentication");
replacements.put("packetSize", "channelSocket.packetSize");
}
private void preProcessProperties() {
Enumeration keys=props.keys();
Vector v = new Vector();
while( keys.hasMoreElements() ) {
String key=(String)keys.nextElement();
Object newName=replacements.get(key);
if( newName !=null ) {
v.addElement(key);
}
}
keys=v.elements();
while( keys.hasMoreElements() ) {
String key=(String)keys.nextElement();
Object propValue=props.getProperty( key );
String replacement= replacements.get(key);
props.put(replacement, propValue);
if( log.isLoggable(Level.FINEST))
log.finest("Substituting " + key + " " + replacement + " " +
propValue);
}
}
private void processProperties() {
preProcessProperties();
Enumeration keys=props.keys();
while( keys.hasMoreElements() ) {
String name=(String)keys.nextElement();
String propValue=props.getProperty( name );
processProperty( name, propValue );
}
}
private void processProperty(String name, String propValue) {
String type=name;
String fullName=name;
String localName="";
String propName="";
// ignore
if( name.startsWith("key.")) return;
int dot=name.indexOf(".");
int lastDot=name.lastIndexOf(".");
if( dot > 0 ) {
type=name.substring(0, dot );
if( dot != lastDot ) {
localName=name.substring( dot + 1, lastDot );
fullName=type + "." + localName;
} else {
fullName=type;
}
propName=name.substring( lastDot+1);
} else {
return;
}
if( log.isLoggable(Level.FINEST) )
log.finest( "Processing " + type + ":" + localName + ":" + fullName + " " + propName );
if( "class".equals( type ) || "handler".equals( type ) ) {
return;
}
JkHandler comp=getWorkerEnv().getHandler( fullName );
if( comp==null ) {
comp=newHandler( type, localName, fullName );
}
if( comp==null )
return;
if( log.isLoggable(Level.FINEST) )
log.finest("Setting " + propName + " on " + fullName + " " + comp);
setBeanProperty( comp, propName, propValue );
}
private JkHandler newHandler( String type, String localName, String fullName )
{
JkHandler handler;
String classN=modules.getProperty(type);
if( classN == null ) {
log.severe("No class name for " + fullName + " " + type );
return null;
}
try {
Class channelclass = Class.forName(classN);
handler=(JkHandler)channelclass.newInstance();
} catch (Throwable ex) {
handler=null;
log.log(Level.SEVERE, "Can't create " + fullName, ex );
return null;
}
if(domain != null ) {
try {
ObjectName handlerOname = new ObjectName
(domain + ":" + "type=JkHandler,name=" + fullName);
Registry.getRegistry(null, null).registerComponent(handler, handlerOname, classN);
} catch (Exception e) {
log.log(Level.SEVERE, "Error registering " + fullName, e );
}
}
wEnv.addHandler( fullName, handler );
return handler;
}
private void processModules() {
Enumeration keys=props.keys();
int plen=6;
while( keys.hasMoreElements() ) {
String k=(String)keys.nextElement();
if( ! k.startsWith( "class." ) )
continue;
String name= k.substring( plen );
String propValue=props.getProperty( k );
if( log.isLoggable(Level.FINEST)) log.finest("Register " + name + " " + propValue );
modules.put( name, propValue );
}
}
private String[] split(String s, String delim ) {
Vector v = new Vector();
StringTokenizer st=new StringTokenizer(s, delim );
while( st.hasMoreTokens() ) {
v.addElement( st.nextToken());
}
String res[]=new String[ v.size() ];
for( int i=0; i