/*
* $Id: Smith.java,v 1.12 2002/09/16 08:05:03 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.script;
import java.io.IOException;
import java.io.OutputStream;
import java.security.PermissionCollection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import anvil.codec.School;
import anvil.server.Address;
import anvil.server.Resource;
import anvil.server.ContainerException;
import anvil.server.MemoryLoader;
import anvil.server.ZoneClassLoader;
import anvil.server.Zone;
import anvil.java.util.Hashlist;
import anvil.Location;
import anvil.ErrorListener;
import anvil.ErrorListenerImpl;
import anvil.ForgingException;
/**
* class Smith
*
* @author: Jani Lehtim�ki
*/
public class Smith implements ErrorListener, School
{
protected static final Object LOAD_LOCK = new Object();
protected ModuleCache _cache;
protected Address _address;
protected Hashlist _forged = new Hashlist();
protected ErrorListener _listener = null;
protected HashSet _loading = new HashSet();
protected HashSet _notimported = null;
protected boolean _changed = false;
protected MemoryLoader _loader = null;
public Smith(ModuleCache cache, Address address)
{
_cache = cache;
_address = address;
}
protected ErrorListener getListener()
{
ErrorListener listener = _listener;
if (listener == null) {
_listener = listener = new ErrorListenerImpl();
}
return listener;
}
public void error(Location location, Throwable throwable)
{
getListener().error(location, throwable);
}
public void error(Location location, String message)
{
getListener().error(location, message);
}
public int errors()
{
if (_listener == null) {
return 0;
} else {
return _listener.errors();
}
}
public Enumeration getEvents()
{
return getListener().getEvents();
}
public void merge(ErrorListener listener)
{
getListener().merge(listener);
}
public Address getAddress()
{
return _address;
}
private void purge(ModuleEnvelope envelope)
{
Address a = envelope.getAddress();
_cache.purge(a);
_forged.remove(a);
}
protected void linkImports(ModuleEnvelope envelope)
{
Iterator iter = envelope.getModule().getDependencies();
while(iter.hasNext()) {
Dependency dep = (Dependency)iter.next();
dep.setScript((ModuleEnvelope)_forged.get(dep.getAddress()));
}
}
protected boolean forgeImports(ModuleEnvelope envelope)
throws IOException, ForgingException
{
boolean doparse = false;
Module script = envelope.getModule();
boolean failed = false;
Iterator iter = script.getDependencies();
Address source = envelope.getAddress();
String sourcepath = source.getPathinfo();
Zone zone = source.getZone();
boolean hasPolicy = zone.getDomain().hasPolicy();
while(iter.hasNext()) {
Dependency dep = (Dependency)iter.next();
try {
Address target = dep.getAddress();
if (hasPolicy) {
String targetpath = target.getPathinfo();
if (!ImportPermission.onSameDir(sourcepath, targetpath)) {
if (!zone.checkPermission(new ImportPermission(targetpath))) {
error(dep.getLocation(), "Access denied: "+targetpath);
}
}
}
ModuleEnvelope imported = forge(target);
if (imported != null) {
boolean source_compiled = envelope.isCompiled();
boolean target_compiled = imported.isCompiled();
if (source_compiled) {
if (target_compiled) {
// check signatures
if (!imported.getDescriptor().equals(dep.getDescriptor())) {
doparse = true;
}
} else {
doparse = true;
}
} else {
// source already parsed
}
} else {
boolean report_error = true;
Address addr = dep.getAddress();
if (_notimported == null) {
_notimported = new HashSet();
_notimported.add(addr);
} else {
if (_notimported.contains(addr)) {
report_error = false;
} else {
_notimported.add(addr);
}
}
if (report_error) {
error(dep.getLocation(), "Couldn't import: "+dep.getPathinfo());
}
}
} catch (IOException e) {
error(dep.getLocation(), "Resource not found: "+dep.getPathinfo());
} catch (ForgingException e) {
merge(e.getErrorListener());
}
}
return doparse;
}
public ModuleEnvelope get(Address address)
{
return (ModuleEnvelope)_forged.get(address);
}
protected ModuleEnvelope load(Address address)
throws IOException, ForgingException
{
ModuleEnvelope envelope = _cache.get(address);
Resource resource = null;
boolean reload = false;
if (envelope != null) {
long lastmodified = envelope.getLastModified();
if ((lastmodified != -1) && address.getZone().shouldInvalidate()) {
resource = address.openResource();
if (resource.getLastModified() > lastmodified) {
reload = true;
}
}
} else {
resource = address.openResource();
reload = true;
}
if (reload) {
_changed = true;
envelope = new ModuleEnvelope(_cache, address, resource);
}
_forged.insert(address, envelope);
return envelope;
}
public ModuleEnvelope forge() throws IOException, ForgingException
{
ModuleEnvelope env = forge(_address);
temper();
return env;
}
protected ModuleEnvelope forge(Address address)
throws IOException, ForgingException
{
ModuleEnvelope envelope = (ModuleEnvelope)_forged.get(address);
if (_loading.contains(address)) {
return envelope;
}
_loading.add(address);
if (envelope != null) {
_forged.remove(address);
_forged.insert(address, envelope);
} else {
envelope = load(address);
}
if (forgeImports(envelope)) {
purge(envelope);
_changed = true;
envelope = envelope.forge();
_forged.put(address, envelope);
}
_loading.remove(address);
return envelope;
}
protected ModuleEnvelope[] getEnvelopes()
{
return (ModuleEnvelope[])_forged.toArray(ModuleEnvelope.class);
}
protected void errorCheck() throws ForgingException
{
if (_listener != null) {
throw new ForgingException(_listener);
}
}
protected void temper() throws ForgingException
{
ModuleEnvelope[] e = getEnvelopes();
int n = e.length;
try {
errorCheck();
if (_changed) {
for(int i=0; i<n; i++) {
linkImports(e[i]);
}
for(int i=0; i<n; i++) {
e[i].importExternals(this);
}
Resolver resolver = new Resolver(this);
for(int i=0; i<n; i++) {
e[i].resolve(resolver);
}
errorCheck();
Verifier verifier = new Verifier(this);
for(int i=0; i<n; i++) {
e[i].verifyPassOne(verifier);
}
errorCheck();
for(int i=0; i<n; i++) {
e[i].verifyPassTwo(verifier);
}
errorCheck();
for(int i=0; i<n; i++) {
e[i].check(this);
}
errorCheck();
_loader = new MemoryLoader(_address.getZone().getCompilerPreferences());
for(int i=0; i<n; i++) {
e[i].compile(this);
}
errorCheck();
synchronized (LOAD_LOCK) {
ZoneClassLoader clsldr = _address.getZone().getClassLoader();
try {
clsldr.setMemoryLoader(_loader);
for(int i=0; i<n; i++) {
if (!e[i].loadClass()) {
error(null, "Loading of "+e[i].getAddress()+" failed");
}
}
} finally {
clsldr.setMemoryLoader(null);
}
errorCheck();
for(int i=0; i<n; i++) {
linkImports(e[i]);
}
_cache.put(e);
}
}
for(int i=0; i<n; i++) {
e[i].init(this);
}
errorCheck();
} finally {
if (_loader != null) {
_loader.destroy();
}
}
}
public OutputStream createClassRoom(String name) throws IOException
{
return _loader.createClassRoom(name);
}
}