Moved debug feature into a single package.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/6939fd07 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/6939fd07 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/6939fd07 Branch: refs/heads/3 Commit: 6939fd075a3188594d94582fac892b032eb92d2d Parents: 64ca9c2 Author: ddekany <[email protected]> Authored: Wed Mar 1 19:38:54 2017 +0100 Committer: ddekany <[email protected]> Committed: Wed Mar 1 19:38:54 2017 +0100 ---------------------------------------------------------------------- .../apache/freemarker/core/ASTDebugBreak.java | 4 +- .../apache/freemarker/core/Configuration.java | 13 +- .../org/apache/freemarker/core/Template.java | 6 +- .../freemarker/core/debug/DebuggerClient.java | 1 - .../freemarker/core/debug/DebuggerServer.java | 130 +++++++ .../core/debug/RmiDebugModelImpl.java | 164 +++++++++ .../core/debug/RmiDebuggedEnvironmentImpl.java | 335 ++++++++++++++++++ .../freemarker/core/debug/RmiDebuggerImpl.java | 86 +++++ .../core/debug/RmiDebuggerListenerImpl.java | 67 ++++ .../core/debug/RmiDebuggerService.java | 307 +++++++++++++++++ .../apache/freemarker/core/debug/SoftCache.java | 89 +++++ .../freemarker/core/debug/_DebuggerService.java | 93 +++++ .../core/debug/impl/DebuggerServer.java | 130 ------- .../core/debug/impl/DebuggerService.java | 93 ----- .../core/debug/impl/RmiDebugModelImpl.java | 165 --------- .../debug/impl/RmiDebuggedEnvironmentImpl.java | 337 ------------------- .../core/debug/impl/RmiDebuggerImpl.java | 90 ----- .../debug/impl/RmiDebuggerListenerImpl.java | 67 ---- .../core/debug/impl/RmiDebuggerService.java | 310 ----------------- .../freemarker/core/debug/impl/SoftCache.java | 89 ----- 20 files changed, 1288 insertions(+), 1288 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java b/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java index b85c10b..fe42f41 100644 --- a/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java +++ b/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java @@ -21,7 +21,7 @@ package org.apache.freemarker.core; import java.io.IOException; -import org.apache.freemarker.core.debug.impl.DebuggerService; +import org.apache.freemarker.core.debug._DebuggerService; /** * AST node: A debug breakpoint @@ -34,7 +34,7 @@ class ASTDebugBreak extends ASTElement { @Override protected ASTElement[] accept(Environment env) throws TemplateException, IOException { - if (!DebuggerService.suspendEnvironment( + if (!_DebuggerService.suspendEnvironment( env, getTemplate().getSourceName(), getChild(0).getBeginLine())) { return getChild(0).accept(env); } else { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/Configuration.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/Configuration.java b/src/main/java/org/apache/freemarker/core/Configuration.java index b85dacc..6b256b6 100644 --- a/src/main/java/org/apache/freemarker/core/Configuration.java +++ b/src/main/java/org/apache/freemarker/core/Configuration.java @@ -36,6 +36,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.TimeZone; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -489,6 +490,11 @@ public class Configuration extends Configurable implements Cloneable, ParserConf loadBuiltInSharedVariables(); } + @Override + public void setTimeZone(TimeZone timeZone) { + super.setTimeZone(timeZone); + } + private void createTemplateResolver() { templateResolver = new DefaultTemplateResolver( null, @@ -500,7 +506,12 @@ public class Configuration extends Configurable implements Cloneable, ParserConf templateResolver.clearTemplateCache(); // for fully BC behavior templateResolver.setTemplateUpdateDelayMilliseconds(5000); } - + + @Override + public TemplateExceptionHandler getTemplateExceptionHandler() { + return super.getTemplateExceptionHandler(); + } + private void recreateTemplateResolverWith( TemplateLoader loader, CacheStorage storage, TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat templateNameFormat, http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/Template.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/Template.java b/src/main/java/org/apache/freemarker/core/Template.java index 87940a8..7b981af 100644 --- a/src/main/java/org/apache/freemarker/core/Template.java +++ b/src/main/java/org/apache/freemarker/core/Template.java @@ -37,7 +37,7 @@ import java.util.List; import java.util.Map; import java.util.Vector; -import org.apache.freemarker.core.debug.impl.DebuggerService; +import org.apache.freemarker.core.debug._DebuggerService; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.TemplateHashModel; import org.apache.freemarker.core.model.TemplateModel; @@ -279,7 +279,7 @@ public class Template extends Configurable { // Throws any exception that JavaCC has silently treated as EOF: ltbReader.throwFailure(); - DebuggerService.registerTemplate(this); + _DebuggerService.registerTemplate(this); namespaceURIToPrefixLookup = Collections.unmodifiableMap(namespaceURIToPrefixLookup); prefixToNamespaceURILookup = Collections.unmodifiableMap(prefixToNamespaceURILookup); } @@ -318,7 +318,7 @@ public class Template extends Configurable { ((ASTStaticText) template.rootElement).replaceText(content); template.setEncoding(encoding); - DebuggerService.registerTemplate(template); + _DebuggerService.registerTemplate(template); return template; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java b/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java index 1aa740d..a4612e2 100644 --- a/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java +++ b/src/main/java/org/apache/freemarker/core/debug/DebuggerClient.java @@ -30,7 +30,6 @@ import java.security.MessageDigest; import java.util.Collection; import java.util.List; -import org.apache.freemarker.core.debug.impl.RmiDebuggerListenerImpl; import org.apache.freemarker.core.util.UndeclaredThrowableException; /** http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/DebuggerServer.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/DebuggerServer.java b/src/main/java/org/apache/freemarker/core/debug/DebuggerServer.java new file mode 100644 index 0000000..d142d16 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/debug/DebuggerServer.java @@ -0,0 +1,130 @@ +/* + * 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 org.apache.freemarker.core.debug; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.ServerSocket; +import java.net.Socket; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Random; + +import org.apache.freemarker.core._CoreLogs; +import org.apache.freemarker.core.debug.Debugger; +import org.apache.freemarker.core.util.UndeclaredThrowableException; +import org.apache.freemarker.core.util._SecurityUtil; +import org.slf4j.Logger; + +/** + */ +class DebuggerServer { + + private static final Logger LOG = _CoreLogs.DEBUG_SERVER; + + // TODO: Eventually replace with Yarrow + // TODO: Can be extremely slow (on Linux, not enough entropy) + private static final Random R = new SecureRandom(); + + private final byte[] password; + private final int port; + private final Serializable debuggerStub; + private boolean stop = false; + private ServerSocket serverSocket; + + public DebuggerServer(Serializable debuggerStub) { + port = _SecurityUtil.getSystemProperty("org.apache.freemarker.core.debug.port", Debugger.DEFAULT_PORT).intValue(); + try { + password = _SecurityUtil.getSystemProperty("org.apache.freemarker.core.debug.password", "").getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new UndeclaredThrowableException(e); + } + this.debuggerStub = debuggerStub; + } + + public void start() { + new Thread(new Runnable() + { + @Override + public void run() { + startInternal(); + } + }, "FreeMarker Debugger Server Acceptor").start(); + } + + private void startInternal() { + try { + serverSocket = new ServerSocket(port); + while (!stop) { + Socket s = serverSocket.accept(); + new Thread(new DebuggerAuthProtocol(s)).start(); + } + } catch (IOException e) { + LOG.error("Debugger server shut down.", e); + } + } + + private class DebuggerAuthProtocol implements Runnable { + private final Socket s; + + DebuggerAuthProtocol(Socket s) { + this.s = s; + } + + @Override + public void run() { + try { + ObjectOutputStream out = new ObjectOutputStream(s.getOutputStream()); + ObjectInputStream in = new ObjectInputStream(s.getInputStream()); + byte[] challenge = new byte[512]; + R.nextBytes(challenge); + out.writeInt(220); // protocol version + out.writeObject(challenge); + MessageDigest md = MessageDigest.getInstance("SHA"); + md.update(password); + md.update(challenge); + byte[] response = (byte[]) in.readObject(); + if (Arrays.equals(response, md.digest())) { + out.writeObject(debuggerStub); + } else { + out.writeObject(null); + } + } catch (Exception e) { + LOG.warn("Connection to {} abruptly broke", s.getInetAddress().getHostAddress(), e); + } + } + + } + + public void stop() { + stop = true; + if (serverSocket != null) { + try { + serverSocket.close(); + } catch (IOException e) { + LOG.error("Unable to close server socket.", e); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java b/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java new file mode 100644 index 0000000..bb11db3 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java @@ -0,0 +1,164 @@ +/* + * 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 org.apache.freemarker.core.debug; + +import java.rmi.RemoteException; +import java.rmi.server.UnicastRemoteObject; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.apache.freemarker.core.model.TemplateBooleanModel; +import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateDateModel; +import org.apache.freemarker.core.model.TemplateHashModel; +import org.apache.freemarker.core.model.TemplateHashModelEx; +import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.model.TemplateMethodModelEx; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateModelIterator; +import org.apache.freemarker.core.model.TemplateNumberModel; +import org.apache.freemarker.core.model.TemplateScalarModel; +import org.apache.freemarker.core.model.TemplateSequenceModel; +import org.apache.freemarker.core.model.TemplateTransformModel; + +/** + */ +class RmiDebugModelImpl extends UnicastRemoteObject implements DebugModel { + private static final long serialVersionUID = 1L; + + private final TemplateModel model; + private final int type; + + RmiDebugModelImpl(TemplateModel model, int extraTypes) throws RemoteException { + super(); + this.model = model; + type = calculateType(model) + extraTypes; + } + + private static DebugModel getDebugModel(TemplateModel tm) throws RemoteException { + return (DebugModel) RmiDebuggedEnvironmentImpl.getCachedWrapperFor(tm); + } + @Override + public String getAsString() throws TemplateModelException { + return ((TemplateScalarModel) model).getAsString(); + } + + @Override + public Number getAsNumber() throws TemplateModelException { + return ((TemplateNumberModel) model).getAsNumber(); + } + + @Override + public Date getAsDate() throws TemplateModelException { + return ((TemplateDateModel) model).getAsDate(); + } + + @Override + public int getDateType() { + return ((TemplateDateModel) model).getDateType(); + } + + @Override + public boolean getAsBoolean() throws TemplateModelException { + return ((TemplateBooleanModel) model).getAsBoolean(); + } + + @Override + public int size() throws TemplateModelException { + if (model instanceof TemplateSequenceModel) { + return ((TemplateSequenceModel) model).size(); + } + return ((TemplateHashModelEx) model).size(); + } + + @Override + public DebugModel get(int index) throws TemplateModelException, RemoteException { + return getDebugModel(((TemplateSequenceModel) model).get(index)); + } + + @Override + public DebugModel[] get(int fromIndex, int toIndex) throws TemplateModelException, RemoteException { + DebugModel[] dm = new DebugModel[toIndex - fromIndex]; + TemplateSequenceModel s = (TemplateSequenceModel) model; + for (int i = fromIndex; i < toIndex; i++) { + dm[i - fromIndex] = getDebugModel(s.get(i)); + } + return dm; + } + + @Override + public DebugModel[] getCollection() throws TemplateModelException, RemoteException { + List list = new ArrayList(); + TemplateModelIterator i = ((TemplateCollectionModel) model).iterator(); + while (i.hasNext()) { + list.add(getDebugModel(i.next())); + } + return (DebugModel[]) list.toArray(new DebugModel[list.size()]); + } + + @Override + public DebugModel get(String key) throws TemplateModelException, RemoteException { + return getDebugModel(((TemplateHashModel) model).get(key)); + } + + @Override + public DebugModel[] get(String[] keys) throws TemplateModelException, RemoteException { + DebugModel[] dm = new DebugModel[keys.length]; + TemplateHashModel h = (TemplateHashModel) model; + for (int i = 0; i < keys.length; i++) { + dm[i] = getDebugModel(h.get(keys[i])); + } + return dm; + } + + @Override + public String[] keys() throws TemplateModelException { + TemplateHashModelEx h = (TemplateHashModelEx) model; + List list = new ArrayList(); + TemplateModelIterator i = h.keys().iterator(); + while (i.hasNext()) { + list.add(((TemplateScalarModel) i.next()).getAsString()); + } + return (String[]) list.toArray(new String[list.size()]); + } + + @Override + public int getModelTypes() { + return type; + } + + private static int calculateType(TemplateModel model) { + int type = 0; + if (model instanceof TemplateScalarModel) type += TYPE_SCALAR; + if (model instanceof TemplateNumberModel) type += TYPE_NUMBER; + if (model instanceof TemplateDateModel) type += TYPE_DATE; + if (model instanceof TemplateBooleanModel) type += TYPE_BOOLEAN; + if (model instanceof TemplateSequenceModel) type += TYPE_SEQUENCE; + if (model instanceof TemplateCollectionModel) type += TYPE_COLLECTION; + if (model instanceof TemplateHashModelEx) type += TYPE_HASH_EX; + else if (model instanceof TemplateHashModel) type += TYPE_HASH; + if (model instanceof TemplateMethodModelEx) type += TYPE_METHOD_EX; + else if (model instanceof TemplateMethodModel) type += TYPE_METHOD; + if (model instanceof TemplateTransformModel) type += TYPE_TRANSFORM; + return type; + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java new file mode 100644 index 0000000..3229c81 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java @@ -0,0 +1,335 @@ +/* + * 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 org.apache.freemarker.core.debug; + +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 org.apache.freemarker.core.Configurable; +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.Template; +import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateHashModelEx; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.impl.SimpleCollection; +import org.apache.freemarker.core.model.impl.SimpleScalar; +import org.apache.freemarker.core.util.UndeclaredThrowableException; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +@SuppressWarnings("serial") +class RmiDebuggedEnvironmentImpl extends RmiDebugModelImpl implements DebuggedEnvironment { + + private static final SoftCache CACHE = new SoftCache(new IdentityHashMap()); + private static final Object ID_LOCK = 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 (ID_LOCK) { + id = nextId++; + } + } + + static synchronized Object getCachedWrapperFor(Object key) + throws RemoteException { + Object value = CACHE.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) { + CACHE.put(key, value); + } + if (value instanceof Remote) { + remotes.add(value); + } + return value; + } + + // TODO See in SuppressFBWarnings + @Override + @SuppressFBWarnings(value="NN_NAKED_NOTIFY", justification="Will have to be re-desigend; postponed.") + public void resume() { + synchronized (this) { + notify(); + } + } + + @Override + public void stop() { + stopped = true; + resume(); + } + + @Override + public long getId() { + return id; + } + + boolean isStopped() { + return stopped; + } + + private abstract static class DebugMapModel implements TemplateHashModelEx { + @Override + public int size() { + return keySet().size(); + } + + @Override + public TemplateCollectionModel keys() { + return new SimpleCollection(keySet()); + } + + @Override + 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); + } + + @Override + 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( + Configurable.ARITHMETIC_ENGINE_KEY, + Configurable.BOOLEAN_FORMAT_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; + } + + @Override + public TemplateModel get(String key) throws TemplateModelException { + return null; // TODO + } + + } + + 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(); + } + + @Override + 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("configuration", "name")); + + private final SimpleScalar name; + + DebugTemplateModel(Template template) { + super(template); + 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( + "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); + } + } + + @Override + 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 ("mainTemplate".equals(key)) { + try { + return (TemplateModel) getCachedWrapperFor(((Environment) configurable).getMainTemplate()); + } catch (RemoteException e) { + throw new TemplateModelException(e); + } + } + if ("currentTemplate".equals(key)) { + try { + return (TemplateModel) getCachedWrapperFor(((Environment) configurable).getCurrentTemplate()); + } 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) { + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerImpl.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerImpl.java b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerImpl.java new file mode 100644 index 0000000..ea54e4e --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerImpl.java @@ -0,0 +1,86 @@ +/* + * 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 org.apache.freemarker.core.debug; + +import java.rmi.RemoteException; +import java.rmi.server.UnicastRemoteObject; +import java.util.Collection; +import java.util.List; + +/** + */ +class RmiDebuggerImpl +extends + UnicastRemoteObject +implements + Debugger { + private static final long serialVersionUID = 1L; + + private final RmiDebuggerService service; + + protected RmiDebuggerImpl(RmiDebuggerService service) throws RemoteException { + this.service = service; + } + + @Override + public void addBreakpoint(Breakpoint breakpoint) { + service.addBreakpoint(breakpoint); + } + + @Override + public Object addDebuggerListener(DebuggerListener listener) { + return service.addDebuggerListener(listener); + } + + @Override + public List getBreakpoints() { + return service.getBreakpointsSpi(); + } + + @Override + public List getBreakpoints(String templateName) { + return service.getBreakpointsSpi(templateName); + } + + @Override + public Collection getSuspendedEnvironments() { + return service.getSuspendedEnvironments(); + } + + @Override + public void removeBreakpoint(Breakpoint breakpoint) { + service.removeBreakpoint(breakpoint); + } + + @Override + public void removeDebuggerListener(Object id) { + service.removeDebuggerListener(id); + } + + @Override + public void removeBreakpoints() { + service.removeBreakpoints(); + } + + @Override + public void removeBreakpoints(String templateName) { + service.removeBreakpoints(templateName); + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerListenerImpl.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerListenerImpl.java b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerListenerImpl.java new file mode 100644 index 0000000..7e263d8 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerListenerImpl.java @@ -0,0 +1,67 @@ +/* + * 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 org.apache.freemarker.core.debug; + +import java.rmi.NoSuchObjectException; +import java.rmi.RemoteException; +import java.rmi.server.UnicastRemoteObject; +import java.rmi.server.Unreferenced; + +import org.apache.freemarker.core._CoreLogs; +import org.apache.freemarker.core.debug.DebuggerClient; +import org.apache.freemarker.core.debug.DebuggerListener; +import org.apache.freemarker.core.debug.EnvironmentSuspendedEvent; +import org.slf4j.Logger; + +/** + * Used by the {@link DebuggerClient} to create local + */ +class RmiDebuggerListenerImpl +extends + UnicastRemoteObject +implements + DebuggerListener, Unreferenced { + + private static final Logger LOG = _CoreLogs.DEBUG_CLIENT; + + private static final long serialVersionUID = 1L; + + private final DebuggerListener listener; + + @Override + public void unreferenced() { + try { + UnicastRemoteObject.unexportObject(this, false); + } catch (NoSuchObjectException e) { + LOG.warn("Failed to unexport RMI debugger listener", e); + } + } + + public RmiDebuggerListenerImpl(DebuggerListener listener) + throws RemoteException { + this.listener = listener; + } + + @Override + public void environmentSuspended(EnvironmentSuspendedEvent e) + throws RemoteException { + listener.environmentSuspended(e); + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerService.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerService.java b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerService.java new file mode 100644 index 0000000..afa0ec0 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerService.java @@ -0,0 +1,307 @@ +/* + * 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 org.apache.freemarker.core.debug; + +import java.io.Serializable; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.rmi.RemoteException; +import java.rmi.server.RemoteObject; +import java.rmi.server.UnicastRemoteObject; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.Template; +import org.apache.freemarker.core._Debug; +import org.apache.freemarker.core.util.UndeclaredThrowableException; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +/** + * @version $Id + */ +class RmiDebuggerService +extends + _DebuggerService { + private final Map templateDebugInfos = new HashMap(); + private final HashSet suspendedEnvironments = new HashSet(); + private final Map listeners = new HashMap(); + private final ReferenceQueue refQueue = new ReferenceQueue(); + + + private final RmiDebuggerImpl debugger; + private DebuggerServer server; + + RmiDebuggerService() { + try { + debugger = new RmiDebuggerImpl(this); + server = new DebuggerServer((Serializable) RemoteObject.toStub(debugger)); + server.start(); + } catch (RemoteException e) { + e.printStackTrace(); + throw new UndeclaredThrowableException(e); + } + } + + @Override + List getBreakpointsSpi(String templateName) { + synchronized (templateDebugInfos) { + TemplateDebugInfo tdi = findTemplateDebugInfo(templateName); + return tdi == null ? Collections.EMPTY_LIST : tdi.breakpoints; + } + } + + List getBreakpointsSpi() { + List sumlist = new ArrayList(); + synchronized (templateDebugInfos) { + for (Iterator iter = templateDebugInfos.values().iterator(); iter.hasNext(); ) { + sumlist.addAll(((TemplateDebugInfo) iter.next()).breakpoints); + } + } + Collections.sort(sumlist); + return sumlist; + } + + // TODO See in SuppressFBWarnings + @Override + @SuppressFBWarnings(value={ "UW_UNCOND_WAIT", "WA_NOT_IN_LOOP" }, justification="Will have to be re-desigend; postponed.") + boolean suspendEnvironmentSpi(Environment env, String templateName, int line) + throws RemoteException { + RmiDebuggedEnvironmentImpl denv = + (RmiDebuggedEnvironmentImpl) + RmiDebuggedEnvironmentImpl.getCachedWrapperFor(env); + + synchronized (suspendedEnvironments) { + suspendedEnvironments.add(denv); + } + try { + EnvironmentSuspendedEvent breakpointEvent = + new EnvironmentSuspendedEvent(this, templateName, line, denv); + + synchronized (listeners) { + for (Iterator iter = listeners.values().iterator(); iter.hasNext(); ) { + DebuggerListener listener = (DebuggerListener) iter.next(); + listener.environmentSuspended(breakpointEvent); + } + } + synchronized (denv) { + try { + denv.wait(); + } catch (InterruptedException e) { + // Intentionally ignored + } + } + return denv.isStopped(); + } finally { + synchronized (suspendedEnvironments) { + suspendedEnvironments.remove(denv); + } + } + } + + @Override + void registerTemplateSpi(Template template) { + String templateName = template.getName(); + synchronized (templateDebugInfos) { + TemplateDebugInfo tdi = createTemplateDebugInfo(templateName); + tdi.templates.add(new TemplateReference(templateName, template, refQueue)); + // Inject already defined breakpoints into the template + for (Iterator iter = tdi.breakpoints.iterator(); iter.hasNext(); ) { + Breakpoint breakpoint = (Breakpoint) iter.next(); + _Debug.insertDebugBreak(template, breakpoint.getLine()); + } + } + } + + Collection getSuspendedEnvironments() { + return (Collection) suspendedEnvironments.clone(); + } + + Object addDebuggerListener(DebuggerListener listener) { + Object id; + synchronized (listeners) { + id = Long.valueOf(System.currentTimeMillis()); + listeners.put(id, listener); + } + return id; + } + + void removeDebuggerListener(Object id) { + synchronized (listeners) { + listeners.remove(id); + } + } + + void addBreakpoint(Breakpoint breakpoint) { + String templateName = breakpoint.getTemplateName(); + synchronized (templateDebugInfos) { + TemplateDebugInfo tdi = createTemplateDebugInfo(templateName); + List breakpoints = tdi.breakpoints; + int pos = Collections.binarySearch(breakpoints, breakpoint); + if (pos < 0) { + // Add to the list of breakpoints + breakpoints.add(-pos - 1, breakpoint); + // Inject the breakpoint into all templates with this name + for (Iterator iter = tdi.templates.iterator(); iter.hasNext(); ) { + TemplateReference ref = (TemplateReference) iter.next(); + Template t = ref.getTemplate(); + if (t == null) { + iter.remove(); + } else { + _Debug.insertDebugBreak(t, breakpoint.getLine()); + } + } + } + } + } + + private TemplateDebugInfo findTemplateDebugInfo(String templateName) { + processRefQueue(); + return (TemplateDebugInfo) templateDebugInfos.get(templateName); + } + + private TemplateDebugInfo createTemplateDebugInfo(String templateName) { + TemplateDebugInfo tdi = findTemplateDebugInfo(templateName); + if (tdi == null) { + tdi = new TemplateDebugInfo(); + templateDebugInfos.put(templateName, tdi); + } + return tdi; + } + + void removeBreakpoint(Breakpoint breakpoint) { + String templateName = breakpoint.getTemplateName(); + synchronized (templateDebugInfos) { + TemplateDebugInfo tdi = findTemplateDebugInfo(templateName); + if (tdi != null) { + List breakpoints = tdi.breakpoints; + int pos = Collections.binarySearch(breakpoints, breakpoint); + if (pos >= 0) { + breakpoints.remove(pos); + for (Iterator iter = tdi.templates.iterator(); iter.hasNext(); ) { + TemplateReference ref = (TemplateReference) iter.next(); + Template t = ref.getTemplate(); + if (t == null) { + iter.remove(); + } else { + _Debug.removeDebugBreak(t, breakpoint.getLine()); + } + } + } + if (tdi.isEmpty()) { + templateDebugInfos.remove(templateName); + } + } + } + } + + void removeBreakpoints(String templateName) { + synchronized (templateDebugInfos) { + TemplateDebugInfo tdi = findTemplateDebugInfo(templateName); + if (tdi != null) { + removeBreakpoints(tdi); + if (tdi.isEmpty()) { + templateDebugInfos.remove(templateName); + } + } + } + } + + void removeBreakpoints() { + synchronized (templateDebugInfos) { + for (Iterator iter = templateDebugInfos.values().iterator(); iter.hasNext(); ) { + TemplateDebugInfo tdi = (TemplateDebugInfo) iter.next(); + removeBreakpoints(tdi); + if (tdi.isEmpty()) { + iter.remove(); + } + } + } + } + + private void removeBreakpoints(TemplateDebugInfo tdi) { + tdi.breakpoints.clear(); + for (Iterator iter = tdi.templates.iterator(); iter.hasNext(); ) { + TemplateReference ref = (TemplateReference) iter.next(); + Template t = ref.getTemplate(); + if (t == null) { + iter.remove(); + } else { + _Debug.removeDebugBreaks(t); + } + } + } + + private static final class TemplateDebugInfo { + final List templates = new ArrayList(); + final List breakpoints = new ArrayList(); + + boolean isEmpty() { + return templates.isEmpty() && breakpoints.isEmpty(); + } + } + + private static final class TemplateReference extends WeakReference { + final String templateName; + + TemplateReference(String templateName, Template template, ReferenceQueue queue) { + super(template, queue); + this.templateName = templateName; + } + + Template getTemplate() { + return (Template) get(); + } + } + + private void processRefQueue() { + for (; ; ) { + TemplateReference ref = (TemplateReference) refQueue.poll(); + if (ref == null) { + break; + } + TemplateDebugInfo tdi = findTemplateDebugInfo(ref.templateName); + if (tdi != null) { + tdi.templates.remove(ref); + if (tdi.isEmpty()) { + templateDebugInfos.remove(ref.templateName); + } + } + } + } + + @Override + void shutdownSpi() { + server.stop(); + try { + UnicastRemoteObject.unexportObject(debugger, true); + } catch (Exception e) { + } + + RmiDebuggedEnvironmentImpl.cleanup(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/SoftCache.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/SoftCache.java b/src/main/java/org/apache/freemarker/core/debug/SoftCache.java new file mode 100644 index 0000000..730574a --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/debug/SoftCache.java @@ -0,0 +1,89 @@ +/* + * 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 org.apache.freemarker.core.debug; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.Map; + +class SoftCache { + + private final ReferenceQueue queue = new ReferenceQueue(); + private final Map map; + + public SoftCache(Map backingMap) { + map = backingMap; + } + + public Object get(Object key) { + processQueue(); + Reference ref = (Reference) map.get(key); + return ref == null ? null : ref.get(); + } + + public void put(Object key, Object value) { + processQueue(); + map.put(key, new SoftValueReference(key, value, queue)); + } + + public void remove(Object key) { + processQueue(); + map.remove(key); + } + + public void clear() { + map.clear(); + processQueue(); + } + + /** + * Returns a close approximation of the number of cache entries. + */ + public int getSize() { + processQueue(); + return map.size(); + } + + private void processQueue() { + for (; ; ) { + SoftValueReference ref = (SoftValueReference) queue.poll(); + if (ref == null) { + return; + } + Object key = ref.getKey(); + map.remove(key); + } + } + + private static final class SoftValueReference extends SoftReference { + private final Object key; + + SoftValueReference(Object key, Object value, ReferenceQueue queue) { + super(value, queue); + this.key = key; + } + + Object getKey() { + return key; + } + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/_DebuggerService.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/_DebuggerService.java b/src/main/java/org/apache/freemarker/core/debug/_DebuggerService.java new file mode 100644 index 0000000..37d094c --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/debug/_DebuggerService.java @@ -0,0 +1,93 @@ +/* + * 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 org.apache.freemarker.core.debug; + +import java.rmi.RemoteException; +import java.util.Collections; +import java.util.List; + +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.Template; +import org.apache.freemarker.core.util._SecurityUtil; + +/** + * Don't use this; used internally by FreeMarker, might changes without notice. + * This class provides debugging hooks for the core FreeMarker engine. It is + * not usable for anyone outside the FreeMarker core classes. + */ +public abstract class _DebuggerService { + private static final _DebuggerService INSTANCE = createInstance(); + + private static _DebuggerService createInstance() { + // Creates the appropriate service class. If the debugging is turned + // off, this is a fast no-op service, otherwise it's the real-thing + // RMI service. + return + _SecurityUtil.getSystemProperty("org.apache.freemarker.core.debug.password", null) == null + ? new NoOpDebuggerService() + : new RmiDebuggerService(); + } + + public static List getBreakpoints(String templateName) { + return INSTANCE.getBreakpointsSpi(templateName); + } + + abstract List getBreakpointsSpi(String templateName); + + public static void registerTemplate(Template template) { + INSTANCE.registerTemplateSpi(template); + } + + abstract void registerTemplateSpi(Template template); + + public static boolean suspendEnvironment(Environment env, String templateName, int line) + throws RemoteException { + return INSTANCE.suspendEnvironmentSpi(env, templateName, line); + } + + abstract boolean suspendEnvironmentSpi(Environment env, String templateName, int line) + throws RemoteException; + + abstract void shutdownSpi(); + + public static void shutdown() { + INSTANCE.shutdownSpi(); + } + + private static class NoOpDebuggerService extends _DebuggerService { + @Override + List getBreakpointsSpi(String templateName) { + return Collections.EMPTY_LIST; + } + + @Override + boolean suspendEnvironmentSpi(Environment env, String templateName, int line) { + throw new UnsupportedOperationException(); + } + + @Override + void registerTemplateSpi(Template template) { + } + + @Override + void shutdownSpi() { + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/impl/DebuggerServer.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/impl/DebuggerServer.java b/src/main/java/org/apache/freemarker/core/debug/impl/DebuggerServer.java deleted file mode 100644 index d664695..0000000 --- a/src/main/java/org/apache/freemarker/core/debug/impl/DebuggerServer.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 org.apache.freemarker.core.debug.impl; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.io.UnsupportedEncodingException; -import java.net.ServerSocket; -import java.net.Socket; -import java.security.MessageDigest; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Random; - -import org.apache.freemarker.core._CoreLogs; -import org.apache.freemarker.core.debug.Debugger; -import org.apache.freemarker.core.util.UndeclaredThrowableException; -import org.apache.freemarker.core.util._SecurityUtil; -import org.slf4j.Logger; - -/** - */ -class DebuggerServer { - - private static final Logger LOG = _CoreLogs.DEBUG_SERVER; - - // TODO: Eventually replace with Yarrow - // TODO: Can be extremely slow (on Linux, not enough entropy) - private static final Random R = new SecureRandom(); - - private final byte[] password; - private final int port; - private final Serializable debuggerStub; - private boolean stop = false; - private ServerSocket serverSocket; - - public DebuggerServer(Serializable debuggerStub) { - port = _SecurityUtil.getSystemProperty("org.apache.freemarker.core.debug.port", Debugger.DEFAULT_PORT).intValue(); - try { - password = _SecurityUtil.getSystemProperty("org.apache.freemarker.core.debug.password", "").getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new UndeclaredThrowableException(e); - } - this.debuggerStub = debuggerStub; - } - - public void start() { - new Thread(new Runnable() - { - @Override - public void run() { - startInternal(); - } - }, "FreeMarker Debugger Server Acceptor").start(); - } - - private void startInternal() { - try { - serverSocket = new ServerSocket(port); - while (!stop) { - Socket s = serverSocket.accept(); - new Thread(new DebuggerAuthProtocol(s)).start(); - } - } catch (IOException e) { - LOG.error("Debugger server shut down.", e); - } - } - - private class DebuggerAuthProtocol implements Runnable { - private final Socket s; - - DebuggerAuthProtocol(Socket s) { - this.s = s; - } - - @Override - public void run() { - try { - ObjectOutputStream out = new ObjectOutputStream(s.getOutputStream()); - ObjectInputStream in = new ObjectInputStream(s.getInputStream()); - byte[] challenge = new byte[512]; - R.nextBytes(challenge); - out.writeInt(220); // protocol version - out.writeObject(challenge); - MessageDigest md = MessageDigest.getInstance("SHA"); - md.update(password); - md.update(challenge); - byte[] response = (byte[]) in.readObject(); - if (Arrays.equals(response, md.digest())) { - out.writeObject(debuggerStub); - } else { - out.writeObject(null); - } - } catch (Exception e) { - LOG.warn("Connection to {} abruply broke", s.getInetAddress().getHostAddress(), e); - } - } - - } - - public void stop() { - stop = true; - if (serverSocket != null) { - try { - serverSocket.close(); - } catch (IOException e) { - LOG.error("Unable to close server socket.", e); - } - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/impl/DebuggerService.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/impl/DebuggerService.java b/src/main/java/org/apache/freemarker/core/debug/impl/DebuggerService.java deleted file mode 100644 index 65ccb27..0000000 --- a/src/main/java/org/apache/freemarker/core/debug/impl/DebuggerService.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 org.apache.freemarker.core.debug.impl; - -import java.rmi.RemoteException; -import java.util.Collections; -import java.util.List; - -import org.apache.freemarker.core.Environment; -import org.apache.freemarker.core.Template; -import org.apache.freemarker.core.util._SecurityUtil; - -/** - * This class provides debugging hooks for the core FreeMarker engine. It is - * not usable for anyone outside the FreeMarker core classes. It is public only - * as an implementation detail. - */ -public abstract class DebuggerService { - private static final DebuggerService instance = createInstance(); - - private static DebuggerService createInstance() { - // Creates the appropriate service class. If the debugging is turned - // off, this is a fast no-op service, otherwise it's the real-thing - // RMI service. - return - _SecurityUtil.getSystemProperty("org.apache.freemarker.core.debug.password", null) == null - ? new NoOpDebuggerService() - : new RmiDebuggerService(); - } - - public static List getBreakpoints(String templateName) { - return instance.getBreakpointsSpi(templateName); - } - - abstract List getBreakpointsSpi(String templateName); - - public static void registerTemplate(Template template) { - instance.registerTemplateSpi(template); - } - - abstract void registerTemplateSpi(Template template); - - public static boolean suspendEnvironment(Environment env, String templateName, int line) - throws RemoteException { - return instance.suspendEnvironmentSpi(env, templateName, line); - } - - abstract boolean suspendEnvironmentSpi(Environment env, String templateName, int line) - throws RemoteException; - - abstract void shutdownSpi(); - - public static void shutdown() { - instance.shutdownSpi(); - } - - private static class NoOpDebuggerService extends DebuggerService { - @Override - List getBreakpointsSpi(String templateName) { - return Collections.EMPTY_LIST; - } - - @Override - boolean suspendEnvironmentSpi(Environment env, String templateName, int line) { - throw new UnsupportedOperationException(); - } - - @Override - void registerTemplateSpi(Template template) { - } - - @Override - void shutdownSpi() { - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebugModelImpl.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebugModelImpl.java b/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebugModelImpl.java deleted file mode 100644 index 97da734..0000000 --- a/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebugModelImpl.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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 org.apache.freemarker.core.debug.impl; - -import java.rmi.RemoteException; -import java.rmi.server.UnicastRemoteObject; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import org.apache.freemarker.core.debug.DebugModel; -import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateCollectionModel; -import org.apache.freemarker.core.model.TemplateDateModel; -import org.apache.freemarker.core.model.TemplateHashModel; -import org.apache.freemarker.core.model.TemplateHashModelEx; -import org.apache.freemarker.core.model.TemplateMethodModel; -import org.apache.freemarker.core.model.TemplateMethodModelEx; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.model.TemplateModelIterator; -import org.apache.freemarker.core.model.TemplateNumberModel; -import org.apache.freemarker.core.model.TemplateScalarModel; -import org.apache.freemarker.core.model.TemplateSequenceModel; -import org.apache.freemarker.core.model.TemplateTransformModel; - -/** - */ -class RmiDebugModelImpl extends UnicastRemoteObject implements DebugModel { - private static final long serialVersionUID = 1L; - - private final TemplateModel model; - private final int type; - - RmiDebugModelImpl(TemplateModel model, int extraTypes) throws RemoteException { - super(); - this.model = model; - type = calculateType(model) + extraTypes; - } - - private static DebugModel getDebugModel(TemplateModel tm) throws RemoteException { - return (DebugModel) RmiDebuggedEnvironmentImpl.getCachedWrapperFor(tm); - } - @Override - public String getAsString() throws TemplateModelException { - return ((TemplateScalarModel) model).getAsString(); - } - - @Override - public Number getAsNumber() throws TemplateModelException { - return ((TemplateNumberModel) model).getAsNumber(); - } - - @Override - public Date getAsDate() throws TemplateModelException { - return ((TemplateDateModel) model).getAsDate(); - } - - @Override - public int getDateType() { - return ((TemplateDateModel) model).getDateType(); - } - - @Override - public boolean getAsBoolean() throws TemplateModelException { - return ((TemplateBooleanModel) model).getAsBoolean(); - } - - @Override - public int size() throws TemplateModelException { - if (model instanceof TemplateSequenceModel) { - return ((TemplateSequenceModel) model).size(); - } - return ((TemplateHashModelEx) model).size(); - } - - @Override - public DebugModel get(int index) throws TemplateModelException, RemoteException { - return getDebugModel(((TemplateSequenceModel) model).get(index)); - } - - @Override - public DebugModel[] get(int fromIndex, int toIndex) throws TemplateModelException, RemoteException { - DebugModel[] dm = new DebugModel[toIndex - fromIndex]; - TemplateSequenceModel s = (TemplateSequenceModel) model; - for (int i = fromIndex; i < toIndex; i++) { - dm[i - fromIndex] = getDebugModel(s.get(i)); - } - return dm; - } - - @Override - public DebugModel[] getCollection() throws TemplateModelException, RemoteException { - List list = new ArrayList(); - TemplateModelIterator i = ((TemplateCollectionModel) model).iterator(); - while (i.hasNext()) { - list.add(getDebugModel(i.next())); - } - return (DebugModel[]) list.toArray(new DebugModel[list.size()]); - } - - @Override - public DebugModel get(String key) throws TemplateModelException, RemoteException { - return getDebugModel(((TemplateHashModel) model).get(key)); - } - - @Override - public DebugModel[] get(String[] keys) throws TemplateModelException, RemoteException { - DebugModel[] dm = new DebugModel[keys.length]; - TemplateHashModel h = (TemplateHashModel) model; - for (int i = 0; i < keys.length; i++) { - dm[i] = getDebugModel(h.get(keys[i])); - } - return dm; - } - - @Override - public String[] keys() throws TemplateModelException { - TemplateHashModelEx h = (TemplateHashModelEx) model; - List list = new ArrayList(); - TemplateModelIterator i = h.keys().iterator(); - while (i.hasNext()) { - list.add(((TemplateScalarModel) i.next()).getAsString()); - } - return (String[]) list.toArray(new String[list.size()]); - } - - @Override - public int getModelTypes() { - return type; - } - - private static int calculateType(TemplateModel model) { - int type = 0; - if (model instanceof TemplateScalarModel) type += TYPE_SCALAR; - if (model instanceof TemplateNumberModel) type += TYPE_NUMBER; - if (model instanceof TemplateDateModel) type += TYPE_DATE; - if (model instanceof TemplateBooleanModel) type += TYPE_BOOLEAN; - if (model instanceof TemplateSequenceModel) type += TYPE_SEQUENCE; - if (model instanceof TemplateCollectionModel) type += TYPE_COLLECTION; - if (model instanceof TemplateHashModelEx) type += TYPE_HASH_EX; - else if (model instanceof TemplateHashModel) type += TYPE_HASH; - if (model instanceof TemplateMethodModelEx) type += TYPE_METHOD_EX; - else if (model instanceof TemplateMethodModel) type += TYPE_METHOD; - if (model instanceof TemplateTransformModel) type += TYPE_TRANSFORM; - return type; - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebuggedEnvironmentImpl.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebuggedEnvironmentImpl.java b/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebuggedEnvironmentImpl.java deleted file mode 100644 index eb89830..0000000 --- a/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebuggedEnvironmentImpl.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * 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 org.apache.freemarker.core.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 org.apache.freemarker.core.Configurable; -import org.apache.freemarker.core.Configuration; -import org.apache.freemarker.core.Environment; -import org.apache.freemarker.core.Template; -import org.apache.freemarker.core.debug.DebugModel; -import org.apache.freemarker.core.debug.DebuggedEnvironment; -import org.apache.freemarker.core.model.TemplateCollectionModel; -import org.apache.freemarker.core.model.TemplateHashModelEx; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.model.impl.SimpleCollection; -import org.apache.freemarker.core.model.impl.SimpleScalar; -import org.apache.freemarker.core.util.UndeclaredThrowableException; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -@SuppressWarnings("serial") -class RmiDebuggedEnvironmentImpl extends RmiDebugModelImpl implements DebuggedEnvironment { - - private static final SoftCache CACHE = new SoftCache(new IdentityHashMap()); - private static final Object ID_LOCK = 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 (ID_LOCK) { - id = nextId++; - } - } - - static synchronized Object getCachedWrapperFor(Object key) - throws RemoteException { - Object value = CACHE.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) { - CACHE.put(key, value); - } - if (value instanceof Remote) { - remotes.add(value); - } - return value; - } - - // TODO See in SuppressFBWarnings - @Override - @SuppressFBWarnings(value="NN_NAKED_NOTIFY", justification="Will have to be re-desigend; postponed.") - public void resume() { - synchronized (this) { - notify(); - } - } - - @Override - public void stop() { - stopped = true; - resume(); - } - - @Override - public long getId() { - return id; - } - - boolean isStopped() { - return stopped; - } - - private abstract static class DebugMapModel implements TemplateHashModelEx { - @Override - public int size() { - return keySet().size(); - } - - @Override - public TemplateCollectionModel keys() { - return new SimpleCollection(keySet()); - } - - @Override - 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); - } - - @Override - 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( - Configurable.ARITHMETIC_ENGINE_KEY, - Configurable.BOOLEAN_FORMAT_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; - } - - @Override - public TemplateModel get(String key) throws TemplateModelException { - return null; // TODO - } - - } - - 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(); - } - - @Override - 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("configuration", "name")); - - private final SimpleScalar name; - - DebugTemplateModel(Template template) { - super(template); - 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( - "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); - } - } - - @Override - 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 ("mainTemplate".equals(key)) { - try { - return (TemplateModel) getCachedWrapperFor(((Environment) configurable).getMainTemplate()); - } catch (RemoteException e) { - throw new TemplateModelException(e); - } - } - if ("currentTemplate".equals(key)) { - try { - return (TemplateModel) getCachedWrapperFor(((Environment) configurable).getCurrentTemplate()); - } 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) { - } - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/6939fd07/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebuggerImpl.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebuggerImpl.java b/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebuggerImpl.java deleted file mode 100644 index 9fcb3a8..0000000 --- a/src/main/java/org/apache/freemarker/core/debug/impl/RmiDebuggerImpl.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 org.apache.freemarker.core.debug.impl; - -import java.rmi.RemoteException; -import java.rmi.server.UnicastRemoteObject; -import java.util.Collection; -import java.util.List; - -import org.apache.freemarker.core.debug.Breakpoint; -import org.apache.freemarker.core.debug.Debugger; -import org.apache.freemarker.core.debug.DebuggerListener; - -/** - */ -class RmiDebuggerImpl -extends - UnicastRemoteObject -implements - Debugger { - private static final long serialVersionUID = 1L; - - private final RmiDebuggerService service; - - protected RmiDebuggerImpl(RmiDebuggerService service) throws RemoteException { - this.service = service; - } - - @Override - public void addBreakpoint(Breakpoint breakpoint) { - service.addBreakpoint(breakpoint); - } - - @Override - public Object addDebuggerListener(DebuggerListener listener) { - return service.addDebuggerListener(listener); - } - - @Override - public List getBreakpoints() { - return service.getBreakpointsSpi(); - } - - @Override - public List getBreakpoints(String templateName) { - return service.getBreakpointsSpi(templateName); - } - - @Override - public Collection getSuspendedEnvironments() { - return service.getSuspendedEnvironments(); - } - - @Override - public void removeBreakpoint(Breakpoint breakpoint) { - service.removeBreakpoint(breakpoint); - } - - @Override - public void removeDebuggerListener(Object id) { - service.removeDebuggerListener(id); - } - - @Override - public void removeBreakpoints() { - service.removeBreakpoints(); - } - - @Override - public void removeBreakpoints(String templateName) { - service.removeBreakpoints(templateName); - } -}
