/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package freemarker.debug.impl;
import java.rmi.
Remote;
import java.rmi.
RemoteException;
import java.rmi.server.
UnicastRemoteObject;
import java.util.
ArrayList;
import java.util.
Arrays;
import java.util.
Collection;
import java.util.
Collections;
import java.util.
HashSet;
import java.util.
IdentityHashMap;
import java.util.
Iterator;
import java.util.
List;
import java.util.
Set;
import
edu.
umd.
cs.
findbugs.
annotations.
SuppressFBWarnings;
import freemarker.cache.
CacheStorage;
import freemarker.cache.
SoftCacheStorage;
import freemarker.core.
Configurable;
import freemarker.core.
Environment;
import freemarker.debug.
DebugModel;
import freemarker.debug.
DebuggedEnvironment;
import freemarker.template.
Configuration;
import freemarker.template.
SimpleCollection;
import freemarker.template.
SimpleScalar;
import freemarker.template.
Template;
import freemarker.template.
TemplateCollectionModel;
import freemarker.template.
TemplateHashModelEx;
import freemarker.template.
TemplateModel;
import freemarker.template.
TemplateModelException;
import freemarker.template.utility.
UndeclaredThrowableException;
/**
*/
class
RmiDebuggedEnvironmentImpl
extends
RmiDebugModelImpl
implements
DebuggedEnvironment {
private static final long
serialVersionUID = 1L;
private static final
CacheStorage storage = new
SoftCacheStorage(new
IdentityHashMap());
private static final
Object idLock = new
Object();
private static long
nextId = 1;
private static
Set remotes = new
HashSet();
private boolean
stopped = false;
private final long
id;
private
RmiDebuggedEnvironmentImpl(
Environment env) throws
RemoteException {
super(new
DebugEnvironmentModel(
env),
DebugModel.
TYPE_ENVIRONMENT);
synchronized (
idLock) {
id =
nextId++;
}
}
static synchronized
Object getCachedWrapperFor(
Object key)
throws
RemoteException {
Object value =
storage.
get(
key);
if (
value == null) {
if (
key instanceof
TemplateModel) {
int
extraTypes;
if (
key instanceof
DebugConfigurationModel) {
extraTypes =
DebugModel.
TYPE_CONFIGURATION;
} else if (
key instanceof
DebugTemplateModel) {
extraTypes =
DebugModel.
TYPE_TEMPLATE;
} else {
extraTypes = 0;
}
value = new
RmiDebugModelImpl((
TemplateModel)
key,
extraTypes);
} else if (
key instanceof
Environment) {
value = new
RmiDebuggedEnvironmentImpl((
Environment)
key);
} else if (
key instanceof
Template) {
value = new
DebugTemplateModel((
Template)
key);
} else if (
key instanceof
Configuration) {
value = new
DebugConfigurationModel((
Configuration)
key);
}
}
if (
value != null) {
storage.
put(
key,
value);
}
if (
value instanceof
Remote) {
remotes.
add(
value);
}
return
value;
}
// TODO See in SuppressFBWarnings
@
SuppressFBWarnings(
value="NN_NAKED_NOTIFY",
justification="Will have to be re-desigend; postponed.")
public void
resume() {
synchronized (this) {
notify();
}
}
public void
stop() {
stopped = true;
resume();
}
public long
getId() {
return
id;
}
boolean
isStopped() {
return
stopped;
}
private abstract static class
DebugMapModel implements
TemplateHashModelEx {
public int
size() {
return
keySet().
size();
}
public
TemplateCollectionModel keys() {
return new
SimpleCollection(
keySet());
}
public
TemplateCollectionModel values() throws
TemplateModelException {
Collection keys =
keySet();
List list = new
ArrayList(
keys.
size());
for (
Iterator it =
keys.
iterator();
it.
hasNext(); ) {
list.
add(
get((
String)
it.
next()));
}
return new
SimpleCollection(
list);
}
public boolean
isEmpty() {
return
size() == 0;
}
abstract
Collection keySet();
static
List composeList(
Collection c1,
Collection c2) {
List list = new
ArrayList(
c1);
list.
addAll(
c2);
Collections.
sort(
list);
return
list;
}
}
private static class
DebugConfigurableModel extends
DebugMapModel {
static final
List KEYS =
Arrays.
asList(new
String[]
{
Configurable.
ARITHMETIC_ENGINE_KEY,
Configurable.
BOOLEAN_FORMAT_KEY,
Configurable.
CLASSIC_COMPATIBLE_KEY,
Configurable.
LOCALE_KEY,
Configurable.
NUMBER_FORMAT_KEY,
Configurable.
OBJECT_WRAPPER_KEY,
Configurable.
TEMPLATE_EXCEPTION_HANDLER_KEY
});
final
Configurable configurable;
DebugConfigurableModel(
Configurable configurable) {
this.
configurable =
configurable;
}
@
Override
Collection keySet() {
return
KEYS;
}
public
TemplateModel get(
String key) throws
TemplateModelException {
String s =
configurable.
getSetting(
key);
return
s == null ? null : new
SimpleScalar(
s);
}
}
private static class
DebugConfigurationModel extends
DebugConfigurableModel {
private static final
List KEYS =
composeList(
DebugConfigurableModel.
KEYS,
Collections.
singleton("sharedVariables"));
private
TemplateModel sharedVariables = new
DebugMapModel()
{
@
Override
Collection keySet() {
return ((
Configuration)
configurable).
getSharedVariableNames();
}
public
TemplateModel get(
String key) {
return ((
Configuration)
configurable).
getSharedVariable(
key);
}
};
DebugConfigurationModel(
Configuration config) {
super(
config);
}
@
Override
Collection keySet() {
return
KEYS;
}
@
Override
public
TemplateModel get(
String key) throws
TemplateModelException {
if ("sharedVariables".
equals(
key)) {
return
sharedVariables;
} else {
return super.get(
key);
}
}
}
private static class
DebugTemplateModel extends
DebugConfigurableModel {
private static final
List KEYS =
composeList(
DebugConfigurableModel.
KEYS,
Arrays.
asList(new
String[] {
"configuration",
"name",
}));
private final
SimpleScalar name;
DebugTemplateModel(
Template template) {
super(
template);
this.
name = new
SimpleScalar(
template.
getName());
}
@
Override
Collection keySet() {
return
KEYS;
}
@
Override
public
TemplateModel get(
String key) throws
TemplateModelException {
if ("configuration".
equals(
key)) {
try {
return (
TemplateModel)
getCachedWrapperFor(((
Template)
configurable).
getConfiguration());
} catch (
RemoteException e) {
throw new
TemplateModelException(
e);
}
}
if ("name".
equals(
key)) {
return
name;
}
return super.get(
key);
}
}
private static class
DebugEnvironmentModel extends
DebugConfigurableModel {
private static final
List KEYS =
composeList(
DebugConfigurableModel.
KEYS,
Arrays.
asList(new
String[] {
"currentNamespace",
"dataModel",
"globalNamespace",
"knownVariables",
"mainNamespace",
"template",
}));
private
TemplateModel knownVariables = new
DebugMapModel()
{
@
Override
Collection keySet() {
try {
return ((
Environment)
configurable).
getKnownVariableNames();
} catch (
TemplateModelException e) {
throw new
UndeclaredThrowableException(
e);
}
}
public
TemplateModel get(
String key) throws
TemplateModelException {
return ((
Environment)
configurable).
getVariable(
key);
}
};
DebugEnvironmentModel(
Environment env) {
super(
env);
}
@
Override
Collection keySet() {
return
KEYS;
}
@
Override
public
TemplateModel get(
String key) throws
TemplateModelException {
if ("currentNamespace".
equals(
key)) {
return ((
Environment)
configurable).
getCurrentNamespace();
}
if ("dataModel".
equals(
key)) {
return ((
Environment)
configurable).
getDataModel();
}
if ("globalNamespace".
equals(
key)) {
return ((
Environment)
configurable).
getGlobalNamespace();
}
if ("knownVariables".
equals(
key)) {
return
knownVariables;
}
if ("mainNamespace".
equals(
key)) {
return ((
Environment)
configurable).
getMainNamespace();
}
if ("template".
equals(
key)) {
try {
return (
TemplateModel)
getCachedWrapperFor(((
Environment)
configurable).
getTemplate());
} catch (
RemoteException e) {
throw new
TemplateModelException(
e);
}
}
return super.get(
key);
}
}
public static void
cleanup() {
for (
Iterator i =
remotes.
iterator();
i.
hasNext(); ) {
Object remoteObject =
i.
next();
try {
UnicastRemoteObject.
unexportObject((
Remote)
remoteObject, true);
} catch (
Exception e) {
}
}
}
}