/*
* ConfiguredHost.java
*
* Created on November 2, 2007, 2:49 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.atomojo.www;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import org.atomojo.www.util.IdentityFilter;
import org.atomojo.www.util.SecurityGuard;
import org.atomojo.www.util.Identity;
import org.atomojo.www.util.IdentityManager;
import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Comparator;
import java.util.Enumeration;
import javax.net.ssl.HttpsURLConnection;
import org.atomojo.app.client.Entry;
import org.atomojo.app.client.Feed;
import org.atomojo.app.client.FeedBuilder;
import org.atomojo.app.client.FeedClient;
import org.atomojo.app.client.Link;
import org.atomojo.app.client.LinkSet;
import org.atomojo.app.client.Ontology;
import org.atomojo.app.client.Term;
import org.atomojo.app.client.Text;
import org.atomojo.www.util.Base64Coder;
import org.atomojo.www.util.ProxyApplication;
import org.atomojo.www.util.ResourceManager;
import org.atomojo.www.util.URLRetriever;
import org.atomojo.www.util.script.ScriptManager;
import org.infoset.xml.DocumentLoader;
import org.infoset.xml.sax.SAXDocumentLoader;
import org.restlet.Application;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.engine.log.AccessLogFileHandler;
import org.restlet.resource.Directory;
import org.restlet.routing.Router;
import org.restlet.routing.Template;
import org.restlet.routing.TemplateRoute;
import org.restlet.routing.VirtualHost;
/**
*
* @author alex
*/
public class ConfiguredHost
{
static {
try {
Ontology.getDefaultInstance().load(ConfiguredHost.class.getResource("www.ontology.xml"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
final static URI T_BASE = URI.create("http://www.atomojo.org/O/www/configuration");
final static URI T_APP = Ontology.getDefaultInstance().find(T_BASE,"application").getURI();
final static URI T_APP_MATCH = Ontology.getDefaultInstance().find(T_BASE,"application/match").getURI();
final static URI T_APP_MATCH_MODE = Ontology.getDefaultInstance().find(T_BASE,"application/match/mode").getURI();
final static URI T_APP_PRIORITY = Ontology.getDefaultInstance().find(T_BASE,"application/priority").getURI();
final static URI T_APP_CLASS = Ontology.getDefaultInstance().find(T_BASE,"application/class").getURI();
final static URI T_APP_PROXY = Ontology.getDefaultInstance().find(T_BASE,"application/proxy").getURI();
final static URI T_APP_RESOURCE = Ontology.getDefaultInstance().find(T_BASE,"application/resource").getURI();
final static URI T_APP_PREFIX = Ontology.getDefaultInstance().find(T_BASE,"application/prefix").getURI();
final static URI T_LAYOUT = Ontology.getDefaultInstance().find(T_BASE,"layout").getURI();
final static URI T_LAYOUT_MATCH = Ontology.getDefaultInstance().find(T_BASE,"layout/match").getURI();
final static URI T_LAYOUT_PRIORITY = Ontology.getDefaultInstance().find(T_BASE,"layout/priority").getURI();
final static URI T_LAYOUT_MEDIA_TYPE = Ontology.getDefaultInstance().find(T_BASE,"layout/media-type").getURI();
final static URI T_RESOURCE = Ontology.getDefaultInstance().find(T_BASE,"resource").getURI();
final static URI T_RESOURCE_NAME = Ontology.getDefaultInstance().find(T_BASE,"resource/name").getURI();
final static URI T_RESOURCE_PATH = Ontology.getDefaultInstance().find(T_BASE,"resource/path").getURI();
final static URI T_RESOURCE_RELATION = Ontology.getDefaultInstance().find(T_BASE,"resource/relation").getURI();
final static URI T_RESOURCE_QUERY = Ontology.getDefaultInstance().find(T_BASE,"resource/query").getURI();
final static URI T_RESOURCE_MEDIA_TYPE = Ontology.getDefaultInstance().find(T_BASE,"resource/media-type").getURI();
final static URI T_SECURITY_PROTECTED_PATH = Ontology.getDefaultInstance().find(T_BASE,"security/protected/path").getURI();
final static URI T_SECURITY_REDIRECT_UNAUTHORIZED = Ontology.getDefaultInstance().find(T_BASE,"security/redirect/unauthorized").getURI();
final static URI T_SECURITY_REDIRECT_LOGIN = Ontology.getDefaultInstance().find(T_BASE,"security/redirect/login").getURI();
final static URI T_SECURITY_GROUP_ID = Ontology.getDefaultInstance().find(T_BASE,"security/group/id").getURI();
final static URI T_SECURITY_ROLE_ID = Ontology.getDefaultInstance().find(T_BASE,"security/role/id").getURI();
class MediaRetriever
{
URI location;
String username;
String password;
/** Creates a new instance of MediaRetriever */
public MediaRetriever(URI location,String username, String password)
{
this.location = location;
this.username = username;
this.password = password==null ? "" : password;
}
public Reader open()
throws IOException
{
URLConnection connection = location.toURL().openConnection();
if (connection instanceof HttpsURLConnection) {
HttpsURLConnection https = (HttpsURLConnection)connection;
https.setHostnameVerifier(org.apache.commons.ssl.HostnameVerifier.DEFAULT_AND_LOCALHOST);
}
if (username!=null) {
connection.setRequestProperty("Authorization", "Basic " + Base64Coder.encode(username+':'+password));
}
String charset = "UTF-8";
InputStream is = connection.getInputStream();
String contentType = connection.getContentType();
if (contentType!=null) {
int semicolon = contentType.indexOf(';');
if (semicolon>=0) {
String type = contentType.substring(0,semicolon);
String rest = contentType.substring(semicolon+1);
int start = rest.indexOf("charset=");
if (start>=0) {
charset = rest.substring(start+8);
}
contentType = type;
}
}
return new InputStreamReader(is,charset);
}
}
class AppInfo {
Application app;
Date edited;
String match;
AppInfo(Application app,Date edited) {
this.app = app;
this.edited = edited;
this.match = null;
}
}
Context context;
Client client;
Router internalRouter;
SecurityGuard security;
VirtualHost vhost;
Configuration.Host hostConf;
Date edited;
Link autoConf;
Router router;
DocumentLoader docLoader;
Map<String,AppInfo> applications;
List<AppInfo> staticApplications;
ScriptManager scriptManager;
ResourceManager resourceManager;
Logger hostLog;
boolean isStatic;
/** Creates a new instance of ConfiguredHost */
public ConfiguredHost(Context context,Router internalRouter,Configuration.Interface iface,Configuration.Host hostConf,Date edited,boolean isStatic)
{
this.context = context;
this.internalRouter = internalRouter;
this.hostConf = hostConf;
this.edited = edited;
this.docLoader = new SAXDocumentLoader();
this.isStatic = isStatic;
context.getAttributes().put(WebComponent.LINKS_ATTR, hostConf.getLinks());
getLogger().info("Adding host "+hostConf.getName()+":"+iface.getPort());
this.vhost = new VirtualHost(context) {
public void handle(Request request, Response response) {
long startTime = System.currentTimeMillis();
super.handle(request,response);
if (hostLog!=null) {
int duration = (int) (System.currentTimeMillis() - startTime);
hostLog.log(Level.INFO,formatLog(request,response,duration));
}
}
};
if (!iface.getAddress().equals("*")) {
try {
InetAddress addr = InetAddress.getByName(iface.getAddress());
String saddr = addr.toString();
saddr = saddr.substring(saddr.indexOf('/')+1);
getLogger().info("Restricting "+hostConf.getName()+" to address "+saddr);
vhost.setServerAddress(saddr);
} catch (UnknownHostException ex) {
getLogger().severe("Cannot resolve host name "+iface.getAddress());
}
}
if (!hostConf.getName().equals("*")) {
vhost.setHostDomain(hostConf.getName());
}
vhost.setHostPort(Integer.toString(iface.getPort()));
if (hostConf.getLogConfiguration().get("pattern")!=null) {
String name = hostConf.getName();
if (name.equals("*")) {
name = "any";
}
name = "host."+name;
hostLog = Logger.getLogger(name);
setupLog();
}
router = vhost;
for (String name : hostConf.getParameters().keySet()) {
String value = hostConf.getParameters().get(name);
getLogger().info("Adding host parameter "+name+" -> "+value);
context.getParameters().set(name,value,false);
}
List<Link> auths = hostConf.getLinks().get("auth-service");
if (auths!=null && auths.size()>0) {
Link authLink = auths.get(0);
getLogger().info("Adding identity and security filters.");
router = new Router(context);
router.setDefaultMatchingMode(Template.MODE_STARTS_WITH);
security = new SecurityGuard(context);
security.setNext(router);
final IdentityFilter filter = new IdentityFilter(context,security,new Reference(authLink.getLink().toString()));
vhost.attachDefault(filter);
this.context.getAttributes().put(IdentityManager.ATTR, new IdentityManager() {
public void add(String id,Identity identity) {
filter.addIdentity(id, identity);
}
public boolean remove(String id)
{
return filter.removeIdentity(id);
}
});
}
this.staticApplications = new ArrayList<AppInfo>();
for (Configuration.Content content : hostConf.getContentSources()) {
try {
final String uri = content.getSource().toString();
String scheme = content.getSource().getScheme();
if (scheme.equals("http") || scheme.equals("https")) {
getLogger().info(" Proxy: "+content.getSource()+" at "+content.getMatch());
ProxyApplication proxy = new ProxyApplication(context,uri.toString());
proxy.getTunnelService().setEnabled(false);
router.attach(content.getMatch(),proxy);
} else {
// hope the directory resource can handle it
getLogger().info(" Directory: "+content.getSource()+" at "+content.getMatch());
Application app = new Application(context) {
public Restlet createRoot() {
Directory directory = new Directory(getContext(),uri);
directory.setIndexName("index.html");
return directory;
}
};
router.attach(content.getMatch(),app);
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot add content at "+content.getMatch(),ex);
}
}
List<Link> metadataSet = hostConf.getLinks().get("metadata");
Link metadata = metadataSet==null ? null : metadataSet.size()>0 ? metadataSet.get(0) : null;
List<Link> autoconfList = hostConf.getLinks().get("autoconf");
this.resourceManager = new ResourceManager(getLogger(),hostConf.getLinks());
this.context.getAttributes().put(ResourceManager.ATTR, resourceManager);
// TODO: make the expiration configurable
client = context.getClientDispatcher();
if (autoconfList!=null && autoconfList.size()>0) {
autoConf = autoconfList.get(autoconfList.size()-1);
applications = new TreeMap<String,AppInfo>();
this.scriptManager = new ScriptManager(getContext(),client,metadata,autoConf.getUsername(),autoConf.getPassword(),60000);
this.context.getAttributes().put(ScriptManager.ATTR, scriptManager);
check();
} else {
this.scriptManager = new ScriptManager(getContext(),client,metadata,null,null,60000);
this.context.getAttributes().put(ScriptManager.ATTR, scriptManager);
configureStaticApplications();
}
}
public Context getContext() {
return context;
}
public boolean isStatic() {
return isStatic;
}
public Logger getLogger() {
return context.getLogger();
}
public Date getEdited() {
return edited;
}
public void setEdited(Date edited)
{
this.edited = edited;
}
public VirtualHost getVirtualHost() {
return vhost;
}
static String getTerm(Entry entry,URI uterm)
{
Term term = entry.getTerm(uterm);
return term==null ? null : term.getFirstValue();
}
protected String formatLog(Request request, Response response,int duration) {
StringBuilder sb = new StringBuilder();
long currentTime = System.currentTimeMillis();
// Append the date of the request
sb.append(String.format("%tF", currentTime));
sb.append('\t');
// Append the time of the request
sb.append(String.format("%tT", currentTime));
sb.append('\t');
// Append the client IP address
String clientAddress = request.getClientInfo().getUpstreamAddress();
sb.append((clientAddress == null) ? "-" : clientAddress);
sb.append('\t');
// Append the user name (via IDENT protocol)
if ((request.getChallengeResponse() != null) && (request.getChallengeResponse().getIdentifier() != null)) {
sb.append(request.getChallengeResponse().getIdentifier());
} else {
// [enddef]
sb.append('-');
}
sb.append('\t');
// Append the server IP address
String serverAddress = response.getServerInfo().getAddress();
sb.append((serverAddress == null) ? "-" : serverAddress);
sb.append('\t');
// Append the server port
Integer serverport = response.getServerInfo().getPort();
sb.append((serverport == null) ? "-" : serverport.toString());
sb.append('\t');
// Append the method name
String methodName = (request.getMethod() == null) ? "-" : request.getMethod().getName();
sb.append((methodName == null) ? "-" : methodName);
// Append the resource path
sb.append('\t');
String resourcePath = (request.getResourceRef() == null) ? "-"
: request.getResourceRef().getPath();
sb.append((resourcePath == null) ? "-" : resourcePath);
// Append the resource query
sb.append('\t');
String resourceQuery = (request.getResourceRef() == null) ? "-"
: request.getResourceRef().getQuery();
sb.append((resourceQuery == null) ? "-" : resourceQuery);
// Append the status code
sb.append('\t');
sb.append((response.getStatus() == null) ? "-" : Integer.toString(response.getStatus().getCode()));
// Append the returned size
sb.append('\t');
if (!response.isEntityAvailable()
|| Status.REDIRECTION_NOT_MODIFIED.equals(response.getStatus())
|| Status.SUCCESS_NO_CONTENT.equals(response.getStatus())
|| Method.HEAD.equals(request.getMethod())) {
sb.append('0');
} else {
sb.append((response.getEntity().getSize() == -1) ? "-" : Long.toString(response.getEntity().getSize()));
}
// Append the received size
sb.append('\t');
if (request.getEntity() == null) {
sb.append('0');
} else {
sb.append((request.getEntity().getSize() == -1) ? "-" : Long.toString(request.getEntity().getSize()));
}
// Append the duration
sb.append('\t');
sb.append(duration);
// Append the host reference
sb.append('\t');
sb.append((request.getHostRef() == null) ? "-" : request.getHostRef().toString());
// Append the agent name
sb.append('\t');
String agentName = request.getClientInfo().getAgent();
sb.append((agentName == null) ? "-" : agentName);
// Append the referrer
sb.append('\t');
sb.append((request.getReferrerRef() == null) ? "-" : request.getReferrerRef().getIdentifier());
return sb.toString();
}
protected void configureSecurity()
{
if (security==null) {
return;
}
// Load the layout locations
URI protectedTermLoc = URI.create(autoConf.getLink().toString()+T_SECURITY_PROTECTED_PATH.toString());
getLogger().info("Loading protected paths from "+protectedTermLoc);
FeedClient layoutFeedClient = new FeedClient(client,new Reference(protectedTermLoc));
if (autoConf.getUsername()!=null) {
layoutFeedClient.setIdentity(autoConf.getUsername(),autoConf.getPassword());
}
try {
FeedBuilder builder = new FeedBuilder();
Response response = layoutFeedClient.get(builder);
if (response.getStatus().isSuccess()) {
Feed feed = builder.getFeed();
feed.index();
Set<String> names = new TreeSet<String>();
List<SecurityGuard.SecureRoute> copyOfRoutes = new ArrayList<SecurityGuard.SecureRoute>();
copyOfRoutes.addAll(security.getRoutes());
try {
security.getRoutes().clear();
for (Entry entry : feed.getEntriesByTerm(T_SECURITY_PROTECTED_PATH)) {
Term paths = entry.getTerm(T_SECURITY_PROTECTED_PATH);
getLogger().info("Security entry "+entry.getId());
String unauthorized = getTerm(entry,T_SECURITY_REDIRECT_UNAUTHORIZED);
Reference unauthorizedRef = unauthorized==null ? null : new Reference(unauthorized);
String login = getTerm(entry,T_SECURITY_REDIRECT_UNAUTHORIZED);
Reference loginRef = login==null ? null : new Reference(login);
try {
Set<UUID> groups = new TreeSet<UUID>();
Term groupSpec = entry.getTerm(T_SECURITY_GROUP_ID);
if (groupSpec!=null) {
for (String id : groupSpec.getValues()) {
UUID uuid = UUID.fromString(id);
groups.add(uuid);
getLogger().info("... group "+uuid+" required.");
}
}
Set<UUID> roles = new TreeSet<UUID>();
Term rolesSpec = entry.getTerm(T_SECURITY_ROLE_ID);
if (rolesSpec!=null) {
for (String id : rolesSpec.getValues()) {
UUID uuid = UUID.fromString(id);
roles.add(uuid);
getLogger().info("... role "+uuid+" required.");
}
}
Collection<String> values = paths.getValues();
if (values!=null) {
for (String path : paths.getValues()) {
getLogger().info("... adding secure path "+path);
SecurityGuard.SecureRoute route = security.addSecureArea(path, loginRef, unauthorizedRef);
route.getArea().getRequiredGroups().addAll(groups);
route.getArea().getRequiredRoles().addAll(roles);
}
}
} catch (IllegalArgumentException ex) {
getLogger().severe("Bad UUID value in entry "+entry.getId()+": "+ex.getMessage());
}
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Exception while processing protected path feed. Restoring secure routes.",ex);
security.getRoutes().clear();
security.getRoutes().addAll(copyOfRoutes);
}
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot get feed "+protectedTermLoc+" due to exception: "+ex.getMessage(),ex);
}
}
protected void configureLayouts() {
// Load layouts
URI layoutTemLoc = URI.create(autoConf.getLink().toString()+T_LAYOUT.toString());
getLogger().info("Loading Layouts from "+layoutTemLoc);
FeedClient appFeedClient = new FeedClient(client,new Reference(layoutTemLoc));
if (autoConf.getUsername()!=null) {
appFeedClient.setIdentity(autoConf.getUsername(),autoConf.getPassword());
}
try {
FeedBuilder builder = new FeedBuilder();
Response response = appFeedClient.get(builder);
boolean found = false;
if (!response.getStatus().isSuccess()) {
if (response.getStatus().getCode()!=404) {
getLogger().severe("Cannot get feed "+layoutTemLoc+", status="+response.getStatus().getCode());
} else {
getLogger().warning("No application feed to load.");
}
} else {
Feed feed = builder.getFeed();
feed.index();
Set<String> ids = new TreeSet<String>();
String base = T_BASE.toString();
for (Entry entry : feed.getEntriesByTerm(T_LAYOUT)) {
String id = entry.getId();
ids.add(id);
Text text = entry.getContent();
URI location = text.getElement().getBaseURI().resolve(text.getSourceLink());
ScriptManager.Entry info = scriptManager.get(id);
if (info!=null && entry.getEdited().equals(info.getEdited())) {
getLogger().info("Script entry "+id+", location="+location+" not modified.");
continue;
}
Set<Term> criteria = new TreeSet<Term>();
for (Term term : entry.getTerms().values()) {
if (!term.getURI().toString().startsWith(base)) {
criteria.add(term);
}
}
Term match = entry.getTerm(T_LAYOUT_MATCH);
Term mediaTypeT = entry.getTerm(T_LAYOUT_MEDIA_TYPE);
Term priorityT = entry.getTerm(T_LAYOUT_PRIORITY);
MediaType mediaType = mediaTypeT==null ? MediaType.APPLICATION_XHTML_XML : MediaType.valueOf(mediaTypeT.getFirstValue());
String matchValue = match==null ? null : match.getFirstValue();
if (matchValue==null && match!=null) {
// set the value to the empty string
matchValue = "";
}
int priority = priorityT==null ? -1 : Integer.parseInt(priorityT.getFirstValue());
if (info!=null) {
getLogger().info("Reloading script entry "+id+", location="+location);
scriptManager.reload(id,entry.getEdited(),criteria,matchValue,priority,mediaType);
} else {
getLogger().info("Configuring script entry "+id+", location="+location);
scriptManager.add(id,entry.getEdited(),criteria,matchValue,priority,mediaType,location);
}
}
for (String id : scriptManager.getKeys()) {
if (!ids.contains(id)) {
getLogger().info("Removing script entry "+id);
scriptManager.remove(id);
}
}
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot get feed "+layoutTemLoc+" due to exception: "+ex.getMessage(),ex);
}
}
protected void configureResources() {
// Load layouts
URI resourceTermLoc = URI.create(autoConf.getLink().toString()+T_RESOURCE.toString());
getLogger().info("Loading resources from "+resourceTermLoc);
FeedClient appFeedClient = new FeedClient(client,new Reference(resourceTermLoc));
if (autoConf.getUsername()!=null) {
appFeedClient.setIdentity(autoConf.getUsername(),autoConf.getPassword());
}
try {
FeedBuilder builder = new FeedBuilder();
Response response = appFeedClient.get(builder);
boolean found = false;
if (!response.getStatus().isSuccess()) {
if (response.getStatus().getCode()!=404) {
getLogger().severe("Cannot get feed "+resourceTermLoc+", status="+response.getStatus().getCode());
} else {
getLogger().warning("No application feed to load.");
}
} else {
Feed feed = builder.getFeed();
feed.index();
Set<String> ids = new TreeSet<String>();
String base = T_BASE.toString();
for (Entry entry : feed.getEntriesByTerm(T_RESOURCE)) {
String id = entry.getId();
ids.add(id);
ResourceManager.Entry info = resourceManager.get(id);
Term nameT = entry.getTerm(T_RESOURCE_NAME);
Term pathT = entry.getTerm(T_RESOURCE_PATH);
Term relationT = entry.getTerm(T_RESOURCE_RELATION);
Term queryT = entry.getTerm(T_RESOURCE_QUERY);
Term mediaTypeT = entry.getTerm(T_RESOURCE_MEDIA_TYPE);
if (nameT==null || nameT.getFirstValue()==null) {
getLogger().warning("Ignoring resource "+id+" without a name.");
continue;
}
String name = nameT.getFirstValue();
if (info!=null && entry.getEdited().equals(info.getEdited())) {
getLogger().info("Resource entry "+id+" named "+name+" not modified.");
continue;
}
String query = null;
if (queryT==null || queryT.getFirstValue()==null) {
Text text = entry.getContent();
if (text.getSourceLink()!=null) {
URI location = text.getElement().getBaseURI().resolve(text.getSourceLink());
}
} else {
query = queryT.getFirstValue();
}
MediaType mediaType = mediaTypeT==null ? null : MediaType.valueOf(mediaTypeT.getFirstValue());
if (info!=null) {
getLogger().info("Reloading resource entry "+id+" named "+name);
resourceManager.reload(id,entry.getEdited(),name,pathT==null ? null : pathT.getFirstValue(),relationT==null ? null : relationT.getFirstValue(),mediaType,query);
} else {
getLogger().info("Configuring resource entry "+id+" named "+name);
resourceManager.add(id,entry.getEdited(),name,pathT==null ? null : pathT.getFirstValue(),relationT==null ? null : relationT.getFirstValue(),mediaType,query);
}
}
for (String id : resourceManager.getKeys()) {
if (!ids.contains(id)) {
getLogger().info("Removing resource entry "+id);
resourceManager.remove(id);
}
}
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot get feed "+resourceTermLoc+" due to exception: "+ex.getMessage(),ex);
}
}
protected void configureStaticApplications() {
String internalName = hostConf.getInternalName();
if (staticApplications.size()>0) {
for (AppInfo info : staticApplications) {
router.detach(info.app);
router.attach(info.match,info.app);
if (internalName!=null) {
internalRouter.detach(info.app);
String route = "/"+internalName+info.match;
getLogger().info(" updating internal route: "+route);
internalRouter.attach(route,info.app).getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);
}
}
} else {
for (Configuration.AppConf appConf : hostConf.getApplications()) {
try {
getLogger().info(" Application: "+appConf.getApplicationDef().getName()+" at "+appConf.getMatch());
LinkSet set = new LinkSet();
set.addLinkSet(hostConf.getLinks());
set.addLinkSet(appConf.getLinks());
Application app = appConf.getApplication(this.context,set);
router.attach(appConf.getMatch(),app);
if (internalName!=null) {
String route = "/"+internalName+appConf.getMatch();
getLogger().info(" adding internal route: "+route);
internalRouter.attach(route,app).getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);
}
AppInfo info = new AppInfo(app,null);
info.match = appConf.getMatch();
staticApplications.add(info);
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot start application at "+appConf.getMatch(),ex);
}
}
}
}
protected void configureApplications() {
// Load applications
URI appTermLoc = URI.create(autoConf.getLink().toString()+T_APP.toString());
getLogger().info("Loading Applications from "+appTermLoc);
FeedClient appFeedClient = new FeedClient(client,new Reference(appTermLoc));
if (autoConf.getUsername()!=null) {
appFeedClient.setIdentity(autoConf.getUsername(),autoConf.getPassword());
}
try {
FeedBuilder builder = new FeedBuilder();
Response response = appFeedClient.get(builder);
boolean found = false;
if (!response.getStatus().isSuccess()) {
if (response.getStatus().getCode()!=404) {
getLogger().severe("Cannot get feed "+appTermLoc+", status="+response.getStatus().getCode());
} else {
getLogger().warning("No application feed to load.");
}
} else {
Feed feed = builder.getFeed();
feed.index();
Set<String> ids = new TreeSet<String>();
List<Entry> entries = new ArrayList<Entry>();
entries.addAll(feed.getEntriesByTerm(T_APP));
Comparator<Entry> sorter = new Comparator<Entry>() {
public int compare(Entry A, Entry B) {
Term AP = A.getTerm(T_APP_PRIORITY);
Term BP = B.getTerm(T_APP_PRIORITY);
//getLogger().info("A: "+A.getTitle()+" "+AP+" vs B: "+B.getTitle()+" "+BP);
if (BP==null && AP==null) {
return 0;
} else if (AP!=null && BP==null) {
return -1;
} else if (AP==null && BP!=null) {
return 1;
} else {
int a = Integer.parseInt(AP.getFirstValue());
int b = Integer.parseInt(BP.getFirstValue());
return a>b ? -1 : a==b ? 0 : 1;
}
}
public boolean equals(Object obj) {
return obj==this;
}
};
Collections.sort(entries,sorter);
Collections.reverse(entries);
for (Entry entry : entries) {
found = true;
URI baseURI = entry.getDocument().getBaseURI();
Term priority = entry.getTerm(T_APP_PRIORITY);
getLogger().info("Application: title: "+entry.getTitle()+", base: "+baseURI+", priority: "+(priority==null ? "" : priority.getFirstValue()));
ids.add(entry.getId());
boolean exact = false;
if (entry.getTerm(T_APP_MATCH_MODE)!=null) {
exact = "exact".equals(entry.getTerm(T_APP_MATCH_MODE).getFirstValue());
}
AppInfo appInfo = applications.get(entry.getId());
if (appInfo!=null && appInfo.edited.getTime()>=entry.getEdited().getTime()) {
// reattach to make sure it is in the right order
router.detach(appInfo.app);
Term match = entry.getTerm(T_APP_MATCH);
if (match!=null && match.getValues()!=null) {
for (String pattern : match.getValues()) {
TemplateRoute route = router.attach(pattern,appInfo.app);
if (exact) {
route.getTemplate().setMatchingMode(Template.MODE_EQUALS);
}
}
} else {
TemplateRoute route = router.attach("",appInfo.app);
if (exact) {
route.getTemplate().setMatchingMode(Template.MODE_EQUALS);
}
}
getLogger().info("No changes, skipping.");
continue;
}
for (Term t : entry.getTerms().values()) {
getLogger().info(t.getURI()+": "+t.getFirstValue());
}
getLogger().info(T_APP_MATCH.toString());
Term classTerm = entry.getTerm(T_APP_CLASS);
Term proxyTerm = entry.getTerm(T_APP_PROXY);
Context appContext = context.createChildContext();
LinkSet set = new LinkSet();
set.addLinkSet(entry.getLinks());
set.addLinkSet(hostConf.getLinks());
appContext.getAttributes().put(WebComponent.LINKS_ATTR,set);
appContext.getAttributes().put(ScriptManager.ATTR,scriptManager);
appContext.getAttributes().put(ResourceManager.ATTR,resourceManager);
for (URI t : entry.getTerms().keySet()) {
String value = entry.getTerm(t).getFirstValue();
getLogger().info("Setting parameter: "+t+"="+value);
appContext.getParameters().set(t.toString(),value,false);
}
appContext.getParameters().set("username",autoConf.getUsername(),false);
appContext.getParameters().set("password",autoConf.getPassword(),false);
for (Term t : entry.getTerms().values()) {
String key = t.getURI().toString();
if (t.getValues()!=null) {
for (String value : t.getValues()) {
appContext.getParameters().add(key, value);
}
} else {
appContext.getParameters().add(key,"true");
}
}
Application app = null;
if (proxyTerm!=null) {
String value = proxyTerm.getFirstValue();
List<Link> links = entry.getLinks().get(value);
Link target = null;
if (links!=null && links.size()>0) {
target = links.get(0);
}
if (target==null) {
links = hostConf.getLinks().get(value);
if (links!=null && links.size()>0) {
target = links.get(0);
}
}
if (target!=null) {
app = new ProxyApplication(appContext,target);
}
} else if (classTerm!=null) {
String className = classTerm.getFirstValue();
List<Link> libraryLinks = entry.getLinks().get("library");
String href = null;
Text text = entry.getContent();
if (text!=null) {
href = text.getSourceLink();
}
Class<Application> appClass = null;
Class<?> foundClass = null;
if ((libraryLinks==null || libraryLinks.size()==0) && href==null) {
foundClass = Class.forName(className);
} else {
URL [] downloads = new URL[(libraryLinks==null ? 0 : libraryLinks.size())+(href==null ? 0 : 1)];
if (href!=null) {
downloads[0] = entry.getDocument().getDocumentElement().getBaseURI().resolve(href).toURL();
}
if (libraryLinks!=null) {
for (int pos = href==null ? 0 : 1; pos<libraryLinks.size(); pos++) {
Link link = libraryLinks.get(pos);
downloads[pos] = link.getLink().toURL();
}
}
URL [] urls = new URL[downloads.length];
for (int i=0; i<downloads.length; i++) {
File jarfile = File.createTempFile("jar-T"+System.currentTimeMillis()+"-", ".jar");
urls[i] = jarfile.toURL();
URLRetriever retriever = new URLRetriever(downloads[i]);
try {
retriever.retrieve(jarfile, autoConf.getUsername(), autoConf.getPassword());
} catch (IOException ex) {
getLogger().info("Cannot download "+downloads[i]+" due to: "+ex.getMessage());
continue;
}
}
URLClassLoader classLoader = new URLClassLoader(urls,ConfiguredHost.class.getClassLoader()) {
protected PermissionCollection getPermissions(CodeSource source) {
PermissionCollection collection = super.getPermissions(source);
URL url = source.getLocation();
getLogger().info("Code source: "+url);
Enumeration<Permission> permissions = collection.elements();
while (permissions.hasMoreElements()) {
Permission p = permissions.nextElement();
getLogger().info("Permission: "+p.getName()+", "+p.getClass().getName()+", action: "+p.getActions());
}
return collection;
}
};
foundClass = classLoader.loadClass(className);
PermissionCollection collection = foundClass.getProtectionDomain().getPermissions();
Enumeration<Permission> permissions = collection.elements();
while (permissions.hasMoreElements()) {
Permission p = permissions.nextElement();
getLogger().info("Permission: "+p.getName()+", "+p.getClass().getName()+", action: "+p.getActions());
}
}
if (!Application.class.isAssignableFrom(foundClass)) {
getLogger().info("Class "+className+" is not an subclas of "+Application.class.getName());
continue;
}
appClass = (Class<Application>)foundClass;
Constructor<Application> makeit = appClass.getConstructor(Context.class);
app = makeit.newInstance(appContext);
}
if (app!=null) {
if (appInfo!=null) {
router.detach(appInfo.app);
appInfo.app = app;
appInfo.edited = entry.getEdited();
Term match = entry.getTerm(T_APP_MATCH);
if (match!=null && match.getValues()!=null) {
for (String pattern : match.getValues()) {
TemplateRoute route = router.attach(pattern,app);
if (exact) {
route.getTemplate().setMatchingMode(Template.MODE_EQUALS);
}
}
} else {
TemplateRoute route = router.attach("",app);
if (exact) {
route.getTemplate().setMatchingMode(Template.MODE_EQUALS);
}
}
} else {
applications.put(entry.getId(),new AppInfo(app,entry.getEdited()));
Term match = entry.getTerm(T_APP_MATCH);
if (match!=null && match.getValues()!=null) {
for (String pattern : match.getValues()) {
TemplateRoute route = router.attach(pattern,app);
if (exact) {
route.getTemplate().setMatchingMode(Template.MODE_EQUALS);
}
}
} else {
TemplateRoute route = router.attach("",app);
if (exact) {
route.getTemplate().setMatchingMode(Template.MODE_EQUALS);
}
}
}
}
}
Set<String> currentIds = new TreeSet<String>();
currentIds.addAll(applications.keySet());
for (String id : currentIds) {
if (!ids.contains(id)) {
AppInfo appInfo = applications.remove(id);
router.detach(appInfo.app);
}
}
}
if (!found) {
getLogger().warning("No application was found.");
} else {
AppInfo appInfo = applications.remove("<none>");
if (appInfo!=null) {
router.detach(appInfo.app);
}
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot get feed "+appTermLoc+" due to exception: "+ex.getMessage(),ex);
}
}
public void setupLog() {
if (hostLog==null) {
return;
}
String pattern = hostConf.getLogConfiguration().get("pattern");
String value = hostConf.getLogConfiguration().get("limit");
int limit = 10*1024*1024;
if (value!=null) {
try {
limit = Integer.parseInt(value);
} catch (NumberFormatException ex) {
getLogger().log(Level.SEVERE,"Cannot parse limit value "+value+" for log configuration: "+ex.getMessage());
}
}
value = hostConf.getLogConfiguration().get("count");
int count = 100;
if (value!=null) {
try {
count = Integer.parseInt(value);
} catch (NumberFormatException ex) {
getLogger().log(Level.SEVERE,"Cannot parse count value "+value+" for log configuration: "+ex.getMessage());
}
}
boolean append = true;
value = hostConf.getLogConfiguration().get("append");
if (value!=null) {
append = value.equals("true");
}
try {
AccessLogFileHandler handler = new AccessLogFileHandler(pattern,limit,count,append);
value = hostConf.getLogConfiguration().get("encoding");
if (value!=null) {
handler.setEncoding(value);
}
value = hostConf.getLogConfiguration().get("level");
handler.setLevel(Level.ALL);
if (value!=null) {
try {
handler.setLevel(Level.parse(value));
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot parse level value "+value+" for log configuration: "+ex.getMessage());
}
}
hostLog.addHandler(handler);
} catch (IOException ex) {
getLogger().log(Level.SEVERE,"Cannot instantiate access log file handler for host.",ex);
}
}
public synchronized void check() {
if (autoConf==null) {
return;
}
configureSecurity();
configureApplications();
configureResources();
configureStaticApplications();
configureLayouts();
}
}