static class Holder { Holder(MongoOptions options) { _options = options; } DBPortPool get(InetSocketAddress addr) { DBPortPool p = _pools.get(addr); if (p != null) return p; synchronized (_pools) { p = _pools.get(addr); if (p != null) { return p; } p = new DBPortPool(addr, _options); _pools.put(addr, p); String name = "com.mongodb:type=ConnectionPool,host=" + addr.toString().replace(':', '_'); try { ObjectName on = new ObjectName(name); if (_server.isRegistered(on)) { _server.unregisterMBean(on); Bytes.LOGGER.log( Level.INFO, "multiple Mongo instances for same host, jmx numbers might be off"); } _server.registerMBean(p, on); } catch (JMException e) { Bytes.LOGGER.log(Level.WARNING, "jmx registration error, continuing", e); } catch (java.security.AccessControlException e) { Bytes.LOGGER.log(Level.WARNING, "jmx registration error, continuing", e); } } return p; } void close() { synchronized (_pools) { for (DBPortPool p : _pools.values()) { p.close(); } } } final MongoOptions _options; final Map<InetSocketAddress, DBPortPool> _pools = Collections.synchronizedMap(new HashMap<InetSocketAddress, DBPortPool>()); final MBeanServer _server = ManagementFactory.getPlatformMBeanServer(); }
static { FILE_UTILS = FileUtils.getFileUtils(); AntClassLoader.pathMap = Collections.synchronizedMap(new HashMap<String, String>()); AntClassLoader.subClassToLoad = null; CONSTRUCTOR_ARGS = new Class[] {ClassLoader.class, Project.class, Path.class, Boolean.TYPE}; if (JavaEnvUtils.isAtLeastJavaVersion("1.5")) { try { AntClassLoader.subClassToLoad = Class.forName("org.apache.tools.ant.loader.AntClassLoader5"); } catch (ClassNotFoundException ex) { } } }
/** * Creates sockets capable of connecting through HTTPS and SOCKS proxies. * * @author Maros Sandor */ public class ProxySocketFactory extends SocketFactory { private static final int CONNECT_TIMEOUT = 1000 * 20; // / 20 seconds timeout private static final String AUTH_NONE = "<none>"; private static final String AUTH_BASIC = "Basic"; private static final Pattern sConnectionEstablishedPattern = Pattern.compile("HTTP\\/\\d+\\.\\d+\\s+200\\s+"); private static final Pattern sProxyAuthRequiredPattern = Pattern.compile("HTTP\\/\\d+\\.\\d+\\s+407\\s+"); private static final ProxySocketFactory instance = new ProxySocketFactory(); private final Map<InetSocketAddress, ConnectivitySettings> lastKnownSettings = Collections.synchronizedMap(new HashMap<InetSocketAddress, ConnectivitySettings>(2)); public static ProxySocketFactory getDefault() { return instance; } private ProxySocketFactory() {} /** Creates probe socket that supports only connect(SocketAddressm, int timeout). */ public Socket createSocket() throws IOException { return new Socket() { public void connect(SocketAddress endpoint, int timeout) throws IOException { Socket s = createSocket((InetSocketAddress) endpoint, timeout); s.close(); } public void bind(SocketAddress bindpoint) { throw new UnsupportedOperationException(); } protected Object clone() { throw new UnsupportedOperationException(); } public synchronized void close() {} public void connect(SocketAddress endpoint) { throw new UnsupportedOperationException(); } public SocketChannel getChannel() { throw new UnsupportedOperationException(); } public InetAddress getInetAddress() { throw new UnsupportedOperationException(); } public InputStream getInputStream() { throw new UnsupportedOperationException(); } public boolean getKeepAlive() { throw new UnsupportedOperationException(); } public InetAddress getLocalAddress() { throw new UnsupportedOperationException(); } public int getLocalPort() { throw new UnsupportedOperationException(); } public SocketAddress getLocalSocketAddress() { throw new UnsupportedOperationException(); } public boolean getOOBInline() { throw new UnsupportedOperationException(); } public OutputStream getOutputStream() { throw new UnsupportedOperationException(); } public int getPort() { throw new UnsupportedOperationException(); } public synchronized int getReceiveBufferSize() { throw new UnsupportedOperationException(); } public SocketAddress getRemoteSocketAddress() { throw new UnsupportedOperationException(); } public boolean getReuseAddress() { throw new UnsupportedOperationException(); } public synchronized int getSendBufferSize() { throw new UnsupportedOperationException(); } public int getSoLinger() { throw new UnsupportedOperationException(); } public synchronized int getSoTimeout() { throw new UnsupportedOperationException(); } public boolean getTcpNoDelay() { throw new UnsupportedOperationException(); } public int getTrafficClass() { throw new UnsupportedOperationException(); } public boolean isBound() { throw new UnsupportedOperationException(); } public boolean isClosed() { throw new UnsupportedOperationException(); } public boolean isConnected() { throw new UnsupportedOperationException(); } public boolean isInputShutdown() { throw new UnsupportedOperationException(); } public boolean isOutputShutdown() { throw new UnsupportedOperationException(); } public void sendUrgentData(int data) { throw new UnsupportedOperationException(); } public void setKeepAlive(boolean on) { throw new UnsupportedOperationException(); } public void setOOBInline(boolean on) { throw new UnsupportedOperationException(); } public synchronized void setReceiveBufferSize(int size) { throw new UnsupportedOperationException(); } public void setReuseAddress(boolean on) { throw new UnsupportedOperationException(); } public synchronized void setSendBufferSize(int size) { throw new UnsupportedOperationException(); } public void setSoLinger(boolean on, int linger) { throw new UnsupportedOperationException(); } public synchronized void setSoTimeout(int timeout) { throw new UnsupportedOperationException(); } public void setTcpNoDelay(boolean on) { throw new UnsupportedOperationException(); } public void setTrafficClass(int tc) { throw new UnsupportedOperationException(); } public void shutdownInput() { throw new UnsupportedOperationException(); } public void shutdownOutput() { throw new UnsupportedOperationException(); } }; } public Socket createSocket(String host, int port) throws IOException { return createSocket(new InetSocketAddress(host, port), CONNECT_TIMEOUT); } public Socket createSocket(InetAddress inetAddress, int port) throws IOException { return createSocket(new InetSocketAddress(inetAddress, port), CONNECT_TIMEOUT); } public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException { throw new IOException("Unsupported operation"); } public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException { throw new IOException("Unsupported operation"); } /** * Connects to the remote machine by establishing a tunnel through a HTTP proxy. It issues a * CONNECT request and eventually authenticates with the HTTP proxy. Supported authentication * methods include: Basic. * * @param address remote machine to connect to * @return a TCP/IP socket connected to the remote machine * @throws UnknownHostException if the proxy host name cannot be resolved * @throws IOException if an I/O error occurs during handshake (a network problem) */ private Socket getHttpsTunnelSocket( InetSocketAddress address, ConnectivitySettings cs, int timeout) throws IOException { Socket proxy = new Socket(); proxy.connect(new InetSocketAddress(cs.getProxyHost(), cs.getProxyPort()), timeout); BufferedReader r = new BufferedReader( new InputStreamReader(new InterruptibleInputStream(proxy.getInputStream()))); DataOutputStream dos = new DataOutputStream(proxy.getOutputStream()); dos.writeBytes("CONNECT "); dos.writeBytes(address.getHostName() + ":" + address.getPort()); dos.writeBytes(" HTTP/1.0\r\n"); dos.writeBytes("Connection: Keep-Alive\r\n\r\n"); dos.flush(); String line; line = r.readLine(); if (sConnectionEstablishedPattern.matcher(line).find()) { for (; ; ) { line = r.readLine(); if (line.length() == 0) break; } return proxy; } else if (sProxyAuthRequiredPattern.matcher(line).find()) { boolean authMethodSelected = false; String authMethod = AUTH_NONE; for (; ; ) { line = r.readLine(); if (line.length() == 0) break; if (line.startsWith("Proxy-Authenticate:") && !authMethodSelected) { authMethod = line.substring(19).trim(); if (authMethod.equals(AUTH_BASIC)) { authMethodSelected = true; } } } // TODO: need to read full response before closing connection? proxy.close(); if (authMethod.startsWith(AUTH_BASIC)) { return authenticateBasic(address, cs); } else { throw new IOException("Unsupported authentication method: " + authMethod); } } else { proxy.close(); throw new IOException("HTTP proxy does not support CONNECT command. Received reply: " + line); } } /** * Connects to the remote machine by establishing a tunnel through a HTTP proxy with Basic * authentication. It issues a CONNECT request and authenticates with the HTTP proxy with Basic * protocol. * * @param address remote machine to connect to * @return a TCP/IP socket connected to the remote machine * @throws IOException if an I/O error occurs during handshake (a network problem) */ private Socket authenticateBasic(InetSocketAddress address, ConnectivitySettings cs) throws IOException { Socket proxy = new Socket(cs.getProxyHost(), cs.getProxyPort()); BufferedReader r = new BufferedReader( new InputStreamReader(new InterruptibleInputStream(proxy.getInputStream()))); DataOutputStream dos = new DataOutputStream(proxy.getOutputStream()); String username = cs.getProxyUsername() == null ? "" : cs.getProxyUsername(); String password = cs.getProxyPassword() == null ? "" : String.valueOf(cs.getProxyPassword()); String credentials = username + ":" + password; String basicCookie = Base64Encoder.encode(credentials.getBytes("US-ASCII")); dos.writeBytes("CONNECT "); dos.writeBytes(address.getHostName() + ":" + address.getPort()); dos.writeBytes(" HTTP/1.0\r\n"); dos.writeBytes("Connection: Keep-Alive\r\n"); dos.writeBytes("Proxy-Authorization: Basic " + basicCookie + "\r\n"); dos.writeBytes("\r\n"); dos.flush(); String line = r.readLine(); if (sConnectionEstablishedPattern.matcher(line).find()) { for (; ; ) { line = r.readLine(); if (line.length() == 0) break; } return proxy; } throw new IOException("Basic authentication failed: " + line); } /** * Creates a new Socket connected to the given IP address. The method uses connection settings * supplied in the constructor for connecting the socket. * * @param address the IP address to connect to * @return connected socket * @throws java.net.UnknownHostException if the hostname of the address or the proxy cannot be * resolved * @throws java.io.IOException if an I/O error occured while connecting to the remote end or to * the proxy */ private Socket createSocket(InetSocketAddress address, int timeout) throws IOException { String socksProxyHost = System.getProperty("socksProxyHost"); System.getProperties().remove("socksProxyHost"); try { ConnectivitySettings cs = lastKnownSettings.get(address); if (cs != null) { try { return createSocket(cs, address, timeout); } catch (IOException e) { // not good anymore, try all proxies lastKnownSettings.remove(address); } } URI uri = addressToURI(address, "socket"); try { return createSocket(uri, address, timeout); } catch (IOException e) { // we will also try https } uri = addressToURI(address, "https"); return createSocket(uri, address, timeout); } finally { if (socksProxyHost != null) { System.setProperty("socksProxyHost", socksProxyHost); } } } private URI addressToURI(InetSocketAddress address, String schema) { URI uri; try { if (address.isUnresolved()) { uri = new URI(schema + "://" + address.getHostName() + ":" + address.getPort()); } else { uri = new URI( schema + "://" + address.getAddress().getHostAddress() + ":" + address.getPort()); } } catch (URISyntaxException e) { throw new RuntimeException(e); } return uri; } private Socket createSocket(URI uri, InetSocketAddress address, int timeout) throws IOException { List<Proxy> proxies = ProxySelector.getDefault().select(uri); IOException lastFailure = null; for (Proxy proxy : proxies) { ConnectivitySettings cs = proxyToCs(proxy, uri); try { Socket s = createSocket(cs, address, timeout); lastKnownSettings.put(address, cs); return s; } catch (IOException e) { lastFailure = e; } } throw lastFailure; } private ConnectivitySettings proxyToCs(Proxy proxy, URI uri) { ConnectivitySettings cs = new ConnectivitySettings(); InetSocketAddress isa = (InetSocketAddress) proxy.address(); switch (proxy.type()) { case HTTP: setupProxy(cs, ConnectivitySettings.CONNECTION_VIA_HTTPS, isa); break; case SOCKS: setupProxy(cs, ConnectivitySettings.CONNECTION_VIA_SOCKS, isa); break; default: } String prosyUser = NetworkSettings.getAuthenticationUsername(uri); if (prosyUser != null && !prosyUser.isEmpty()) { cs.setProxyUsername(prosyUser); cs.setProxyPassword(Keyring.read(NetworkSettings.getKeyForAuthenticationPassword(uri))); } return cs; } private void setupProxy( ConnectivitySettings cs, int connectionType, InetSocketAddress inetSocketAddress) { cs.setConnectionType(connectionType); InetAddress address = inetSocketAddress.getAddress(); cs.setProxyHost((address != null) ? address.getHostAddress() : inetSocketAddress.getHostName()); cs.setProxyPort(inetSocketAddress.getPort()); } private Socket createSocket(ConnectivitySettings cs, InetSocketAddress address, int timeout) throws IOException { switch (cs.getConnectionType()) { case ConnectivitySettings.CONNECTION_VIA_SOCKS: case ConnectivitySettings.CONNECTION_DIRECT: Socket s = new Socket(); s.connect(address, timeout); return s; case ConnectivitySettings.CONNECTION_VIA_HTTPS: return getHttpsTunnelSocket(address, cs, timeout); default: throw new IllegalArgumentException("Illegal connection type: " + cs.getConnectionType()); } } }
public class DBPort { public static final int PORT = 27017; static final boolean USE_NAGLE = false; static final long CONN_RETRY_TIME_MS = 15000; public DBPort(InetSocketAddress addr) throws IOException { this(addr, null, new MongoOptions()); } DBPort(InetSocketAddress addr, DBPortPool pool, MongoOptions options) throws IOException { _options = options; _addr = addr; _pool = pool; _hashCode = _addr.hashCode(); _logger = Logger.getLogger(_rootLogger.getName() + "." + addr.toString()); } /** @param response will get wiped */ DBMessage call(DBMessage msg, ByteDecoder decoder) throws IOException { return go(msg, decoder); } void say(DBMessage msg) throws IOException { go(msg, null); } private synchronized DBMessage go(DBMessage msg, ByteDecoder decoder) throws IOException { if (_sock == null) _open(); { ByteBuffer out = msg.prepare(); while (out.remaining() > 0) _sock.write(out); } if (_pool != null) _pool._everWorked = true; if (decoder == null) return null; ByteBuffer response = decoder._buf; if (response.position() != 0) throw new IllegalArgumentException(); int read = 0; while (read < DBMessage.HEADER_LENGTH) read += _read(response); int len = response.getInt(0); if (len <= DBMessage.HEADER_LENGTH) throw new IllegalArgumentException("db sent invalid length: " + len); if (len > response.capacity()) throw new IllegalArgumentException( "db message size is too big (" + len + ") " + "max is (" + response.capacity() + ")"); response.limit(len); while (read < len) read += _read(response); if (read != len) throw new RuntimeException("something is wrong"); response.flip(); return new DBMessage(response); } public synchronized void ensureOpen() throws IOException { if (_sock != null) return; _open(); } void _open() throws IOException { long sleepTime = 100; final long start = System.currentTimeMillis(); while (true) { IOException lastError = null; try { _sock = SocketChannel.open(); _socket = _sock.socket(); _socket.connect(_addr, _options.connectTimeout); _socket.setTcpNoDelay(!USE_NAGLE); _socket.setSoTimeout(_options.socketTimeout); _in = _socket.getInputStream(); return; } catch (IOException ioe) { // TODO - erh to fix lastError = new IOException( "couldn't connect to [" + // _addr + "] bc:" + lastError , lastError ); lastError = new IOException("couldn't connect to [" + _addr + "] bc:" + ioe); _logger.log(Level.INFO, "connect fail to : " + _addr, ioe); } if (!_options.autoConnectRetry || (_pool != null && !_pool._everWorked)) throw lastError; long sleptSoFar = System.currentTimeMillis() - start; if (sleptSoFar >= CONN_RETRY_TIME_MS) throw lastError; if (sleepTime + sleptSoFar > CONN_RETRY_TIME_MS) sleepTime = CONN_RETRY_TIME_MS - sleptSoFar; _logger.severe( "going to sleep and retry. total sleep time after = " + (sleptSoFar + sleptSoFar) + "ms this time:" + sleepTime + "ms"); ThreadUtil.sleep(sleepTime); sleepTime *= 2; } } public int hashCode() { return _hashCode; } public String host() { return _addr.toString(); } public String toString() { return "{DBPort " + host() + "}"; } protected void finalize() { if (_sock != null) { try { _sock.close(); } catch (Exception e) { // don't care } _in = null; _socket = null; _sock = null; } } void checkAuth(DB db) { if (db._username == null) return; if (_authed.containsKey(db)) return; if (_inauth) return; _inauth = true; try { if (db.reauth()) { _authed.put(db, true); return; } } finally { _inauth = false; } throw new MongoInternalException("can't reauth!"); } private int _read(ByteBuffer buf) throws IOException { int x = _in.read(buf.array(), buf.position(), buf.remaining()); if (x < 0) throw new IOException("connection to server closed unexpectedly"); buf.position(buf.position() + x); return x; } final int _hashCode; final InetSocketAddress _addr; final DBPortPool _pool; final MongoOptions _options; final Logger _logger; private SocketChannel _sock; private Socket _socket; private InputStream _in; private boolean _inauth = false; private Map<DB, Boolean> _authed = Collections.synchronizedMap(new WeakHashMap<DB, Boolean>()); private static Logger _rootLogger = Logger.getLogger("com.mongodb.port"); }
MultichatServer() { clients = new HashMap(); // hashmap = key와 value를 가진다. Collections.synchronizedMap(clients); // synchronized }
/** * This class offers methods to execute database commands via the client/server architecture. * Commands are sent to the server instance over a socket connection: * * <ul> * <li>A socket instance is created by the constructor. * <li>The {@link #execute} method sends database commands to the server. All strings are encoded * as UTF8 and suffixed by a zero byte. * <li>If the command has been successfully executed, the result string is read. * <li>Next, the command info string is read. * <li>A last byte is next sent to indicate if command execution was successful (0) or not (1). * <li>{@link #close} closes the session by sending the {@link Cmd#EXIT} command to the server. * </ul> * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public class ClientSession extends Session { /** Event notifications. */ protected final Map<String, EventNotifier> notifiers = Collections.synchronizedMap(new HashMap<String, EventNotifier>()); /** Server output (buffered). */ protected final PrintOutput sout; /** Server input. */ protected final InputStream sin; /** Socket reference. */ private final Socket socket; /** Socket host name. */ private final String ehost; /** Socket event reference. */ private Socket esocket; /** * Constructor, specifying login data. * * @param context database context * @param user user name * @param pass password * @throws IOException I/O exception */ public ClientSession(final Context context, final String user, final String pass) throws IOException { this(context, user, pass, null); } /** * Constructor, specifying login data and an output stream. * * @param context database context * @param user user name * @param pass password * @param output client output; if set to {@code null}, results will be returned as strings. * @throws IOException I/O exception */ public ClientSession( final Context context, final String user, final String pass, final OutputStream output) throws IOException { this(context.mprop.get(MainProp.HOST), context.mprop.num(MainProp.PORT), user, pass, output); } /** * Constructor, specifying the server host:port combination and login data. * * @param host server name * @param port server port * @param user user name * @param pass password * @throws IOException I/O exception */ public ClientSession(final String host, final int port, final String user, final String pass) throws IOException { this(host, port, user, pass, null); } /** * Constructor, specifying the server host:port combination, login data and an output stream. * * @param host server name * @param port server port * @param user user name * @param pass password * @param output client output; if set to {@code null}, results will be returned as strings. * @throws IOException I/O exception */ public ClientSession( final String host, final int port, final String user, final String pass, final OutputStream output) throws IOException { super(output); ehost = host; socket = new Socket(); try { // limit timeout to five seconds socket.connect(new InetSocketAddress(host, port), 5000); } catch (final IllegalArgumentException ex) { throw new BaseXException(ex); } sin = socket.getInputStream(); // receive timestamp final BufferInput bi = new BufferInput(sin); final String ts = bi.readString(); // send user name and hashed password/timestamp sout = PrintOutput.get(socket.getOutputStream()); send(user); send(Token.md5(Token.md5(pass) + ts)); sout.flush(); // receive success flag if (!ok(bi)) throw new LoginException(); } @Override public void create(final String name, final InputStream input) throws IOException { send(ServerCmd.CREATE, input, name); } @Override public void add(final String path, final InputStream input) throws IOException { send(ServerCmd.ADD, input, path); } @Override public void replace(final String path, final InputStream input) throws IOException { send(ServerCmd.REPLACE, input, path); } @Override public void store(final String path, final InputStream input) throws IOException { send(ServerCmd.STORE, input, path); } @Override public ClientQuery query(final String query) throws IOException { return new ClientQuery(query, this, out); } @Override public synchronized void close() throws IOException { if (esocket != null) esocket.close(); socket.close(); } @Override protected void execute(final String cmd, final OutputStream os) throws IOException { send(cmd); sout.flush(); receive(os); } @Override protected void execute(final Command cmd, final OutputStream os) throws IOException { execute(cmd.toString(), os); } /** * Watches an event. * * @param name event name * @param notifier event notification * @throws IOException I/O exception */ public void watch(final String name, final EventNotifier notifier) throws IOException { sout.write(ServerCmd.WATCH.code); if (esocket == null) { sout.flush(); final BufferInput bi = new BufferInput(sin); final int eport = Integer.parseInt(bi.readString()); // initialize event socket esocket = new Socket(); esocket.connect(new InetSocketAddress(ehost, eport), 5000); final OutputStream so = esocket.getOutputStream(); so.write(bi.readBytes()); so.write(0); so.flush(); final InputStream is = esocket.getInputStream(); is.read(); listen(is); } send(name); sout.flush(); receive(null); notifiers.put(name, notifier); } /** * Unwatches an event. * * @param name event name * @throws IOException I/O exception */ public void unwatch(final String name) throws IOException { sout.write(ServerCmd.UNWATCH.code); send(name); sout.flush(); receive(null); notifiers.remove(name); } /** * Starts the listener thread. * * @param in input stream */ private void listen(final InputStream in) { final BufferInput bi = new BufferInput(in); new Thread() { @Override public void run() { try { while (true) { final EventNotifier n = notifiers.get(bi.readString()); final String l = bi.readString(); if (n != null) n.notify(l); } } catch (final IOException ex) { // listener did not receive any more input } } }.start(); } /** * Sends the specified stream to the server. * * @param input input stream * @throws IOException I/O exception */ private void send(final InputStream input) throws IOException { final EncodingOutput eo = new EncodingOutput(sout); for (int b; (b = input.read()) != -1; ) eo.write(b); sout.write(0); sout.flush(); receive(null); } /** * Receives the info string. * * @param os output stream to send result to. If {@code null}, no result will be requested * @throws IOException I/O exception */ private void receive(final OutputStream os) throws IOException { final BufferInput bi = new BufferInput(sin); if (os != null) receive(bi, os); info = bi.readString(); if (!ok(bi)) throw new BaseXException(info); } /** * Checks the next success flag. * * @param bi buffer input * @return value of check * @throws IOException I/O exception */ protected static boolean ok(final BufferInput bi) throws IOException { return bi.read() == 0; } /** * Sends the specified command, string arguments and input. * * @param cmd command * @param input input stream * @param strings string arguments * @throws IOException I/O exception */ protected void send(final ServerCmd cmd, final InputStream input, final String... strings) throws IOException { sout.write(cmd.code); for (final String s : strings) send(s); send(input); } /** * Retrieves data from the server. * * @param bi buffered server input * @param os output stream * @throws IOException I/O exception */ protected static void receive(final BufferInput bi, final OutputStream os) throws IOException { final DecodingInput di = new DecodingInput(bi); for (int b; (b = di.read()) != -1; ) os.write(b); } /** * Sends a string to the server. * * @param s string to be sent * @throws IOException I/O exception */ protected void send(final String s) throws IOException { sout.write(Token.token(s)); sout.write(0); } /** * Executes a command and sends the result to the specified output stream. * * @param cmd server command * @param arg argument * @param os target output stream * @return string * @throws IOException I/O exception */ protected String exec(final ServerCmd cmd, final String arg, final OutputStream os) throws IOException { final OutputStream o = os == null ? new ArrayOutput() : os; sout.write(cmd.code); send(arg); sout.flush(); final BufferInput bi = new BufferInput(sin); ClientSession.receive(bi, o); if (!ClientSession.ok(bi)) throw new BaseXException(bi.readString()); return o.toString(); } @Override public String toString() { return ehost + ':' + socket.getPort(); } }
/** * This is the container for an instance of a site on a single server. This can be access via * __instance__ * * @anonymous name : {local}, isField : {true}, desc : {Refers to the site being run.}, type: * {library} * @anonymous name : {core}, isField : {true}, desc : {Refers to corejs.} example : * {core.core.mail() calls corejs/core/mail.js}, type : {library} * @anonymous name : {external} isField : {true}, desc : {Refers to the external libraries.}, type : * {library} * @anonymous name : {db}, isField : {true}, desc : {Refers to the database.}, type : {database} * @anonymous name : {setDB} desc : {changes <tt>db</tt> to refer to a different database.} param : * {type : (string) name : (dbname) desc : (name of the database to which to connect)} * @anonymous name : {SYSOUT} desc : {Prints a string.} param : {type : (string) name : (str) desc : * (the string to print)} * @anonymous name : {log} desc : {Global logger.} param : {type : (string) name : (str) desc : (the * string to log)} * @expose * @docmodule system.system.__instance__ */ public class AppContext extends ServletContextBase implements JSObject, Sizable { /** @unexpose */ static final boolean DEBUG = AppServer.D; /** * If these files exist in the directory or parent directories of a file being run, run these * files first. Includes _init.js and /~~/core/init.js. */ static final String INIT_FILES[] = new String[] {"/~~/core/init.js", "PREFIX_init"}; /** * Initializes a new context for a given site directory. * * @param f the file to run */ public AppContext(File f) { this(f.toString()); } /** * Initializes a new context for a given site's path. * * @param root the path to the site from where ed is being run */ public AppContext(String root) { this(root, guessNameAndEnv(root).name, guessNameAndEnv(root).env); } /** * Initializes a new context. * * @param root the path to the site * @param name the name of the site * @param environment the version of the site */ public AppContext(String root, String name, String environment) { this(root, new File(root), name, environment); } /** * Initializes a new context. * * @param root the path to the site * @param rootFile the directory in which the site resides * @param name the name of the site * @param environment the version of the site */ public AppContext(String root, File rootFile, String name, String environment) { this(root, rootFile, name, environment, null); } private AppContext( String root, File rootFile, String name, String environment, AppContext nonAdminParent) { super(name + ":" + environment); if (root == null) throw new NullPointerException("AppContext root can't be null"); if (rootFile == null) throw new NullPointerException("AppContext rootFile can't be null"); if (name == null) name = guessNameAndEnv(root).name; if (name == null) throw new NullPointerException("how could name be null"); _root = root; _rootFile = rootFile; _git = new GitDir(_rootFile); _name = name; _environment = environment; _nonAdminParent = nonAdminParent; _admin = _nonAdminParent != null; _codePrefix = _admin ? "/~~/modules/admin/" : ""; _moduleRegistry = ModuleRegistry.getNewGlobalChild(); if (_git.isValid()) { _gitBranch = _git.getBranchOrTagName(); _gitHash = _git.getCurrentHash(); } _isGrid = name.equals("grid"); _scope = new Scope( "AppContext:" + root + (_admin ? ":admin" : ""), _isGrid ? ed.cloud.Cloud.getInstance().getScope() : Scope.newGlobal(), null, Language.JS(), _rootFile); _scope.setGlobal(true); _initScope = _scope.child("_init"); _usage = new UsageTracker(this); _baseScopeInit(); _adminContext = _admin ? null : new AppContext(root, rootFile, name, environment, this); _rootContextReachable = new SeenPath(); if (!_admin) _logger.info( "Started Context. root:" + _root + " environment:" + environment + " git branch: " + _gitBranch); } /** * Returns the adapter type for the given file. Will first use the adapter selector function if it * was specified in init.js, otherwise will use the static type (either set in _init file, as a * server-wide override in 10gen.properties, or default of DIRECT_10GEN) * * @param file to produce type for * @return adapter type for the specified file */ public AdapterType getAdapterType(File file) { // Q : I think this is the right thing to do if (inScopeSetup()) { return AdapterType.DIRECT_10GEN; } /* * cheap hack - prevent any _init.* file from getting run as anythign but DIRECT_10GEN */ if (file != null && file.getName().indexOf("_init.") != -1) { return AdapterType.DIRECT_10GEN; } if (_adapterSelector == null) { return _staticAdapterType; } /* * only let the app select type if file is part of application (i.e. * don't do it for corejs, core modules, etc... */ String fp = file.getAbsolutePath(); String fullRoot = _rootFile.getAbsolutePath(); // there must be a nicer way to do this? if (!fp.startsWith(fullRoot)) { return AdapterType.DIRECT_10GEN; } Object o = _adapterSelector.call(_initScope, new JSString(fp.substring(fullRoot.length()))); if (o == null) { return _staticAdapterType; } if (!(o instanceof JSString)) { log("Error : adapter selector not returning string. Ignoring and using static adapter type"); return _staticAdapterType; } AdapterType t = getAdapterTypeFromString(o.toString()); return (t == null ? _staticAdapterType : t); } /** * Creates a copy of this context. * * @return an identical context */ AppContext newCopy() { return new AppContext(_root, _rootFile, _name, _environment, _nonAdminParent); } /** Initializes the base scope for the application */ private void _baseScopeInit() { // --- libraries if (_admin) _scope.put("local", new JSObjectBase(), true); else _setLocalObject(new JSFileLibrary(_rootFile, "local", this)); _loadConfig(); _core = CoreJS.get().getLibrary(getCoreJSVersion(), this, null, true); _logger.info("corejs : " + _core.getRoot()); _scope.put("core", _core, true); _external = Module.getModule("external").getLibrary(getVersionForLibrary("external"), this, null, true); _scope.put("external", _external, true); _scope.put("__instance__", this, true); _scope.lock("__instance__"); // --- db if (!_isGrid) { _scope.put("db", DBProvider.get(this), true); _scope.put( "setDB", new JSFunctionCalls1() { public Object call(Scope s, Object name, Object extra[]) { if (name.equals(_lastSetTo)) return true; DBBase db = (DBBase) AppContext.this._scope.get("db"); if (!db.allowedToAccess(name.toString())) throw new JSException("you are not allowed to access db [" + name + "]"); if (name.equals(db.getName())) return true; AppContext.this._scope.put( "db", DBProvider.get(AppContext.this, name.toString()), false); _lastSetTo = name.toString(); if (_adminContext != null) { // yes, i do want a new copy so Constructors don't get copied for both _adminContext._scope.put( "db", DBProvider.get(AppContext.this, name.toString()), false); } return true; } String _lastSetTo = null; }, true); } // --- output _scope.put( "SYSOUT", new JSFunctionCalls1() { public Object call(Scope s, Object str, Object foo[]) { System.out.println(AppContext.this._name + " \t " + str); return true; } }, true); _scope.put("log", _logger, true); // --- random? _scope.put( "openFile", new JSFunctionCalls1() { public Object call(Scope s, Object name, Object extra[]) { return new JSLocalFile(_rootFile, name.toString()); } }, true); _scope.put("globalHead", _globalHead, true); Map<String, JSFileLibrary> rootFileMap = new HashMap<String, JSFileLibrary>(); for (String rootKey : new String[] {"local", "core", "external"}) { Object temp = _scope.get(rootKey); if (temp instanceof JSFileLibrary) rootFileMap.put(rootKey, (JSFileLibrary) temp); } _scope.put( "fork", new JSFunctionCalls1() { public Object call(final Scope scope, final Object funcJS, final Object extra[]) { if (!(funcJS instanceof JSFunction)) throw new JSException("fork has to take a function"); return queueWork("forked", (JSFunction) funcJS, extra); } }); _scope.lock("fork"); ed.appserver.templates.djang10.JSHelper.install(_scope, rootFileMap, _logger); _scope.lock("user"); // protection against global user object } private void _loadConfig() { try { _configScope.set("__instance__", this); _loadConfigFromCloudObject(getSiteObject()); _loadConfigFromCloudObject(getEnvironmentObject()); File f; if (!_admin) { f = getFileSafe("_config.js"); if (f == null || !f.exists()) f = getFileSafe("_config"); } else f = new File( Module.getModule("core-modules/admin").getRootFile(getVersionForLibrary("admin")), "_config.js"); _libraryLogger.info("config file [" + f + "] exists:" + f.exists()); if (f == null || !f.exists()) return; Convert c = new Convert(f); JSFunction func = c.get(); func.setUsePassedInScope(true); func.call(_configScope); _logger.debug("config things " + _configScope.keySet()); } catch (Exception e) { throw new RuntimeException("couldn't load config", e); } } private void _loadConfigFromCloudObject(JSObject o) { if (o == null) return; _configScope.putAll((JSObject) o.get("config")); } /** * Get the version of corejs to run for this AppContext. * * @return the version of corejs as a string. null if should use default */ public String getCoreJSVersion() { Object o = _scope.get("corejsversion"); if (o != null) { _logger.error("you are using corejsversion which is deprecated. please use version.corejs"); return JS.toString(o); } return getVersionForLibrary("corejs"); } /** * Get the version of a library to run. * * @param name the name of the library to look up * @return the version of the library to run as a string. null if should use default */ public String getVersionForLibrary(String name) { String version = getVersionForLibrary(_configScope, name, this); _libraryVersions.set(name, version); return version; } public JSObject getLibraryVersionsLoaded() { return _libraryVersions; } /** @unexpose */ public static String getVersionForLibrary(Scope s, String name) { AppRequest ar = AppRequest.getThreadLocal(); return getVersionForLibrary(s, name, ar == null ? null : ar.getContext()); } /** @unexpose */ private static String getVersionForLibrary(Scope s, String name, AppContext ctxt) { final String version = _getVersionForLibrary(s, name, ctxt); _libraryLogger.log( ctxt != null && !ctxt._admin ? Level.DEBUG : Level.INFO, ctxt + "\t" + name + "\t" + version); return version; } private static String _getVersionForLibrary(Scope s, String name, AppContext ctxt) { final JSObject o1 = ctxt == null ? null : (JSObject) (s.get("version_" + ctxt.getEnvironmentName())); final JSObject o2 = (JSObject) s.get("version"); _libraryLogger.debug(ctxt + "\t versionConfig:" + (o1 != null) + " config:" + (o2 != null)); String version = _getString(name, o1, o2); if (version != null) return version; if (ctxt == null || ctxt._nonAdminParent == null) return null; return ctxt._nonAdminParent.getVersionForLibrary(name); } private static String _getString(String name, JSObject... places) { for (JSObject o : places) { if (o == null) continue; Object temp = o.get(name); if (temp == null) continue; return temp.toString(); } return null; } /** @return [ <name> , <env> ] */ static NameAndEnv guessNameAndEnv(String root) { root = ed.io.FileUtil.clean(root); root = root.replaceAll("\\.+/", ""); String pcs[] = root.split("/+"); if (pcs.length == 0) throw new RuntimeException("no root for : " + root); // handle anything with sites/foo for (int i = 0; i < pcs.length - 1; i++) if (pcs[i].equals("sites")) { return new NameAndEnv(pcs[i + 1], i + 2 < pcs.length ? pcs[i + 2] : null); } final int start = pcs.length - 1; for (int i = start; i > 0; i--) { String s = pcs[i]; if (i == start && (s.equals("master") || s.equals("test") || s.equals("www") || s.equals("staging") || // s.equals("stage") || s.equals("dev"))) continue; return new NameAndEnv(s, i + 1 < pcs.length ? pcs[i + 1] : null); } return new NameAndEnv(pcs[0], pcs.length > 1 ? pcs[1] : null); } static class NameAndEnv { NameAndEnv(String name, String env) { this.name = name; this.env = env; } final String name; final String env; } /** * Returns the name of the site being run. * * @return the name of the site */ public String getName() { return _name; } /** * Get the database being used. * * @return The database being used */ public DBBase getDB() { return (DBBase) _scope.get("db"); } /** * Given the _id of a JSFile, return the file. * * @param id _id of the file to find * @return The file, if found, otherwise null */ JSFile getJSFile(String id) { if (id == null) return null; DBCollection f = getDB().getCollection("_files"); return (JSFile) (f.find(new ObjectId(id))); } /** * Returns (and if necessary, reinitializes) the scope this context is using. * * @return the scope */ public Scope getScope() { return _scope(); } public Scope getInitScope() { return _initScope; } public Object getInitObject(String what) { return getFromInitScope(what); } public Object getFromInitScope(String what) { if (!_knownInitScopeThings.contains(what)) System.err.println("*** Unknown thing requested from initScope [" + what + "]"); return _initScope.get(what); } public void setInitObject(String name, Object value) { _initScope.set(name, value); } void setTLPreferredScope(AppRequest req, Scope s) { _scope.setTLPreferred(s); } private synchronized Scope _scope() { if (_inScopeSetup) return _scope; if (_getScopeTime() > _lastScopeInitTime) _scopeInited = false; if (_scopeInited) return _scope; _scopeInited = true; _lastScopeInitTime = System.currentTimeMillis(); _setupScope(); _setStaticAdapterType(); _setAdapterSelectorFunction(); return _scope; } protected void _setAdapterSelectorFunction() { Object o = this.getFromInitScope(INIT_ADAPTER_SELECTOR); if (o == null) { log("Adapter selector function not specified in _init file"); return; } if (!(o instanceof JSFunction)) { log( "Adapter selector function specified in _init file not a function. Ignoring. [" + o.getClass() + "]"); return; } _adapterSelector = (JSFunction) o; log("Adapter selector function specified in _init file"); } public void setStaticAdapterTypeValue(AdapterType type) { log("Static adapter type directly set : " + type); _staticAdapterType = type; } public AdapterType getStaticAdapterTypeValue() { return _staticAdapterType; } /** * Figure out what kind of static adapter type was specified. By default it's a 10genDEFAULT app */ protected void _setStaticAdapterType() { /* * app configuration steps could have set this already. If so, don't bother doing anything */ if (_staticAdapterType != AdapterType.UNSET) { log("Static adapter type has already been directly set to " + _staticAdapterType); return; } /* * check to see if overridden in 10gen.properties */ String override = Config.get().getProperty(INIT_ADAPTER_TYPE); if (override != null) { AdapterType t = getAdapterTypeFromString(override); if (t == null) { log( "Static adapter type specified as override [" + override + "] unknown - will use _init file specified or default"); } else { log("Static adapter type overridden by 10gen.properties or env. Value : " + override); _staticAdapterType = t; return; } } /* * if not, use the one from _init file if specified */ _staticAdapterType = AdapterType.DIRECT_10GEN; Object o = getFromInitScope(INIT_ADAPTER_TYPE); if (o == null) { log("Static adapter type not specified in _init file - using default value of DIRECT_10GEN"); return; } if (!(o instanceof JSString)) { log("Static adapter type from _init file not a string - using default value of DIRECT_10GEN"); return; } _staticAdapterType = getAdapterTypeFromString(o.toString()); if (_staticAdapterType == null) { log( "Static adapter type from _init file [" + o.toString() + "] unknown - using default value of DIRECT_10GEN"); _staticAdapterType = AdapterType.DIRECT_10GEN; return; } log("Static adapter type specified in _init file = " + _staticAdapterType); return; } public AdapterType getAdapterTypeFromString(String s) { if (AdapterType.DIRECT_10GEN.toString().equals(s.toUpperCase())) { return AdapterType.DIRECT_10GEN; } if (AdapterType.CGI.toString().equals(s.toUpperCase())) { return AdapterType.CGI; } if (AdapterType.WSGI.toString().equals(s.toUpperCase())) { return AdapterType.WSGI; } return null; } /** @unexpose */ public File getFileSafe(final String uri) { try { return getFile(uri); } catch (FileNotFoundException fnf) { return null; } } /** @unexpose */ public File getFile(final String uri) throws FileNotFoundException { File f = _files.get(uri); if (f != null) return f; if (uri.startsWith("/~~/") || uri.startsWith("~~/")) f = _core.getFileFromPath(uri.substring(3)); else if (uri.startsWith("/admin/")) f = _core.getFileFromPath("/modules" + uri); else if (uri.startsWith("/@@/") || uri.startsWith("@@/")) f = _external.getFileFromPath(uri.substring(3)); else if (_localObject != null && uri.startsWith("/modules/")) f = _localObject.getFileFromPath(uri); else f = new File(_rootFile, uri); if (f == null) throw new FileNotFoundException(uri); _files.put(uri, f); return f; } public String getRealPath(String path) { try { return getFile(path).getAbsolutePath(); } catch (FileNotFoundException fnf) { throw new RuntimeException("file not found [" + path + "]"); } } public URL getResource(String path) { try { File f = getFile(path); if (!f.exists()) return null; return f.toURL(); } catch (FileNotFoundException fnf) { // the spec says to return null if we can't find it // even though this is weird... return null; } catch (IOException ioe) { throw new RuntimeException("error opening [" + path + "]", ioe); } } public InputStream getResourceAsStream(String path) { URL url = getResource(path); if (url == null) return null; try { return url.openStream(); } catch (IOException ioe) { throw new RuntimeException("can't getResourceAsStream [" + path + "]", ioe); } } /** * This causes the AppContext to be started over. All context level variable will be lost. If code * is being managed, will cause it to check that its up to date. */ public void reset() { _reset = true; } /** Checks if this context has been reset. */ public boolean isReset() { return _reset; } /** * Returns the path to the directory the appserver is running. (For example, site/version.) * * @return the path */ public String getRoot() { return _root; } public File getRootFile() { return _rootFile; } /** * Creates an new request for the app server from an HTTP request. * * @param request HTTP request to create * @return the request */ public AppRequest createRequest(HttpRequest request) { return createRequest(request, request.getHost(), request.getURI()); } /** * Creates an new request for the app server from an HTTP request. * * @param request HTTP request to create * @param uri the URI requested * @return the request */ public AppRequest createRequest(HttpRequest request, String host, String uri) { _numRequests++; if (AppRequest.isAdmin(request)) return new AppRequest(_adminContext, request, host, uri); return new AppRequest(this, request, host, uri); } /** * Tries to find the given file, assuming that it's missing the ".jxp" extension * * @param f File to check * @return same file if not found to be missing the .jxp, or a new File w/ the .jxp appended */ File tryNoJXP(File f) { if (f.exists()) return f; if (f.getName().indexOf(".") >= 0) return f; File temp = new File(f.toString() + ".jxp"); return temp.exists() ? temp : f; } File tryOtherExtensions(File f) { if (f.exists()) return f; if (f.getName().indexOf(".") >= 0) return f; for (int i = 0; i < JSFileLibrary._srcExtensions.length; i++) { File temp = new File(f.toString() + JSFileLibrary._srcExtensions[i]); if (temp.exists()) return temp; } return f; } /** * Maps a servlet-like URI to a jxp file. * * @param f File to check * @return new File with <root>.jxp if exists, orig file if not * @example /wiki/geir -> maps to wiki.jxp if exists */ File tryServlet(File f) { if (f.exists()) return f; String uri = f.toString(); if (uri.startsWith(_rootFile.toString())) uri = uri.substring(_rootFile.toString().length()); if (_core != null && uri.startsWith(_core._base.toString())) uri = "/~~" + uri.substring(_core._base.toString().length()); while (uri.startsWith("/")) uri = uri.substring(1); int start = 0; while (true) { int idx = uri.indexOf("/", start); if (idx < 0) break; String foo = uri.substring(0, idx); File temp = getFileSafe(foo + ".jxp"); if (temp != null && temp.exists()) f = temp; start = idx + 1; } return f; } /** * Returns the index.jxp for the File argument if it's an existing directory, and the index.jxp * file exists * * @param f directory to check * @return new File for index.jxp in that directory, or same file object if not */ File tryIndex(File f) { if (!(f.isDirectory() && f.exists())) return f; for (int i = 0; i < JSFileLibrary._srcExtensions.length; i++) { File temp = new File(f, "index" + JSFileLibrary._srcExtensions[i]); if (temp.exists()) return temp; } return f; } JxpSource getSource(File f) throws IOException { if (DEBUG) System.err.println("getSource\n\t " + f); File temp = _findFile(f); if (DEBUG) System.err.println("\t " + temp); if (!temp.exists()) return handleFileNotFound(f); // if it's a directory (and we know we can't find the index file) // TODO : at some point, do something where we return an index for the dir? if (temp.isDirectory()) return null; // if we are at init time, save it as an initializaiton file loadedFile(temp); // Ensure that this is w/in the right tree for the context if (_localObject != null && _localObject.isIn(temp)) return _localObject.getSource(temp); // if not, is it core? if (_core.isIn(temp)) return _core.getSource(temp); throw new RuntimeException("what? can't find:" + f); } /** * Finds the appropriate file for the given path. * * <p>We have a hierarchy of attempts as we try to find a file : * * <p>1) first, see if it exists as is, or if it's really a .jxp w/o the extension 2) next, see if * it can be deconstructed as a servlet such that /foo/bar maps to /foo.jxp 3) See if we can find * the index file for it if a directory */ File _findFile(File f) { File temp; if ((temp = tryNoJXP(f)) != f) { return temp; } if ((temp = tryOtherExtensions(f)) != f) { return temp; } if ((temp = tryServlet(f)) != f) { return temp; } if ((temp = tryIndex(f)) != f) { return temp; } return f; } public void loadedFile(File f) { if (_inScopeSetup) _initFlies.add(f); } public void addInitDependency(File f) { _initFlies.add(f); } JxpServlet getServlet(File f) throws IOException { // if this site doesn't exist, don't return anything if (!_rootFile.exists()) return null; JxpSource source = getSource(f); if (source == null) return null; return source.getServlet(this); } private void _setupScope() { if (_inScopeSetup) return; final Scope saveTLPref = _scope.getTLPreferred(); _scope.setTLPreferred(null); final Scope saveTL = Scope.getThreadLocal(); _scope.makeThreadLocal(); _inScopeSetup = true; try { Object fo = getConfigObject("framework"); if (fo != null) { Framework f = null; if (fo instanceof JSString) { f = Framework.byName(fo.toString(), null); // we allow people to just specify name _logger.info("Setting framework by name [" + fo.toString() + "]"); } else if (fo instanceof JSObjectBase) { JSObjectBase obj = (JSObjectBase) fo; if (obj.containsKey("name")) { String s = obj.getAsString("version"); f = Framework.byName(obj.getAsString("name"), s); _logger.info("Setting framework by name [" + obj.getAsString("name") + "]"); } else if (obj.containsKey("custom")) { Object o = obj.get("custom"); if (o instanceof JSObjectBase) { f = Framework.byCustom((JSObjectBase) o); _logger.info("Setting framework by custom [" + o + "]"); } else { throw new RuntimeException("Error - custom framework wasn't an object [" + o + "]"); } } else if (obj.containsKey("classname")) { f = Framework.byClass(obj.getAsString("classname")); _logger.info("Setting framework by class [" + obj.getAsString("classname") + "]"); } } if (f == null) { throw new RuntimeException("Error : can't find framework [" + fo + "]"); } f.install(this); } _runInitFiles(INIT_FILES); if (_adminContext != null) { _adminContext._scope.set("siteScope", _scope); _adminContext._setLocalObject(_localObject); } _lastScopeInitTime = _getScopeTime(); } catch (RuntimeException re) { _scopeInited = false; throw re; } catch (Exception e) { _scopeInited = false; throw new RuntimeException(e); } finally { _inScopeSetup = false; _scope.setTLPreferred(saveTLPref); if (saveTL != null) saveTL.makeThreadLocal(); this.approxSize(_rootContextReachable); } } public boolean inScopeSetup() { return _inScopeSetup; } private void _runInitFiles(String[] files) throws IOException { if (files == null) return; for (JSFunction func : _initRefreshHooks) { func.call(_initScope, null); } for (int i = 0; i < files.length; i++) runInitFile(files[i].replaceAll("PREFIX", _codePrefix)); } public void addInitRefreshHook(JSFunction func) { _initRefreshHooks.add(func); } /** @param path (ex: /~~/foo.js ) */ public void runInitFile(String path) throws IOException { _runInitFile(tryOtherExtensions(getFile(path))); } private void _runInitFile(File f) throws IOException { if (f == null) return; if (!f.exists()) return; _initFlies.add(f); JxpSource s = getSource(f); JSFunction func = s.getFunction(); func.setUsePassedInScope(true); func.call(_initScope); } long _getScopeTime() { long last = 0; for (File f : _initFlies) if (f.exists()) last = Math.max(last, f.lastModified()); return last; } /** * Convert this AppContext to a string by returning the name of the directory it's running in. * * @return the filename of its root directory */ public String toString() { return _rootFile.toString(); } public String debugInfo() { return _rootFile + " admin:" + _admin; } public void fix(Throwable t) { StackTraceHolder.getInstance().fix(t); } /** * Get a "global" head array. This array contains HTML that will be inserted into the head of * every request served by this app context. It's analagous to the <tt>head</tt> array, but * persistent. * * @return a mutable array */ public JSArray getGlobalHead() { return _globalHead; } /** * Gets the date of creation for this app context. * * @return the creation date as a JS Date. */ public JSDate getWhenCreated() { return _created; } /** * Gets the number of requests served by this app context. * * @return the number of requests served */ public int getNumRequests() { return _numRequests; } /** * Get the name of the git branch we think we're running. * * @return the name of the git branch, as a string */ public String getGitBranch() { return _gitBranch; } public String getGitHash() { return _gitHash; } /** * Update the git branch that we're running and return it. * * @return the name of the git branch, or null if there isn't any */ public String getCurrentGitBranch() { return getCurrentGitBranch(false); } public String getCurrentGitBranch(boolean forceUpdate) { if (_gitBranch == null) return null; if (_gitFile == null) _gitFile = new File(_rootFile, ".git/HEAD"); if (!_gitFile.exists()) throw new RuntimeException("this should be impossible"); if (forceUpdate || _lastScopeInitTime < _gitFile.lastModified()) { _gitBranch = _git.getBranchOrTagName(); _gitHash = _git.getCurrentHash(); } return _gitBranch; } /** * Get the environment in which this site is running * * @return the environment name as a string */ public String getEnvironmentName() { return _environment; } /** * updates the context to the correct branch based on environment and to the latest version of the * code if name or environemnt is missing, does nothing */ public String updateCode() { if (!_git.isValid()) throw new RuntimeException(_rootFile + " is not a git repository"); _logger.info("going to update code"); _git.fullUpdate(); if (_name == null || _environment == null) return getCurrentGitBranch(); JSObject env = getEnvironmentObject(); if (env == null) return null; String branch = env.get("branch").toString(); _logger.info("updating to [" + branch + "]"); _git.checkout(branch); Python.deleteCachedJythonFiles(_rootFile); return getCurrentGitBranch(true); } private JSObject getSiteObject() { return AppContextHolder.getSiteFromCloud(_name); } private JSObject getEnvironmentObject() { return AppContextHolder.getEnvironmentFromCloud(_name, _environment); } private void _setLocalObject(JSFileLibrary local) { _localObject = local; _scope.put("local", _localObject, true); _scope.put("jxp", _localObject, true); _scope.warn("jxp"); } JxpSource handleFileNotFound(File f) { String name = f.getName(); if (name.endsWith(".class")) { name = name.substring(0, name.length() - 6); return getJxpServlet(name); } return null; } public JxpSource getJxpServlet(String name) { JxpSource source = _httpServlets.get(name); if (source != null) return source; try { Class c = Class.forName(name); Object n = c.newInstance(); if (!(n instanceof HttpServlet)) throw new RuntimeException("class [" + name + "] is not a HttpServlet"); HttpServlet servlet = (HttpServlet) n; servlet.init(createServletConfig(name)); source = new ServletSource(servlet); _httpServlets.put(name, source); return source; } catch (Exception e) { throw new RuntimeException("can't load [" + name + "]", e); } } ServletConfig createServletConfig(final String name) { final Object rawServletConfigs = _scope.get("servletConfigs"); final Object servletConfigObject = rawServletConfigs instanceof JSObject ? ((JSObject) rawServletConfigs).get(name) : null; final JSObject servletConfig; if (servletConfigObject instanceof JSObject) servletConfig = (JSObject) servletConfigObject; else servletConfig = null; return new ServletConfig() { public String getInitParameter(String name) { if (servletConfig == null) return null; Object foo = servletConfig.get(name); if (foo == null) return null; return foo.toString(); } public Enumeration getInitParameterNames() { Collection keys; if (servletConfig == null) keys = new LinkedList(); else keys = servletConfig.keySet(); return new CollectionEnumeration(keys); } public ServletContext getServletContext() { return AppContext.this; } public String getServletName() { return name; } }; } public static AppContext findThreadLocal() { AppContext context = _tl.get(); if (context != null) return context; AppRequest req = AppRequest.getThreadLocal(); if (req != null) return req._context; Scope s = Scope.getThreadLocal(); if (s != null) { Object foo = s.get("__instance__"); if (foo instanceof AppContext) return (AppContext) foo; } return null; } public void makeThreadLocal() { _tl.set(this); } public static void clearThreadLocal() { _tl.set(null); } public String getInitParameter(String name) { Object foo = _configScope.get(name); if (foo == null) return null; return foo.toString(); } public Enumeration getInitParameterNames() { return new CollectionEnumeration(_configScope.keySet()); } public Object getConfigObject(String name) { return _configScope.get(name); } public void setConfigObject(String name, Object value) { _configScope.set(name, value); } public String getContextPath() { return ""; } public RequestDispatcher getNamedDispatcher(String name) { throw new RuntimeException("getNamedDispatcher not implemented"); } public RequestDispatcher getRequestDispatcher(String name) { throw new RuntimeException("getRequestDispatcher not implemented"); } public Set getResourcePaths(String path) { throw new RuntimeException("getResourcePaths not implemented"); } public AppContext getSiteInstance() { if (_nonAdminParent == null) return this; return _nonAdminParent; } public long approxSize() { return approxSize(new SeenPath()); } public long approxSize(SeenPath seen) { long size = 0; seen.visited(this); if (seen.shouldVisit(_scope, this)) size += _scope.approxSize(seen, false, true); if (seen.shouldVisit(_initScope, this)) size += _initScope.approxSize(seen, true, false); size += JSObjectSize.size(_localObject, seen, this); size += JSObjectSize.size(_core, seen, this); size += JSObjectSize.size(_external, seen, this); if (seen.shouldVisit(_adminContext, this)) size += _adminContext.approxSize(seen); size += JSObjectSize.size(_logger, seen, this); return size; } public int hashCode() { return System.identityHashCode(this); } public boolean equals(Object o) { return o == this; } public AppWork queueWork(String identifier, JSFunction work, Object... params) { return queueWork(new AppWork.FunctionAppWork(this, identifier, work, params)); } public AppWork queueWork(AppWork work) { if (_workQueue == null) { _workQueue = new ArrayBlockingQueue<AppWork>(100); AppWork.addQueue(_workQueue); } if (_workQueue.offer(work)) return work; throw new RuntimeException("work queue full!"); } public Logger getLogger(String sub) { return _logger.getChild(sub); } public ModuleRegistry getModuleRegistry() { return _moduleRegistry; } // ---- START JSObject INTERFACE public Object get(Object n) { return _scope.get(n); } public JSFunction getFunction(String name) { return _scope.getFunction(name); } public final Set<String> keySet() { return _scope.keySet(); } public Set<String> keySet(boolean includePrototype) { return _scope.keySet(includePrototype); } public boolean containsKey(String s) { return _scope.containsKey(s); } public boolean containsKey(String s, boolean includePrototype) { return _scope.containsKey(s, includePrototype); } public Object set(Object n, Object v) { return _scope.putExplicit(n.toString(), v); } public Object setInt(int n, Object v) { throw new RuntimeException("not allowed"); } public Object getInt(int n) { return _scope.getInt(n); } public Object removeField(Object n) { return _scope.removeField(n); } public JSFunction getConstructor() { return null; } public JSObject getSuper() { return null; } // ---- END BROKEN JSOBJET INTERFACE public TimeZone getTimeZone() { return _tz; } public void setTimeZone(String tz) { if (tz.length() == 3) tz = tz.toUpperCase(); _tz = TimeZone.getTimeZone(tz); if (!_tz.getID().equals(tz)) throw new RuntimeException("can't find time zone[" + tz + "]"); } final String _name; final String _root; final File _rootFile; final GitDir _git; private String _gitBranch; private String _gitHash; final String _environment; final boolean _admin; final AppContext _adminContext; final String _codePrefix; final AppContext _nonAdminParent; private JSFileLibrary _localObject; private JSFileLibrary _core; private JSFileLibrary _external; final Scope _scope; final SeenPath _rootContextReachable; final Scope _initScope; final Scope _configScope = new Scope(); final UsageTracker _usage; final ModuleRegistry _moduleRegistry; final JSArray _globalHead = new JSArray(); private final Map<String, File> _files = Collections.synchronizedMap(new HashMap<String, File>()); private final Set<File> _initFlies = new HashSet<File>(); private final Map<String, JxpSource> _httpServlets = Collections.synchronizedMap(new HashMap<String, JxpSource>()); private final JSObject _libraryVersions = new JSObjectBase(); private Queue<AppWork> _workQueue; private TimeZone _tz = TimeZone.getDefault(); boolean _scopeInited = false; boolean _inScopeSetup = false; long _lastScopeInitTime = 0; final boolean _isGrid; boolean _reset = false; int _numRequests = 0; final JSDate _created = new JSDate(); private File _gitFile = null; private long _lastGitCheckTime = 0; private Collection<JSFunction> _initRefreshHooks = new ArrayList<JSFunction>(); /* * adapter type - can have either a static ("all files in this app are X") * or dynamic - the provided selector function dynamically chooses, falling * back to the static if it returns null */ public static final String INIT_ADAPTER_TYPE = "adapterType"; public static final String INIT_ADAPTER_SELECTOR = "adapterSelector"; private AdapterType _staticAdapterType = AdapterType.UNSET; private JSFunction _adapterSelector = null; private static Logger _libraryLogger = Logger.getLogger("library.load"); static { _libraryLogger.setLevel(Level.INFO); } private static final Set<String> _knownInitScopeThings = new HashSet<String>(); private static final ThreadLocal<AppContext> _tl = new ThreadLocal<AppContext>(); static { _knownInitScopeThings.add("mapUrlToJxpFileCore"); _knownInitScopeThings.add("mapUrlToJxpFile"); _knownInitScopeThings.add("allowed"); _knownInitScopeThings.add("staticCacheTime"); _knownInitScopeThings.add("handle404"); _knownInitScopeThings.add(INIT_ADAPTER_TYPE); _knownInitScopeThings.add(INIT_ADAPTER_SELECTOR); } public static final class AppContextReachable extends ReflectionVisitor.Reachable { public boolean follow(Object o, Class c, java.lang.reflect.Field f) { if (_reachableStoppers.contains(c)) return false; if (f != null && _reachableStoppers.contains(f.getType())) return false; return super.follow(o, c, f); } } private static final Set<Class> _reachableStoppers = new HashSet<Class>(); static { _reachableStoppers.add(HttpServer.class); _reachableStoppers.add(AppServer.class); _reachableStoppers.add(DBTCP.class); _reachableStoppers.add(Mongo.class); _reachableStoppers.add(WeakBag.class); _reachableStoppers.add(WeakValueMap.class); } }
/** * Contains shared code for loading information from a server and caching it for later use. * * <p>Created by Jesse on 1/17/14. */ public class ServerInfoCache<T extends ServiceInfo> { private final Map<URI, T> cache = Collections.synchronizedMap(new HashMap<URI, T>()); private final ServiceInfoLoader<T> loader; public ServerInfoCache(ServiceInfoLoader loader) { this.loader = loader; } public synchronized void clearCache() { cache.clear(); } public final synchronized T getInfo(URI uri, RenderingContext context) { T result = cache.get(uri); if (result == null) { try { result = requestInfo(uri, context); } catch (Exception e) { loader .logger() .info( "Error while getting capabilities for " + uri + ". The print module will assume it's a standard WMS."); String stackTrace = ""; for (StackTraceElement el : e.getStackTrace()) { stackTrace += el.toString() + "\n"; } loader.logger().info(stackTrace); result = loader.createNewErrorResult(); } if (loader.logger().isDebugEnabled()) { loader.logger().debug("GetCapabilities " + uri + ": " + result); } cache.put(uri, result); } return result; } private T requestInfo(URI baseUrl, RenderingContext context) throws IOException, URISyntaxException, ParserConfigurationException, SAXException { URL url = loader.createURL(baseUrl, context); GetMethod method = null; try { final InputStream stream; if ((url.getProtocol().equals("http") || url.getProtocol().equals("https")) && context.getConfig().localHostForwardIsFrom(url.getHost())) { String scheme = url.getProtocol(); final String host = url.getHost(); if (url.getProtocol().equals("https") && context.getConfig().localHostForwardIsHttps2http()) { scheme = "http"; } URL localUrl = new URL(scheme, "localhost", url.getPort(), url.getFile()); HttpURLConnection connexion = (HttpURLConnection) localUrl.openConnection(); connexion.setRequestProperty("Host", host); for (Map.Entry<String, String> entry : context.getHeaders().entrySet()) { connexion.setRequestProperty(entry.getKey(), entry.getValue()); } stream = connexion.getInputStream(); } else { method = new GetMethod(url.toString()); for (Map.Entry<String, String> entry : context.getHeaders().entrySet()) { method.setRequestHeader(entry.getKey(), entry.getValue()); } context.getConfig().getHttpClient(baseUrl).executeMethod(method); int code = method.getStatusCode(); if (code < 200 || code >= 300) { throw new IOException( "Error " + code + " while reading the Capabilities from " + url + ": " + method.getStatusText()); } stream = method.getResponseBodyAsStream(); } final T result; try { result = loader.parseInfo(stream); } finally { stream.close(); } return result; } finally { if (method != null) { method.releaseConnection(); } } } public abstract static class ServiceInfoLoader<T extends ServiceInfo> { public abstract Log logger(); public abstract T createNewErrorResult(); public abstract URL createURL(URI baseUrl, RenderingContext context) throws UnsupportedEncodingException, URISyntaxException, MalformedURLException; public abstract T parseInfo(InputStream stream) throws ParserConfigurationException, IOException, SAXException; /** * Get the text content of the _child_ element or return default value if element does not * exist. An exception is thrown if the child is not found. * * @param element * @param tagName */ public static String getTextContentOfChild(Element element, String tagName) { String result = getTextContextFromPath(element, tagName, null); if (result == null) { throw new NoSuchElementException( "No child " + tagName + " was found in element " + element.getNodeName()); } return result; } /** * Get the text content of the _child_ element or return default value if element does not * exist. The elements are used for selection but the namespace is ignored. * * @param element * @param tagName * @param defaultValue */ public static String getTextContentOfChild( Element element, String tagName, String defaultValue) { Element child = getFirstChildElement(element, tagName); if (child == null || child.getTextContent().trim().isEmpty()) { return defaultValue; } return child.getTextContent(); } /** * Find all the children with the provided tagName and return an array of their non-empty text. * * @param element parent element of elements to get * @param tagName child names * @return */ public static ArrayList<String> getTextContentOfChildren(Element element, String tagName) { ArrayList<String> text = new ArrayList<String>(); final NodeList nodeList = element.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node instanceof Element) { Element child = (Element) node; if (getLocalName(child).equals(tagName)) { final String textContent = node.getTextContent(); if (!textContent.trim().isEmpty()) { text.add(textContent); } } } } return text; } /** * Find the text at the indicated path (not an XPath just a / separated path of tagnames) or * return default value. * * <p>Assumptions: * * <ul> * <li>Only the first child in each path segment is traversed. If there are multiple then they * will be ignored. * </ul> * * @param element * @param path * @param defaultValue * @return */ public static String getTextContextFromPath(Element element, String path, String defaultValue) { return getTextContextFromPath(element, Arrays.asList(path.split("/")), defaultValue); } private static String getTextContextFromPath( Element element, List<String> path, String defaultValue) { final Element child = getFirstChildElement(element, path.get(0)); if (child == null) { return defaultValue; } if (path.size() == 1) { String text = child.getTextContent(); if (text.trim().isEmpty()) { return defaultValue; } else { return text; } } return getTextContextFromPath(child, path.subList(1, path.size()), defaultValue); } private static Element getFirstChildElement(Element element, String tagName) { final NodeList childNodes = element.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node elem = childNodes.item(i); if (elem instanceof Element) { if (getLocalName(elem).equals(tagName)) { return (Element) elem; } } } return null; } private static String getLocalName(Node elem) { final String nodeName = elem.getNodeName(); String[] split = nodeName.split(":", 2); return split.length == 2 ? split[1] : split[0]; } } }