From b1c8641a63a67e3c64d948f9e8dce5c01e11e2dd Mon Sep 17 00:00:00 2001 From: mcentner Date: Wed, 5 May 2010 15:29:01 +0000 Subject: Merged feature branch mocca-1.2.13-id@r724 back to trunk. git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@725 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../egiz/bku/binding/AbstractBindingProcessor.java | 111 ++- .../binding/AbstractBindingProcessorFactory.java | 81 ++ .../at/gv/egiz/bku/binding/BindingProcessor.java | 153 +++- .../egiz/bku/binding/BindingProcessorFactory.java | 42 + .../egiz/bku/binding/BindingProcessorFuture.java | 73 ++ .../egiz/bku/binding/BindingProcessorManager.java | 138 ++-- .../bku/binding/BindingProcessorManagerImpl.java | 414 +++++----- .../egiz/bku/binding/DataURLConnectionFactory.java | 26 + .../main/java/at/gv/egiz/bku/binding/DataUrl.java | 91 +-- .../at/gv/egiz/bku/binding/DataUrlConnection.java | 134 +-- .../gv/egiz/bku/binding/DataUrlConnectionImpl.java | 340 +++----- .../gv/egiz/bku/binding/DataUrlConnectionSPI.java | 64 -- .../java/at/gv/egiz/bku/binding/ExpiryRemover.java | 67 -- .../egiz/bku/binding/FormDataURLDereferencer.java | 71 ++ .../gv/egiz/bku/binding/FormDataURLSupplier.java | 27 + .../gv/egiz/bku/binding/HTTPBindingProcessor.java | 859 +------------------- .../bku/binding/HTTPBindingProcessorFactory.java | 80 ++ .../egiz/bku/binding/HTTPBindingProcessorImpl.java | 896 +++++++++++++++++++++ .../gv/egiz/bku/binding/HttpDataURLConnection.java | 68 ++ .../main/java/at/gv/egiz/bku/binding/HttpUtil.java | 3 +- .../egiz/bku/binding/HttpsDataURLConnection.java | 72 ++ .../java/at/gv/egiz/bku/binding/IdFactory.java | 180 ++--- .../main/java/at/gv/egiz/bku/binding/IdImpl.java | 11 +- .../gv/egiz/bku/binding/InputDecoderFactory.java | 148 ++-- .../bku/binding/MultiPartFormDataInputDecoder.java | 235 +++--- .../at/gv/egiz/bku/binding/ProcessingContext.java | 59 -- .../at/gv/egiz/bku/binding/RemovalStrategy.java | 26 - .../gv/egiz/bku/binding/SLCommandInvokerImpl.java | 33 +- .../egiz/bku/binding/XWWWFormUrlInputIterator.java | 1 + 29 files changed, 2433 insertions(+), 2070 deletions(-) create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessorFactory.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorFactory.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorFuture.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/DataURLConnectionFactory.java delete mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionSPI.java delete mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/ExpiryRemover.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/FormDataURLDereferencer.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/FormDataURLSupplier.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessorFactory.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessorImpl.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpDataURLConnection.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpsDataURLConnection.java delete mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/ProcessingContext.java delete mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/RemovalStrategy.java (limited to 'bkucommon/src/main/java/at/gv/egiz/bku/binding') diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessor.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessor.java index 23f62134..5201e817 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessor.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessor.java @@ -16,74 +16,119 @@ */ package at.gv.egiz.bku.binding; -import at.gv.egiz.bku.conf.Configuration; -import java.io.InputStream; import java.util.Date; +import java.util.Locale; +import org.apache.commons.configuration.Configuration; +import org.slf4j.MDC; + +import at.gv.egiz.bku.slcommands.SLCommandFactory; import at.gv.egiz.bku.slcommands.SLCommandInvoker; +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; import at.gv.egiz.stal.STAL; public abstract class AbstractBindingProcessor implements BindingProcessor { + + protected Configuration configuration; + + protected SLCommandFactory slCommandFactory; + + protected Locale locale = Locale.getDefault(); + protected Id id; - protected Configuration config; protected STAL stal; protected SLCommandInvoker commandInvoker; + protected long lastAccessedTime = System.currentTimeMillis(); - public AbstractBindingProcessor(String idString) { - this.id = IdFactory.getInstance().createId(idString); + protected URLDereferencer urlDereferencer; + + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; } - /** - * @see java.lang.Thread#run() - */ - public abstract void run(); + @Override + public void setSlCommandFactory(SLCommandFactory slCommandFactory) { + this.slCommandFactory = slCommandFactory; + } - /** - * The caller is advised to check the result in case an error occurred. - * - * @see #getResult() - */ - public abstract void consumeRequestStream(InputStream aIs); + @Override + public void setLocale(Locale locale) { + if (locale == null) { + throw new NullPointerException("Locale must not be set to null."); + } + this.locale = locale; + } + + @Override + public void init(String id, STAL stal, SLCommandInvoker commandInvoker) { + if (id == null) { + throw new NullPointerException("Id must not be null."); + } + if (stal == null) { + throw new NullPointerException("STAL must not null."); + } + if (commandInvoker == null) { + throw new NullPointerException("CommandInvoker must null."); + } + this.id = IdFactory.getInstance().createId(id); + this.stal = stal; + this.commandInvoker = commandInvoker; + } + @Override public Id getId() { return id; } + @Override public STAL getSTAL() { return stal; } + @Override public SLCommandInvoker getCommandInvoker() { return commandInvoker; } - + + @Override public void updateLastAccessTime() { lastAccessedTime = System.currentTimeMillis(); } + @Override public Date getLastAccessTime() { return new Date(lastAccessedTime); } - /** - * To be called after object creation. - * - * @param aStal - * must not be null - * @param aCommandInvoker - * must not be null - */ - public void init(STAL aStal, SLCommandInvoker aCommandInvoker, Configuration conf) { - if (aStal == null) { - throw new NullPointerException("STAL must not be set to null"); + @Override + public void run() { + + if (this.id != null) { + MDC.put("id", this.id.toString()); } - if (aCommandInvoker == null) { - throw new NullPointerException("Commandinvoker must not be set to null"); + try { + process(); + } finally { + MDC.remove("id"); } - config = conf; - stal = aStal; - commandInvoker = aCommandInvoker; - Thread.currentThread().setName("BPID#"+getId().toString()); + + } + + public abstract void process(); + + /** + * @return the urlDereferencer + */ + public URLDereferencer getUrlDereferencer() { + return urlDereferencer; + } + + /** + * @param urlDereferencer the urlDereferencer to set + */ + public void setUrlDereferencer(URLDereferencer urlDereferencer) { + this.urlDereferencer = urlDereferencer; } + } \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessorFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessorFactory.java new file mode 100644 index 00000000..8cf71260 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/AbstractBindingProcessorFactory.java @@ -0,0 +1,81 @@ +/* +* Copyright 2009 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed 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 at.gv.egiz.bku.binding; + +import java.util.Set; + +import org.apache.commons.configuration.Configuration; + +import at.gv.egiz.bku.slcommands.SLCommandFactory; +import at.gv.egiz.bku.utils.binding.Protocol; +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; + + +public abstract class AbstractBindingProcessorFactory implements BindingProcessorFactory { + + protected Set supportedProtocols; + protected SLCommandFactory slCommandFactory; + protected Configuration configuration; + protected URLDereferencer urlDereferencer; + + @Override + public Set getSupportedProtocols() { + return supportedProtocols; + } + + @Override + public SLCommandFactory getSlCommandFactory() { + return slCommandFactory; + } + + @Override + public void setSlCommandFactory(SLCommandFactory slCommandFactory) { + this.slCommandFactory = slCommandFactory; + } + + @Override + public Configuration getConfiguration() { + return configuration; + } + + @Override + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; + } + + /** + * @return the urlDereferencer + */ + public URLDereferencer getUrlDereferencer() { + return urlDereferencer; + } + + /** + * @param urlDereferencer the urlDereferencer to set + */ + public void setUrlDereferencer(URLDereferencer urlDereferencer) { + this.urlDereferencer = urlDereferencer; + } + + protected void configureBindingProcessor(AbstractBindingProcessor bindingProcessor) { + bindingProcessor.setConfiguration(configuration); + bindingProcessor.setSlCommandFactory(slCommandFactory); + bindingProcessor.setUrlDereferencer(urlDereferencer); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessor.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessor.java index 0d978992..148fe296 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessor.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessor.java @@ -1,78 +1,147 @@ /* -* Copyright 2008 Federal Chancellery Austria and -* Graz University of Technology -* -* Licensed 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. -*/ + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed 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 at.gv.egiz.bku.binding; -import at.gv.egiz.bku.conf.Configuration; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Date; import java.util.Locale; +import at.gv.egiz.bku.slcommands.SLCommandFactory; import at.gv.egiz.bku.slcommands.SLCommandInvoker; import at.gv.egiz.stal.STAL; /** - * Represents an single instance of a SL HTTP binding. + * BindingProcessors implement the processing of a specific protocol binding + * (e.g. HTTP) for Security Layer requests. * - * @author wbauer - * + * @author wbauer, mcentner */ public interface BindingProcessor extends Runnable { /** - * The stream must be read completely within this method. + * Sets the command factory for creating Security Layer. Must be set before + * {@link #consumeRequestStream(String, InputStream)} is called. + * + * @param slCommandFactory + * the command factory for creating Security Layer commands. + */ + void setSlCommandFactory(SLCommandFactory slCommandFactory); + + /** + * Sets the preferred locale for user interaction. If the locale is not set + * the default locale will be used. Should be set before + * {@link #consumeRequestStream(String, InputStream)} is called to allow for a + * proper localization. + * + * @param locale + * must not be null. + */ + public void setLocale(Locale locale); + + /** + * Instructs this BindingProcessor to consume the request + * inputStream. + *

+ * Implementing classes are assumed to read the entire provided + * inputStream + *

+ *

+ * Any errors are reported via the result produced by this BindingProcessor. + *

* - * The caller is advised to check the result in case an error occurred. + * @param url + * the URL request is associated with (e.g. has been received on). + * + * @see BindingProcessor#writeResultTo(OutputStream, String) + */ + public void consumeRequestStream(String url, InputStream aIs); + + /** + * Initialize this BindingProcessor for processing. This method must be called + * before {@link #run()} is called. * - * @see #getResult() + * @param id + * the (unique) processing id (usually a HTTP session id) + * @param stal + * the STAL + * @param commandInvoker + * the CommandInvoker + * @throws NullPointerException + * if one of the provided parameters is null */ - public void consumeRequestStream(InputStream aIs); + public void init(String id, STAL stal, SLCommandInvoker commandInvoker); /** - * The unique Id of this http binding instance. - * @return + * Returns the unique processing id. + * + * @return the unique processing id or null if not yet assigned. */ public Id getId(); /** - * The used underlying STAL instance - * @return + * Returns the STAL used for processing. + * + * @return the STAL used for processing or null if not yet + * assigned. */ public STAL getSTAL(); + /** + * Returns the CommandInvoker used for processing. + * + * @return the CommandInvoker used for processing or null if not + * yet assigned. + */ public SLCommandInvoker getCommandInvoker(); - public Date getLastAccessTime(); - - public void updateLastAccessTime(); - + /** + * Returns the ContentType of the processing result. + * + * @return the ContentType type of the processing result or + * null if a result is not yet available. + */ public String getResultContentType(); - - public void writeResultTo(OutputStream os, String encoding) throws IOException; - public void init(STAL aStal, SLCommandInvoker aCommandInvoker, Configuration config); - /** - * Sets the preferred locale for userinteraction. - * If the locale is not set the default locale will be used. - * @param locale must not be null. - */ - public void setLocale(Locale locale); - - public boolean isFinished(); + * Writes the processing result to the given outputStream using + * the given character encoding. + * + * @param outputStream + * the OutputStream to write the result to + * @param encoding + * the character encoding to be used + * @throws IOException + * if writing to outputStream fails for any reason + */ + public void writeResultTo(OutputStream outputStream, String encoding) + throws IOException; + + /** + * Returns the time of the last access to this BindingProcessor instance. + * + * @return the time of the last access to this BindingProcessor instance. + */ + public Date getLastAccessTime(); + + /** + * Updates the time this BindingProcessor was accessed last. + */ + public void updateLastAccessTime(); + } \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorFactory.java new file mode 100644 index 00000000..ac922974 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorFactory.java @@ -0,0 +1,42 @@ +/* +* Copyright 2009 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed 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 at.gv.egiz.bku.binding; + +import java.util.Set; + +import org.apache.commons.configuration.Configuration; + +import at.gv.egiz.bku.slcommands.SLCommandFactory; +import at.gv.egiz.bku.utils.binding.Protocol; + + +public interface BindingProcessorFactory { + + public Set getSupportedProtocols(); + + public void setConfiguration(Configuration configuration); + + public Configuration getConfiguration(); + + public void setSlCommandFactory(SLCommandFactory commandFactory); + + public SLCommandFactory getSlCommandFactory(); + + public BindingProcessor createBindingProcessor(); + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorFuture.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorFuture.java new file mode 100644 index 00000000..f0c65323 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorFuture.java @@ -0,0 +1,73 @@ +/* +* Copyright 2009 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed 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 at.gv.egiz.bku.binding; + +import java.util.concurrent.FutureTask; + +public class BindingProcessorFuture extends FutureTask { + + private BindingProcessor bindingProcessor; + + private long startTime; + + private long executionTime; + + public BindingProcessorFuture(BindingProcessor bindingProcessor) { + super(bindingProcessor, null); + this.bindingProcessor = bindingProcessor; + } + + /** + * @return the bindingProcessor + */ + public BindingProcessor getBindingProcessor() { + return bindingProcessor; + } + + /* (non-Javadoc) + * @see java.util.concurrent.FutureTask#run() + */ + @Override + public void run() { + startTime = System.currentTimeMillis(); + try { + super.run(); + } finally { + executionTime = System.currentTimeMillis() - startTime; + } + } + + /** + * @return the startTime + */ + public long getStartTime() { + return startTime; + } + + /** + * @return the executionTime + */ + public long getExecutionTime() { + return executionTime; + } + + public long getAge() { + return System.currentTimeMillis() - startTime; + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManager.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManager.java index 9cad95a4..f32d3c4b 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManager.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManager.java @@ -1,107 +1,103 @@ /* -* Copyright 2008 Federal Chancellery Austria and -* Graz University of Technology -* -* Licensed 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. -*/ + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed 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 at.gv.egiz.bku.binding; -import java.net.MalformedURLException; import java.util.Locale; import java.util.Set; -import at.gv.egiz.bku.slcommands.SLCommandInvoker; -import at.gv.egiz.stal.STALFactory; - /** - * Central player that handles the protocol binding. - * - * @author wbauer + * A BindingProcessorManager provides factory methods for creating + * BindingProcessors and allows for scheduling them for processing. * + * @author wbauer, mcentner */ public interface BindingProcessorManager { /** - * FactoryMethod creating a new BindingProcessor object. - * The created binding processor must be passed to the process method to execute. + * Creates a new BindingProcessor for the given protocol. * - * @param urlString - * the source url - * @param aSessionId - * optional an external sessionId (e.g. http session) could be - * provided. This parameter may be null. - * @param locale the locale used for user interaction, may be null + * @param protocol + * the name of the protocol binding the created BindingProcessor is + * required to implement + * @param locale + * the locale to be used by the binding processor, may be + * null */ - public BindingProcessor createBindingProcessor(String urlString, - String aSessionId, Locale locale) throws MalformedURLException; + public BindingProcessor createBindingProcessor(String protocol, Locale locale); /** - * FactoryMethod creating a new BindingProcessor object. - * The created binding processor must be passed to the process method to execute. + * Creates a new BindingProcessor for the given protocol. * - * @param protcol - * the source url - * @param aSessionId - * optional an external sessionId (e.g. http session) could be - * provided. This parameter may be null. + * @param protocol + * the name of the protocol binding the created BindingProcessor is + * required to implement */ - public BindingProcessor createBindingProcessor(String urlString, - String aSessionId) throws MalformedURLException; + public BindingProcessor createBindingProcessor(String protocol); - /** - * Gets the binding processor with a certain id. The binding processor must be passed to the - * process method before it is managed and thus returned by this method. - * @param aId must not be null - * @return null if the binding processor was not "processed" before. + * Returns the BindingProcessor which has been scheduled for processing with + * the given id. + * + * @param id + * the processing id of the requested BindingProcessor + * + * @return the BindingProcessor which has been scheduled for processing with + * the given id, or null if no + * BindingProcessor has been scheduled with the given id. */ - public BindingProcessor getBindingProcessor(Id aId); + public BindingProcessor getBindingProcessor(Id id); /** - * Sets the STAL factory that is used for creating STAL objects that are used by BindingProcessor objects. - * For each new BindingProcessor a new STAL object is created. - * @param aStalFactory the factory to be used. Must not be null. + * Schedules the given BindingProcessor for processing. + *

+ *

    + *
  1. Creates a processing context with the given id.
  2. + *
  3. Schedules the given BindingProcessor for processing, and
  4. + *
  5. Immediately returns the processing context.
  6. + *
+ *

+ * + * @param id + * @param bindingProcessor */ - public void setSTALFactory(STALFactory aStalFactory); - + public BindingProcessorFuture process(Id id, BindingProcessor bindingProcessor); + /** - * Sets the invoker to be used. - * @param invoker + * Removes the BindingProcessor with the given processing id. + * + * @param id + * the processing id of the BindingProcessor to be removed */ - public void setSLCommandInvoker(SLCommandInvoker invoker); + public void removeBindingProcessor(Id id); /** - * Creates a processing context, - * schedules the provided binding processor for processing and - * immediately returns the context. + * Returns the set of Ids of currently managed BindingProcessor. * - * @param aBindingProcessor + * @return the set of Ids of currently managed BindingProcessor. */ - public ProcessingContext process(BindingProcessor aBindingProcessor); - + public Set getManagedIds(); + /** - * Removes a formerly added (by calling the process method) binding processor. - * @param bindingProcessor must not be null + * Schedule shutdown of this BindingProcessorManager. */ - public void removeBindingProcessor(Id sessionId); - + public void shutdown(); + /** - * A set of all managed binding processors. - * @return + * Immediately shutdown this BindingProcessorManager. */ - public Set getManagedIds(); - - public void shutdown(); - public void shutdownNow(); } \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManagerImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManagerImpl.java index bf9a63e2..eee80b03 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManagerImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/BindingProcessorManagerImpl.java @@ -16,315 +16,283 @@ */ package at.gv.egiz.bku.binding; -import at.gv.egiz.bku.conf.Configuration; -import java.net.MalformedURLException; -import java.net.URL; +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.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.configuration.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import at.gv.egiz.bku.jmx.ComponentMXBean; +import at.gv.egiz.bku.jmx.ComponentState; import at.gv.egiz.bku.slcommands.SLCommandInvoker; import at.gv.egiz.bku.slexceptions.SLRuntimeException; import at.gv.egiz.bku.utils.binding.Protocol; -import at.gv.egiz.stal.STAL; import at.gv.egiz.stal.STALFactory; /** * This class maintains all active BindingProcessor Objects. Currently, only * HTTPBinding is supported. */ -public class BindingProcessorManagerImpl implements BindingProcessorManager { +public class BindingProcessorManagerImpl implements BindingProcessorManager, ComponentMXBean { + + public static long DEFAULT_MAX_ACCEPTED_AGE = 2 * 60 * 1000; + + public static int DEFAULT_CLEAN_UP_INTERVAL = 60; - public final static Protocol[] SUPPORTED_PROTOCOLS = { Protocol.HTTP, - Protocol.HTTPS }; + private final Logger log = LoggerFactory.getLogger(BindingProcessorManagerImpl.class); - private static Log log = LogFactory.getLog(BindingProcessorManagerImpl.class); + private List factories = Collections.emptyList(); - /** spring injected config - * Passed to created bindingprocessors, to replace their configuration */ - protected Configuration config; + private Configuration configuration; - protected STALFactory stalFactory; - protected SLCommandInvoker commandInvokerClass; + private STALFactory stalFactory; + + private SLCommandInvoker commandInvoker; - private RemovalStrategy removalStrategy; - private ExecutorService executorService; - private Map contextMap = Collections.synchronizedMap(new HashMap()); -// private Map bindingProcessorMap = Collections -// .synchronizedMap(new HashMap()); + private ExecutorService executorService = Executors.newCachedThreadPool(); + private Map submittedFutures = Collections + .synchronizedMap(new HashMap()); + + private int cleanUpInterval = DEFAULT_CLEAN_UP_INTERVAL; + + private long maxAcceptedAge = DEFAULT_MAX_ACCEPTED_AGE; + + private ScheduledExecutorService cleanUpService = Executors + .newSingleThreadScheduledExecutor(); + + public BindingProcessorManagerImpl() { + cleanUpService.scheduleAtFixedRate(new CleanUpTask(), cleanUpInterval, + cleanUpInterval, TimeUnit.SECONDS); + } + /** - * Container to hold a Future and Bindingprocessor object as map value. - * - * @author wbauer - * @see BindingProcessorManagerImpl#bindingProcessorMap + * @return the configuration */ -// static class MapEntityWrapper { -// private Future future; -// private BindingProcessor bindingProcessor; -// -// public MapEntityWrapper(Future future, BindingProcessor bindingProcessor) { -// if ((bindingProcessor == null) || (future == null)) { -// throw new NullPointerException("Argument must not be null"); -// } -// this.bindingProcessor = bindingProcessor; -// this.future = future; -// } -// -// public Future getFuture() { -// return future; -// } -// -// public BindingProcessor getBindingProcessor() { -// return bindingProcessor; -// } -// -// public int hashCode() { -// return bindingProcessor.getId().hashCode(); -// } -// -// public boolean equals(Object other) { -// if (other instanceof MapEntityWrapper) { -// MapEntityWrapper o = (MapEntityWrapper) other; -// return (o.bindingProcessor.getId().equals(bindingProcessor.getId())); -// } else { -// return false; -// } -// } -// } + public Configuration getConfiguration() { + return configuration; + } /** - * - * @param fab - * must not be null - * @param ci - * must not be null (prototype to generate new instances) + * @param configuration the configuration to set */ - public BindingProcessorManagerImpl(STALFactory fab, SLCommandInvoker ci, Configuration conf) { - if (fab == null) { - throw new NullPointerException("STALFactory must not be null"); - } - stalFactory = fab; - if (ci == null) { - throw new NullPointerException("SLCommandInvoker must not be null"); - } - commandInvokerClass = ci; - config = conf; - executorService = Executors.newCachedThreadPool(); + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; } /** - * - * @return the STALFactory currently used. + * @return the factoryMap */ - public STALFactory getStalFactory() { - return stalFactory; + public List getFactories() { + return factories; } /** - * Sets the STALFactory to be used. - * - * @param stalFactory + * @param factoryMap the factoryMap to set */ - public void setStalFactory(STALFactory stalFactory) { - this.stalFactory = stalFactory; + public void setFactories(List factories) { + this.factories = factories; } /** - * Could be used to setup a new executor service during application stratup. + * Sets a SLCommandInvoker prototype used to create a SLCommandInvoker for + * initialization of a BindingProcessor. * - * @param executorService + * @param invoker + */ + public void setSlCommandInvoker(SLCommandInvoker invoker) { + commandInvoker = invoker; + } + + /** + * @return the SLCommandInvoker prototype used to create a SLCommandInvoker + * for initialization of a BindingProcessor. */ - public void setExecutorService(ExecutorService executorService) { - this.executorService = executorService; + public SLCommandInvoker getCommandInvoker() { + return commandInvoker; } - public void setRemovalStrategy(RemovalStrategy aStrategy) { - removalStrategy = aStrategy; + /** + * @return the STALFactory currently used. + */ + public STALFactory getStalFactory() { + return stalFactory; } - public RemovalStrategy getRemovlaStrategy() { - return removalStrategy; + /** + * Sets the STALFactory used to create a STAL implementation for initialization of + * a BindingProcessor. + * + * @param stalFactory + */ + public void setStalFactory(STALFactory stalFactory) { + this.stalFactory = stalFactory; } + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.BindingProcessorManager#shutdown() + */ + @Override public void shutdown() { - log.info("Shutting down the BindingProcessorManager"); + log.info("Shutting down the BindingProcessorManager."); executorService.shutdown(); + cleanUpService.shutdown(); } + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.BindingProcessorManager#shutdownNow() + */ + @Override public void shutdownNow() { log.info("Shutting down the BindingProcessorManager NOW!"); + cleanUpService.shutdownNow(); executorService.shutdownNow(); - log.debug("Number of binding contexts currently managed: " - + contextMap.size()); -// + bindingProcessorMap.size()); + log.debug("Number of binding contexts currently managed: {}.", submittedFutures.size()); if (log.isDebugEnabled()) { - for (ProcessingContext ctx : contextMap.values()) { - Id bpId = ctx.getBindingProcessor().getId(); - Future future = ctx.getFuture(); - log.debug(bpId + " cancelled: " + future.isCancelled()); - log.debug(bpId + " done: " + future.isDone()); + for (BindingProcessorFuture future : submittedFutures.values()) { + if (future.isCancelled()) { + log.debug("BindingProcessor {} is cancelled.", future.getBindingProcessor().getId()); + } else { + log.debug("BindingProcessor {} is done: {}.", future.getBindingProcessor().getId(), future.isDone()); + } } -// for (Iterator it = bindingProcessorMap.values() -// .iterator(); it.hasNext();) { -// MapEntityWrapper entry = it.next(); -// log.debug(entry.getBindingProcessor().getId() + ": isDone: " -// + entry.getFuture().isDone()); -// log.debug(entry.getBindingProcessor().getId() + ": isCanceled: " -// + entry.getFuture().isCancelled()); -// } } } - /** - * Uses the default locale + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.BindingProcessorManager#createBindingProcessor(java.lang.String, java.lang.String) */ - public BindingProcessor createBindingProcessor(String srcUrl, - String aSessionId) throws MalformedURLException { - return createBindingProcessor(srcUrl, aSessionId, null); + @Override + public BindingProcessor createBindingProcessor(String protocol) { + Protocol p = Protocol.fromString(protocol); + for (BindingProcessorFactory factory : factories) { + if (factory.getSupportedProtocols().contains(p)) { + return factory.createBindingProcessor(); + } + } + throw new IllegalArgumentException(); } - /** - * FactoryMethod creating a new BindingProcessor object. - * - * @param protocol - * must not be null - * @throws MalformedURLException + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.BindingProcessorManager#createBindingProcessor(java.lang.String, java.lang.String, java.util.Locale) */ - public BindingProcessor createBindingProcessor(String srcUrl, - String aSessionId, Locale locale) throws MalformedURLException { - URL url = new URL(srcUrl); - String low = url.getProtocol().toLowerCase(); - Protocol proto = null; - for (int i = 0; i < SUPPORTED_PROTOCOLS.length; i++) { - if (SUPPORTED_PROTOCOLS[i].toString().equals(low)) { - proto = SUPPORTED_PROTOCOLS[i]; - break; - } - } - if (proto == null) { - throw new UnsupportedOperationException(); - } - BindingProcessor bindingProcessor = new HTTPBindingProcessor(aSessionId, - commandInvokerClass.newInstance(), url); - stalFactory.setLocale(locale); - STAL stal = stalFactory.createSTAL(); - bindingProcessor.init(stal, commandInvokerClass.newInstance(), config); - if (locale != null) { - bindingProcessor.setLocale(locale); -// stal.setLocale(locale); - } + @Override + public BindingProcessor createBindingProcessor(String protocol, Locale locale) { + BindingProcessor bindingProcessor = createBindingProcessor(protocol); + bindingProcessor.setLocale(locale); return bindingProcessor; } - /** - * @return the bindingprocessor object for this id or null if no - * bindingprocessor was found. + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.BindingProcessorManager#process(java.lang.String, at.gv.egiz.bku.binding.BindingProcessor) */ - @Override - public BindingProcessor getBindingProcessor(Id aId) { -// if (bindingProcessorMap.get(aId) != null) { -// return bindingProcessorMap.get(aId).getBindingProcessor(); - ProcessingContext ctx = contextMap.get(aId); - if (ctx != null) { - return ctx.getBindingProcessor(); - } else { - return null; + @Override + public BindingProcessorFuture process(Id id, BindingProcessor bindingProcessor) { + + log.trace("Initialize BindingProcessor for processing."); + bindingProcessor.init(id.toString(), stalFactory.createSTAL(), commandInvoker.newInstance()); + + BindingProcessorFuture future = new BindingProcessorFuture(bindingProcessor); + if (submittedFutures.containsKey(bindingProcessor.getId())) { + log.error("BindingProcessor with with id {} already submitted.", id); + throw new SLRuntimeException("BindingProcessor with with id " + id + + " already submitted."); } + + try { + log.debug("Submitting BindingProcessor {} for processing.", id); + executorService.execute(future); + submittedFutures.put(bindingProcessor.getId(), future); + } catch (RejectedExecutionException e) { + log.error("BindingProcessor {} processing rejected.", id, e); + throw new SLRuntimeException("BindingProcessor {} " + id + " processing rejected.", e); + } + + return future; + } - /** - * + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.BindingProcessorManager#getBindingProcessor(at.gv.egiz.bku.binding.Id) */ - @Override - public void setSTALFactory(STALFactory aStalFactory) { - if (aStalFactory == null) { - throw new NullPointerException("Cannot set STALFactory to null"); + @Override + public BindingProcessor getBindingProcessor(Id id) { + BindingProcessorFuture future = submittedFutures.get(id); + if (future != null) { + return future.getBindingProcessor(); + } else { + return null; } - stalFactory = aStalFactory; } - /** - * Causes the BindingProcessorManager to manage the provided BindingProcessor - * Creates a processing context, - * schedules the provided binding processor for processing and - * immediately returns the context. - * - * @param aBindingProcessor - * must not be null + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.BindingProcessorManager#removeBindingProcessor(at.gv.egiz.bku.binding.Id) */ @Override - public ProcessingContext process(BindingProcessor aBindingProcessor) { - if (contextMap.containsKey(aBindingProcessor.getId())) { -// if (bindingProcessorMap.containsKey(aBindingProcessor.getId())) { - log.fatal("Clashing ids, cannot process bindingprocessor with id:" - + aBindingProcessor.getId()); - throw new SLRuntimeException( - "Clashing ids, cannot process bindingprocessor with id:" - + aBindingProcessor.getId()); + public void removeBindingProcessor(Id id) { + BindingProcessorFuture future = submittedFutures.remove(id); + if (future != null) { + if (!future.isDone()) { + log.debug("Interrupting BindingProcessor {}.", id ); + future.cancel(true); + } + if (log.isInfoEnabled()) { + Object[] args = {id, future.getExecutionTime() / 1000.0, future.getAge() / 1000.0}; + log.info("Removing BindingProcessor {} (active:{}s/age:{}s).", args); + } } - log.debug("processing bindingprocessor: " + aBindingProcessor.getId()); - Future f = executorService.submit(aBindingProcessor); - ProcessingContext ctx = new ProcessingContext(aBindingProcessor, f); - contextMap.put(aBindingProcessor.getId(), ctx); -// bindingProcessorMap.put(aBindingProcessor.getId(), new MapEntityWrapper(f, -// aBindingProcessor)); - return ctx; } + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.BindingProcessorManager#getManagedIds() + */ @Override - public void setSLCommandInvoker(SLCommandInvoker invoker) { - commandInvokerClass = invoker; + public Set getManagedIds() { + return Collections.unmodifiableSet(new HashSet(submittedFutures.keySet())); } + /* (non-Javadoc) + * @see at.gv.egiz.bku.jmx.ComponentMXBean#checkComponentState() + */ @Override - public void removeBindingProcessor(Id sessionId) { - log.debug("Removing binding processor: " + sessionId); - ProcessingContext ctx = contextMap.get(sessionId); - if (ctx == null) { - log.warn("no processing context to remove for session " + sessionId); - return; - } - Future f = ctx.getFuture(); - -// MapEntityWrapper wrapper = bindingProcessorMap.get(sessionId); -// if (wrapper == null) { -// return; -// } -// Future f = wrapper.getFuture(); - - if (!f.isDone()) { - log.trace("canceling " + sessionId); - f.cancel(true); - } - contextMap.remove(sessionId); -// bindingProcessorMap.remove(sessionId); + public ComponentState checkComponentState() { + return new ComponentState(true); } - - @Override - public Set getManagedIds() { - Set result = new HashSet(); - synchronized (contextMap) { - for (Id id : contextMap.keySet()) { - result.add(id); + + public class CleanUpTask implements Runnable { + + @Override + public void run() { + Collection futures = submittedFutures.values(); + List toBeRemoved = new ArrayList(); + int active = 0; + for(BindingProcessorFuture future : futures) { + BindingProcessor bindingProcessor = future.getBindingProcessor(); + if (!future.isDone()) { + active++; + } + if ((bindingProcessor.getLastAccessTime().getTime() - System + .currentTimeMillis()) > maxAcceptedAge) { + toBeRemoved.add(bindingProcessor.getId()); + } + } + for (Id id : toBeRemoved) { + removeBindingProcessor(id); } } -// synchronized (bindingProcessorMap) { -// for (Iterator it = bindingProcessorMap.keySet().iterator(); it -// .hasNext();) { -// result.add(it.next()); -// } -// } - return result; + } -} \ No newline at end of file +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataURLConnectionFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataURLConnectionFactory.java new file mode 100644 index 00000000..d6e5c701 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataURLConnectionFactory.java @@ -0,0 +1,26 @@ +/* +* Copyright 2009 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed 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 at.gv.egiz.bku.binding; + +import java.net.URL; + +public abstract class DataURLConnectionFactory { + + public abstract DataUrlConnection openConnection(URL url); + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java index d3945253..f267f9a9 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java @@ -16,18 +16,9 @@ */ package at.gv.egiz.bku.binding; -import at.gv.egiz.bku.conf.Configurator; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; -import java.util.Properties; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLSocketFactory; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import at.gv.egiz.bku.slexceptions.SLRuntimeException; /** * Used to handle DataUrl connections as specified in the CCE's HTTP protocol binding. @@ -35,77 +26,37 @@ import at.gv.egiz.bku.slexceptions.SLRuntimeException; */ public class DataUrl { - private static Log log = LogFactory.getLog(DataUrl.class); - private static DataUrlConnectionSPI connection; - private static Properties configuration; - private static SSLSocketFactory sslSocketFactory; - private static HostnameVerifier hostNameVerifier; - private URL url; - - /** spring injected config, to replace configuration */ - //private Configuration config; - + private static DataURLConnectionFactory connectionFactory; + /** - * Sets the default DataUrlConnection implementation - * @param aClass must not be null + * @return the connectionFactory */ - static void setDataUrlConnectionImpl(DataUrlConnectionSPI conn) { - if (conn != null) { - connection = conn; - } - } - - public DataUrl(String aUrlString) throws MalformedURLException { - url = new URL(aUrlString); - if (connection == null) { - log.debug("Using default DataURLConnection class"); - connection = new DataUrlConnectionImpl(); - } - connection.setConfiguration(configuration); - connection.setSSLSocketFactory(sslSocketFactory); - connection.setHostnameVerifier(hostNameVerifier); + public static DataURLConnectionFactory getConnectionFactory() { + return connectionFactory; } - public DataUrlConnection openConnection() { - try { - log.debug("Opening dataurl connection"); - DataUrlConnectionSPI retVal = connection.newInstance(); - retVal.init(url); - return retVal; - } catch (Exception e) { - log.error(e); - throw new SLRuntimeException("Cannot instantiate a dataurlconnection:", e); - } - } - - /** - * set configuration for all subsequently instantiated DataURL objects - * @param props + * @param connectionFactory the connectionFactory to set */ - public static void setConfiguration(Properties props) { - configuration = props; - if (configuration != null) { - String className = configuration.getProperty(Configurator.DATAURLCONNECTION_CONFIG_P); - if (className != null) { - log.warn("Set DataURLConnection class not supported!"); - } - } + public static void setConnectionFactory( + DataURLConnectionFactory connectionFactory) { + DataUrl.connectionFactory = connectionFactory; } /** - * set SSLSocketFactory for all subsequently instantiated DataURL objects - * @param socketFactory + * The URL. */ - public static void setSSLSocketFactory(SSLSocketFactory socketFactory) { - sslSocketFactory = socketFactory; + private URL url; + + public DataUrl(String spec) throws MalformedURLException { + url = new URL(spec); } - /** - * set HostnameVerifier for all subsequently instantiated DataURL objects - * @param hostNameVerifier - */ - public static void setHostNameVerifier(HostnameVerifier hostNameVerifier) { - DataUrl.hostNameVerifier = hostNameVerifier; + public DataUrlConnection openConnection() throws IOException { + if (connectionFactory != null) { + return connectionFactory.openConnection(url); + } else { + return new DataUrlConnectionImpl(url); + } } } \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java index 384cf71c..13b1e627 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java @@ -1,82 +1,92 @@ /* -* Copyright 2008 Federal Chancellery Austria and -* Graz University of Technology -* -* Licensed 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. -*/ + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed 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 at.gv.egiz.bku.binding; import java.io.IOException; -import java.io.InputStream; import java.net.SocketTimeoutException; import java.net.URL; -import java.security.cert.X509Certificate; +import java.net.URLConnection; import at.gv.egiz.bku.slcommands.SLResult; /** - * Transmit a security layer result to DataURL via HTTP POST, encoded as multipart/form-data. - * The HTTP header user-agent is set to citizen-card-environment/1.2 BKU2 1.0. - * The form-parameter ResponseType is set to HTTP-Security-Layer-RESPONSE. - * All other headers/parameters are set by the caller. + * Transmit a security layer result to DataURL via HTTP POST, encoded as + * multipart/form-data. The HTTP header user-agent is set to + * citizen-card-environment/1.2 BKU2 1.0. The form-parameter + * ResponseType is set to HTTP-Security-Layer-RESPONSE. All other + * headers/parameters are set by the caller. * * @author clemens */ -public interface DataUrlConnection { +public abstract class DataUrlConnection { + + public static final String FORMPARAM_RESPONSETYPE = "ResponseType"; + public static final String DEFAULT_RESPONSETYPE = "HTTP-Security-Layer-RESPONSE"; + public static final String FORMPARAM_XMLRESPONSE = "XMLResponse"; + public static final String FORMPARAM_BINARYRESPONSE = "BinaryResponse"; + + public static final String XML_RESPONSE_ENCODING = "UTF-8"; - public static final String FORMPARAM_RESPONSETYPE = "ResponseType"; - public static final String DEFAULT_RESPONSETYPE = "HTTP-Security-Layer-RESPONSE"; - public static final String FORMPARAM_XMLRESPONSE = "XMLResponse"; - public static final String FORMPARAM_BINARYRESPONSE = "BinaryResponse"; - - public static final String XML_RESPONSE_ENCODING = "UTF-8"; + /** + * The URL to send responses and retrieve any further requests. + */ + protected URL url; - - public String getProtocol(); - - public URL getUrl(); - - /** - * Set a HTTP Header. - * @param key - * @param value multiple values are assumed to have the correct formatting (comma-separated list) - */ - public void setHTTPHeader(String key, String value); + /** + * Constructs a DataURL connection to the specified URL. + * + * @param url + * the URL to send responses and retrieve any further requests + */ + protected DataUrlConnection(URL url) { + this.url = url; + } - /** - * Set a form-parameter. - * @param name - * @param data - * @param contentType may be null - * @param charSet may be null - * @param transferEncoding may be null - */ - public void setHTTPFormParameter(String name, InputStream data, String contentType, String charSet, String transferEncoding); - - /** - * @pre httpHeaders != null - * @throws java.net.SocketTimeoutException - * @throws java.io.IOException - */ - public void connect() throws SocketTimeoutException, IOException; + /** + * Returns the URL to send responses and retrieve any further requests. + * + * @return the URL + */ + public URL getURL() { + return url; + } - public X509Certificate getServerCertificate(); + /** + * @see URLConnection#connect() + */ + public abstract void connect() throws SocketTimeoutException, IOException; - /** - * @pre connection != null - * @throws java.io.IOException - */ - public void transmit(SLResult slResult) throws IOException; + /** + * Transmit the given SLResult to the resource identified by this + * URL. + * + * @param slResult the SLResult + * @throws IOException if an I/O exception occurs + */ + public abstract void transmit(SLResult slResult) throws IOException; - public DataUrlResponse getResponse() throws IOException; + /** + * Returns the DataUrlResponse received from the resource + * identified by this URL. + * + * @return the DataUrlResponse received + * + * @throws IOException if an I/O exception occurs + */ + public abstract DataUrlResponse getResponse() throws IOException; + } \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java index 93e5bb1c..1ce6d2cc 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java @@ -26,29 +26,27 @@ import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.Charset; -import java.security.cert.X509Certificate; +import java.security.cert.Certificate; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Properties; -import java.util.Set; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSocketFactory; import javax.xml.transform.stream.StreamResult; import org.apache.commons.httpclient.methods.multipart.FilePart; import org.apache.commons.httpclient.methods.multipart.Part; import org.apache.commons.httpclient.methods.multipart.StringPart; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import at.gv.egiz.bku.binding.multipart.InputStreamPartSource; import at.gv.egiz.bku.binding.multipart.SLResultPart; -import at.gv.egiz.bku.conf.Configurator; import at.gv.egiz.bku.slcommands.SLResult; import at.gv.egiz.bku.slcommands.SLResult.SLResultType; import at.gv.egiz.bku.slexceptions.SLRuntimeException; @@ -62,168 +60,144 @@ import at.gv.egiz.bku.utils.binding.Protocol; * systems. * */ -public class DataUrlConnectionImpl implements DataUrlConnectionSPI { +public class DataUrlConnectionImpl extends HttpsDataURLConnection { - private final static Log log = LogFactory.getLog(DataUrlConnectionImpl.class); - - public static final byte[] B_DEFAULT_RESPONSETYPE = DEFAULT_RESPONSETYPE.getBytes(Charset.forName("UTF-8")); + private final Logger log = LoggerFactory.getLogger(DataUrlConnectionImpl.class); + + public static final byte[] B_DEFAULT_RESPONSETYPE = DEFAULT_RESPONSETYPE + .getBytes(Charset.forName("UTF-8")); /** - * Supported protocols are HTTP and HTTPS. + * Supported protocols are HTTP and HTTPS. */ public final static Protocol[] SUPPORTED_PROTOCOLS = { Protocol.HTTP, Protocol.HTTPS }; /** - * The X509 certificate of the DataURL server. - */ - protected X509Certificate serverCertificate; - - /** - * The protocol of the DataURL. - */ - protected Protocol protocol; - - /** - * Use application/x-www-form-urlencoded instead of - * standard conform application/x-www-form-urlencoded. + * Use application/x-www-form-urlencoded instead of standard + * conform application/x-www-form-urlencoded. */ protected boolean urlEncoded = true; - - /** - * The value of the DataURL. - */ - protected URL url; - + /** * The URLConnection used for communication with the DataURL server. */ private HttpURLConnection connection; - - /** - * The HTTP request headers. - */ - protected Map requestHttpHeaders; - + /** * The HTTP form parameters. */ - protected ArrayList httpFormParameter; - + protected List httpFormParameter = new ArrayList(); + /** * The boundary for multipart/form-data requests. */ protected String boundary; - - /** - * The configuration properties. - */ - protected Properties config = null; - - /** - * The SSLSocketFactory for HTTPS connections. - */ - protected SSLSocketFactory sslSocketFactory; - - /** - * The HostnameVerifier for HTTPS connections. - */ - protected HostnameVerifier hostnameVerifier; /** * The response of the DataURL server. */ - protected DataUrlResponse result; + protected DataUrlResponse response; - /* (non-Javadoc) - * @see at.gv.egiz.bku.binding.DataUrlConnection#getProtocol() + /** + * Constructs a new instance of this DataUrlConnection implementation. + * + * @param url the URL + * + * @throws IOException if an I/O exception occurs */ - public String getProtocol() { + public DataUrlConnectionImpl(URL url) throws IOException { + super(url); + + Protocol protocol = null; + for (int i = 0; i < SUPPORTED_PROTOCOLS.length; i++) { + if (SUPPORTED_PROTOCOLS[i].toString().equalsIgnoreCase(url.getProtocol())) { + protocol = SUPPORTED_PROTOCOLS[i]; + break; + } + } if (protocol == null) { - return null; + throw new SLRuntimeException("Protocol " + url.getProtocol() + + " not supported for data url."); } - return protocol.toString(); + connection = (HttpURLConnection) url.openConnection(); + connection.setInstanceFollowRedirects(false); + + connection.setDoOutput(true); + + + boundary = "--" + IdFactory.getInstance().createId().toString(); } - /* (non-Javadoc) + @Override + public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { + if (connection instanceof HttpsURLConnection) { + ((HttpsURLConnection) connection).setHostnameVerifier(hostnameVerifier); + } + } + + @Override + public void setSSLSocketFactory(SSLSocketFactory socketFactory) { + if (connection instanceof HttpsURLConnection) { + ((HttpsURLConnection) connection).setSSLSocketFactory(socketFactory); + } + } + + /* + * (non-Javadoc) + * * @see at.gv.egiz.bku.binding.DataUrlConnection#connect() */ public void connect() throws SocketTimeoutException, IOException { - connection = (HttpURLConnection) url.openConnection(); - if (connection instanceof HttpsURLConnection) { - log.trace("Detected ssl connection"); - HttpsURLConnection https = (HttpsURLConnection) connection; - if (sslSocketFactory != null) { - log.debug("Setting custom ssl socket factory for ssl connection"); - https.setSSLSocketFactory(sslSocketFactory); - } else { - log.trace("No custom socket factory set"); - } - if (hostnameVerifier != null) { - log.debug("Setting custom hostname verifier"); - https.setHostnameVerifier(hostnameVerifier); - } - } else { - log.trace("No secure connection with: " + url + " class=" - + connection.getClass()); - } - connection.setDoOutput(true); // Transfer-Encoding: chunked is problematic ... // e.g. https://issues.apache.org/bugzilla/show_bug.cgi?id=37794 // ... therefore disabled. // connection.setChunkedStreamingMode(5*1024); if (urlEncoded) { - log.debug("Setting DataURL Content-Type to " - + HttpUtil.APPLICATION_URL_ENCODED); + log.debug("Setting DataURL Content-Type to {}.", + HttpUtil.APPLICATION_URL_ENCODED); connection.addRequestProperty(HttpUtil.HTTP_HEADER_CONTENT_TYPE, HttpUtil.APPLICATION_URL_ENCODED); } else { - log.debug("Setting DataURL Content-Type to " - + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY); + log.debug("Setting DataURL Content-Type to {}.", + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY); connection.addRequestProperty(HttpUtil.HTTP_HEADER_CONTENT_TYPE, HttpUtil.MULTIPART_FOTMDATA + HttpUtil.SEPERATOR[0] + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY + "=" + boundary); } - Set headers = requestHttpHeaders.keySet(); - Iterator headerIt = headers.iterator(); - while (headerIt.hasNext()) { - String name = headerIt.next(); - connection.setRequestProperty(name, requestHttpHeaders.get(name)); - } - log.trace("Connecting to: " + url); + log.trace("Connecting to URL '{}'.", url); connection.connect(); - if (connection instanceof HttpsURLConnection) { - HttpsURLConnection ssl = (HttpsURLConnection) connection; - X509Certificate[] certs = (X509Certificate[]) ssl.getServerCertificates(); - if ((certs != null) && (certs.length >= 1)) { - log.trace("Server certificate: " + certs[0]); - serverCertificate = certs[0]; - } - } } /* (non-Javadoc) - * @see at.gv.egiz.bku.binding.DataUrlConnection#getServerCertificate() + * @see at.gv.egiz.bku.binding.HttpsDataURLConnection#getServerCertificates() */ - public X509Certificate getServerCertificate() { - return serverCertificate; + @Override + public Certificate[] getServerCertificates() + throws SSLPeerUnverifiedException, IllegalStateException { + if (connection instanceof HttpsURLConnection) { + return ((HttpsURLConnection) connection).getServerCertificates(); + } else { + return null; + } } /* (non-Javadoc) - * @see at.gv.egiz.bku.binding.DataUrlConnection#setHTTPHeader(java.lang.String, java.lang.String) + * @see at.gv.egiz.bku.binding.HttpDataURLConnection#setHTTPHeader(java.lang.String, java.lang.String) */ + @Override public void setHTTPHeader(String name, String value) { - if (name != null && value != null) { - requestHttpHeaders.put(name, value); - } + connection.setRequestProperty(name, value); } /* (non-Javadoc) - * @see at.gv.egiz.bku.binding.DataUrlConnection#setHTTPFormParameter(java.lang.String, java.io.InputStream, java.lang.String, java.lang.String, java.lang.String) + * @see at.gv.egiz.bku.binding.HttpDataURLConnection#setHTTPFormParameter(java.lang.String, java.io.InputStream, java.lang.String, java.lang.String, java.lang.String) */ + @Override public void setHTTPFormParameter(String name, InputStream data, String contentType, String charSet, String transferEncoding) { - // if a content type is specified we have to switch to multipart/formdata encoding + // if a content type is specified we have to switch to multipart/form-data + // encoding if (contentType != null && contentType.length() > 0) { urlEncoded = false; } @@ -231,27 +205,27 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { charSet, transferEncoding)); } - - /* (non-Javadoc) * @see at.gv.egiz.bku.binding.DataUrlConnection#transmit(at.gv.egiz.bku.slcommands.SLResult) */ + @Override public void transmit(SLResult slResult) throws IOException { - log.trace("Sending data"); + log.trace("Sending data."); if (urlEncoded) { // // application/x-www-form-urlencoded (legacy, SL < 1.2) // - + OutputStream os = connection.getOutputStream(); - OutputStreamWriter streamWriter = new OutputStreamWriter(os, HttpUtil.DEFAULT_CHARSET); + OutputStreamWriter streamWriter = new OutputStreamWriter(os, + HttpUtil.DEFAULT_CHARSET); // ResponseType streamWriter.write(FORMPARAM_RESPONSETYPE); streamWriter.write("="); streamWriter.write(URLEncoder.encode(DEFAULT_RESPONSETYPE, "UTF-8")); streamWriter.write("&"); - + // XMLResponse / Binary Response if (slResult.getResultType() == SLResultType.XML) { streamWriter.write(DataUrlConnection.FORMPARAM_XMLRESPONSE); @@ -271,17 +245,18 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { streamWriter.write("&"); streamWriter.write(URLEncoder.encode(formParameter.getName(), "UTF-8")); streamWriter.write("="); - InputStreamReader reader = new InputStreamReader(formParameter.getData(), - (formParameter.getCharSet() != null) - ? formParameter.getCharSet() - : "UTF-8"); // assume request was application/x-www-form-urlencoded, formParam therefore UTF-8 + InputStreamReader reader = new InputStreamReader(formParameter + .getData(), (formParameter.getCharSet() != null) ? formParameter + .getCharSet() : "UTF-8"); // assume request was + // application/x-www-form-urlencoded, + // formParam therefore UTF-8 while ((len = reader.read(cbuf)) != -1) { urlEnc.write(cbuf, 0, len); } urlEnc.flush(); } streamWriter.close(); - + } else { // // multipart/form-data (conforming to SL 1.2) @@ -294,7 +269,7 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { DEFAULT_RESPONSETYPE, "UTF-8"); responseType.setTransferEncoding(null); parts.add(responseType); - + // XMLResponse / Binary Response SLResultPart slResultPart = new SLResultPart(slResult, XML_RESPONSE_ENCODING); @@ -307,7 +282,7 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { slResultPart.setContentType(slResult.getMimeType()); } parts.add(slResultPart); - + // transfer parameters for (HTTPFormParameter formParameter : httpFormParameter) { InputStreamPartSource source = new InputStreamPartSource(null, @@ -319,20 +294,21 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { } OutputStream os = connection.getOutputStream(); - Part.sendParts(os, parts.toArray(new Part[parts.size()]), boundary.getBytes()); + Part.sendParts(os, parts.toArray(new Part[parts.size()]), boundary + .getBytes()); os.close(); - + } - + // MultipartRequestEntity PostMethod InputStream is = null; try { is = connection.getInputStream(); } catch (IOException iox) { - log.info(iox); + log.info("Failed to get InputStream of HTTPUrlConnection.", iox); } - log.trace("Reading response"); - result = new DataUrlResponse(url.toString(), connection.getResponseCode(), + log.trace("Reading response."); + response = new DataUrlResponse(url.toString(), connection.getResponseCode(), is); Map responseHttpHeaders = new HashMap(); Map> httpHeaders = connection.getHeaderFields(); @@ -349,105 +325,26 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { responseHttpHeaders.put(key, valString); } } - result.setResponseHttpHeaders(responseHttpHeaders); + response.setResponseHttpHeaders(responseHttpHeaders); } @Override public DataUrlResponse getResponse() throws IOException { - return result; - } - - /** - * inits protocol, url, httpHeaders, formParams - * - * @param url - * must not be null - */ - @Override - public void init(URL url) { - - for (int i = 0; i < SUPPORTED_PROTOCOLS.length; i++) { - if (SUPPORTED_PROTOCOLS[i].toString().equalsIgnoreCase(url.getProtocol())) { - protocol = SUPPORTED_PROTOCOLS[i]; - break; - } - } - if (protocol == null) { - throw new SLRuntimeException("Protocol " + url.getProtocol() - + " not supported for data url"); - } - this.url = url; - boundary = "--" + IdFactory.getInstance().createId().toString(); - requestHttpHeaders = new HashMap(); - - if (config != null) { - String version = config.getProperty(Configurator.SIGNATURE_LAYOUT); - if ((version != null) && (!"".equals(version.trim()))) { - log.debug("setting SignatureLayout header to " + version); - requestHttpHeaders.put(Configurator.SIGNATURE_LAYOUT, version); - } else { - log.debug("do not set SignatureLayout header"); - } - String userAgent = config.getProperty(Configurator.USERAGENT_CONFIG_P, Configurator.USERAGENT_DEFAULT); - requestHttpHeaders.put(HttpUtil.HTTP_HEADER_USER_AGENT, userAgent); - } else { - requestHttpHeaders - .put(HttpUtil.HTTP_HEADER_USER_AGENT, Configurator.USERAGENT_DEFAULT); - - } - - httpFormParameter = new ArrayList(); - - } - - @Override - public DataUrlConnectionSPI newInstance() { - DataUrlConnectionSPI uc = new DataUrlConnectionImpl(); - uc.setConfiguration(config); - uc.setSSLSocketFactory(sslSocketFactory); - uc.setHostnameVerifier(hostnameVerifier); - return uc; + return response; } - @Override - public URL getUrl() { - return url; - } - - @Override - public void setConfiguration(Properties config) { - this.config = config; - } - - @Override - public void setSSLSocketFactory(SSLSocketFactory socketFactory) { - this.sslSocketFactory = socketFactory; - } - - @Override - public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { - this.hostnameVerifier = hostnameVerifier; - } - public class HTTPFormParameter { - private String name; - + private String name; + private InputStream data; - + private String contentType; - + private String charSet; - + private String transferEncoding; - - /** - * @param name - * @param data - * @param contentType - * @param charSet - * @param transferEncoding - */ + public HTTPFormParameter(String name, InputStream data, String contentType, String charSet, String transferEncoding) { super(); @@ -466,7 +363,8 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { } /** - * @param name the name to set + * @param name + * the name to set */ public void setName(String name) { this.name = name; @@ -480,7 +378,8 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { } /** - * @param data the data to set + * @param data + * the data to set */ public void setData(InputStream data) { this.data = data; @@ -494,7 +393,8 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { } /** - * @param contentType the contentType to set + * @param contentType + * the contentType to set */ public void setContentType(String contentType) { this.contentType = contentType; @@ -508,7 +408,8 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { } /** - * @param charSet the charSet to set + * @param charSet + * the charSet to set */ public void setCharSet(String charSet) { this.charSet = charSet; @@ -522,13 +423,12 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { } /** - * @param transferEncoding the transferEncoding to set + * @param transferEncoding + * the transferEncoding to set */ public void setTransferEncoding(String transferEncoding) { this.transferEncoding = transferEncoding; } - - } -} \ No newline at end of file +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionSPI.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionSPI.java deleted file mode 100644 index f838b919..00000000 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionSPI.java +++ /dev/null @@ -1,64 +0,0 @@ -/* -* Copyright 2008 Federal Chancellery Austria and -* Graz University of Technology -* -* Licensed 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 at.gv.egiz.bku.binding; - -import java.net.URL; -import java.util.Properties; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLSocketFactory; - -/** - * Prototype of a DataurlconnectionSPI - * @author wbauer - * - */ -public interface DataUrlConnectionSPI extends DataUrlConnection { - - /** - * Returns a new instance of this class to handle a dataurl. - * Called by the factory each time the openConnection method is called. - * @return - */ - public DataUrlConnectionSPI newInstance(); - - /** - * Initializes the DataUrlConnection - * @param url - */ - public void init(URL url); - - /** - * Sets configuration parameters for this connection - * @param config - */ - public void setConfiguration(Properties config); - - /** - * Sets the socketfactory to be used for ssl connections. - * @param socketFactory if null the socket factory will not be set explicitly - */ - public void setSSLSocketFactory(SSLSocketFactory socketFactory); - - /** - * Sets the hostname verifier to be used, - * @param hostnameVerifier if null the default hostname verifier will be used - */ - public void setHostnameVerifier(HostnameVerifier hostnameVerifier); - - -} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/ExpiryRemover.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/ExpiryRemover.java deleted file mode 100644 index d17a27c2..00000000 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/ExpiryRemover.java +++ /dev/null @@ -1,67 +0,0 @@ -/* -* Copyright 2008 Federal Chancellery Austria and -* Graz University of Technology -* -* Licensed 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 at.gv.egiz.bku.binding; - -import java.util.Iterator; -import java.util.Set; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * This class can be used to check the BindingProcessorManager for expired entries and remove them. - * Should be run periodically. - * - */ -public class ExpiryRemover implements RemovalStrategy { - - private static Log log = LogFactory.getLog(ExpiryRemover.class); - - protected BindingProcessorManager bindingProcessorManager; - // keep max 5 min. - protected long maxAcceptedAge = 1000 * 60 * 5; - - @Override - public void execute() { - log.debug("Triggered Expiry Remover"); - if (bindingProcessorManager == null) { - log.warn("Bindingprocessor not set, skipping removal"); - return; - } - Set managedIds = bindingProcessorManager.getManagedIds(); - for (Iterator it = managedIds.iterator(); it.hasNext();) { - Id bindId = it.next(); - BindingProcessor bp = bindingProcessorManager.getBindingProcessor(bindId); - if (bp != null) { - if (bp.getLastAccessTime().getTime() < (System.currentTimeMillis() - maxAcceptedAge)) { - log.debug("Removing binding processor: " + bp.getId()); - bindingProcessorManager.removeBindingProcessor(bp.getId()); - } - } - } - } - - public void setMaxAcceptedAge(long maxAcceptedAge) { - this.maxAcceptedAge = maxAcceptedAge; - } - - @Override - public void setBindingProcessorManager(BindingProcessorManager bp) { - bindingProcessorManager = bp; - } - -} \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormDataURLDereferencer.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormDataURLDereferencer.java new file mode 100644 index 00000000..2f62775b --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormDataURLDereferencer.java @@ -0,0 +1,71 @@ +/* +* Copyright 2009 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed 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 at.gv.egiz.bku.binding; + +import java.io.IOException; +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.bku.utils.urldereferencer.StreamData; +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; + +public class FormDataURLDereferencer implements URLDereferencer { + + public final static String PROTOCOL = "formdata"; + + private final Logger log = LoggerFactory.getLogger(FormDataURLDereferencer.class); + + private URLDereferencer urlDereferencer; + + private FormDataURLSupplier formDataURLSupplier; + + public FormDataURLDereferencer(URLDereferencer urlDereferencer, FormDataURLSupplier formDataURLSupplier) { + this.urlDereferencer = urlDereferencer; + this.formDataURLSupplier = formDataURLSupplier; + } + + @Override + public StreamData dereference(String url) + throws IOException { + + String urlString = url.toLowerCase().trim(); + if (urlString.startsWith(PROTOCOL)) { + log.debug("Requested to dereference a formdata url."); + return dereferenceFormData(url); + } else { + return urlDereferencer.dereference(url); + } + + } + + protected StreamData dereferenceFormData(String url) throws IOException { + log.debug("Dereferencing formdata url: {}.", url); + String[] parts = url.split(":", 2); + + String contentType = formDataURLSupplier.getFormDataContentType(parts[1]); + InputStream is = formDataURLSupplier.getFormData(parts[1]); + if (is != null) { + return new StreamData(url, contentType, is); + } + throw new IOException("Cannot dereference URL: '" + url + "' not found."); + } + + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormDataURLSupplier.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormDataURLSupplier.java new file mode 100644 index 00000000..a248e683 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/FormDataURLSupplier.java @@ -0,0 +1,27 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed 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 at.gv.egiz.bku.binding; + +import java.io.InputStream; + +public interface FormDataURLSupplier { + + public InputStream getFormData(String aParameterName); + + public String getFormDataContentType(String aParameterName); + +} \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java index e39addb5..db422498 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java @@ -1,844 +1,35 @@ /* - * Copyright 2008 Federal Chancellery Austria and - * Graz University of Technology - * - * Licensed 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 at.gv.egiz.bku.binding; +* Copyright 2009 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed 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. +*/ -import iaik.utils.Base64InputStream; +package at.gv.egiz.bku.binding; -import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.Writer; -import java.net.URL; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; import java.util.Map; -import javax.net.ssl.SSLHandshakeException; -import javax.xml.transform.Templates; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.URIResolver; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import at.gv.egiz.bku.slcommands.ErrorResult; -import at.gv.egiz.bku.slcommands.SLCommand; -import at.gv.egiz.bku.slcommands.SLCommandContext; -import at.gv.egiz.bku.slcommands.SLCommandFactory; -import at.gv.egiz.bku.slcommands.SLCommandInvoker; -import at.gv.egiz.bku.slcommands.SLResult; -import at.gv.egiz.bku.slcommands.SLSourceContext; -import at.gv.egiz.bku.slcommands.SLTargetContext; -import at.gv.egiz.bku.slcommands.impl.ErrorResultImpl; -import at.gv.egiz.bku.slexceptions.SLBindingException; -import at.gv.egiz.bku.slexceptions.SLException; -import at.gv.egiz.bku.slexceptions.SLRuntimeException; -import at.gv.egiz.bku.utils.StreamUtil; -import at.gv.egiz.bku.utils.binding.Protocol; -import at.gv.egiz.bku.utils.urldereferencer.FormDataURLSupplier; -import at.gv.egiz.bku.utils.urldereferencer.SimpleFormDataContextImpl; -import at.gv.egiz.bku.utils.urldereferencer.StreamData; -import at.gv.egiz.bku.utils.urldereferencer.URIResolverAdapter; -import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; -import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext; -import at.gv.egiz.stal.QuitRequest; -import at.gv.egiz.stal.STALRequest; - -/** - * Class performing the HTTP binding as defined by the CCE specification. - * Currently a huge monolithic class. - * - * @TODO refactor - */ -@SuppressWarnings("unchecked") -public class HTTPBindingProcessor extends AbstractBindingProcessor implements - FormDataURLSupplier { - - private static Log log = LogFactory.getLog(HTTPBindingProcessor.class); - - private static enum State { - INIT, PROCESS, DATAURL, TRANSFORM, FINISHED - }; - - public final static Collection XML_REQ_TRANSFER_ENCODING = Arrays - .asList(new String[] { "binary" }); - - protected static String XML_MIME_TYPE = "text/xml"; - protected static String BINARY_MIME_TYPE = "application/octet-stream"; - - /** - * If null everything is ok and the result is taken from the command invoker. - */ - protected SLException bindingProcessorError; - protected SLCommandInvoker commandInvoker; - protected DataUrlResponse dataUrlResponse; - protected Map headerMap = Collections.EMPTY_MAP; - protected SLCommand slCommand; - protected Map formParameterMap = new HashMap(); - protected SLSourceContext srcContex = new SLSourceContext(); - protected SLTargetContext targetContext = new SLTargetContext(); - protected URL srcUrl; - protected State currentState = State.INIT; - protected Templates templates = null; - protected String resultContentType = null; - protected SLResult slResult = null; - protected int responseCode = 200; - protected Map responseHeaders = Collections.EMPTY_MAP; - protected Locale locale = Locale.getDefault(); - protected boolean finished = false; - - /** - * - * @param id - * may be null. In this case a new session id will be created. - * @param cmdInvoker - * must not be null; - */ - public HTTPBindingProcessor(String id, SLCommandInvoker cmdInvoker, URL source) { - super(id); - this.srcUrl = source; - Protocol protocol = Protocol.fromString(source.getProtocol()); - if ((protocol != Protocol.HTTP) && (protocol != Protocol.HTTPS)) { - throw new SLRuntimeException("Protocol not supported: " + protocol); - } - if (cmdInvoker == null) { - throw new NullPointerException("Commandinvoker cannot be set to null"); - } - commandInvoker = cmdInvoker; - srcContex.setSourceUrl(source); - srcContex.setSourceIsDataURL(false); - } - - //---------------------------------------------------------------------------- - // ----------- BEGIN CONVENIENCE METHODS ----------- - - protected void sendSTALQuit() { - log.info("Sending QUIT command to STAL"); - List quit = new ArrayList(1); - quit.add(new QuitRequest()); - getSTAL().handleRequest(quit); - } - - protected String getFormParameterAsString(String formParameterName) { - FormParameter fp = formParameterMap.get(formParameterName); - return getFormParameterAsString(fp); - } - - protected String getFormParameterAsString(FormParameter fp) { - if (fp == null) { - return null; - } - try { - return StreamUtil.asString(fp.getFormParameterValue(), HttpUtil - .getCharset(fp.getFormParameterContentType(), true)); - } catch (IOException e) { - return null; - } - } - - protected String getDataUrl() { - return getFormParameterAsString(FixedFormParameters.DATAURL); - } - - protected String getStyleSheetUrl() { - return getFormParameterAsString(FixedFormParameters.STYLESHEETURL); - } - - protected List getFormParameters(String parameterNamePostfix) { - List resultList = new ArrayList(); - for (Iterator fpi = formParameterMap.keySet().iterator(); fpi - .hasNext();) { - String paramName = fpi.next(); - if (paramName.endsWith(parameterNamePostfix)) { - resultList.add(formParameterMap.get(paramName)); - } - } - return resultList; - } - - protected List getTransferHeaders() { - return getFormParameters("__"); - } - - protected List getTransferForms() { - List resultList = new ArrayList(); - for (Iterator fpi = formParameterMap.keySet().iterator(); fpi - .hasNext();) { - String paramName = fpi.next(); - if ((paramName.endsWith("_")) && (!paramName.endsWith("__"))) { - resultList.add(formParameterMap.get(paramName)); - } - } - return resultList; - } - - protected void closeDataUrlConnection() { - log.debug("Closing data url input stream"); - if (dataUrlResponse == null) { - return; - } - InputStream is = dataUrlResponse.getStream(); - if (is != null) { - try { - is.close(); - } catch (IOException e) { - log.info("Error closing input stream to dataurl server:" + e); - } - } - } - - //---------------------------------------------------------------------------- - // ----------- END CONVENIENCE METHODS ----------- - - //---------------------------------------------------------------------------- - // -- BEGIN Methods that handle the http binding activities as defined in the - // activity diagram -- - - protected void init() { - log.info("Starting Bindingprocessor in Thread: " - + Thread.currentThread().getId()); - if (bindingProcessorError != null) { - log.debug("Detected binding processor error, sending quit command"); - // sendSTALQuit(); - currentState = State.FINISHED; - } else if (slCommand == null) { - log.error("SLCommand not set (consumeRequest not called ??)"); - bindingProcessorError = new SLException(2000); - // sendSTALQuit(); - currentState = State.FINISHED; - } else { - currentState = State.PROCESS; - } - } - - protected void processRequest() { - log.debug("Entered State: " + State.PROCESS); - log.debug("Processing command: " + slCommand); - commandInvoker.setCommand(slCommand); - responseCode = 200; - responseHeaders = Collections.EMPTY_MAP; - dataUrlResponse = null; - try { - commandInvoker.invoke(srcContex); - } catch (SLException e) { - log.info("Caught exception: " + e); - bindingProcessorError = e; - currentState = State.TRANSFORM; - } - if (getDataUrl() != null) { - log.debug("Data Url set to: " + getDataUrl()); - currentState = State.DATAURL; - } else { - log.debug("No data url set"); - currentState = State.TRANSFORM; - } - } - - protected void handleDataUrl() { - log.debug("Entered State: " + State.DATAURL); - try { - DataUrl dataUrl = new DataUrl(getDataUrl()); - DataUrlConnection conn = dataUrl.openConnection(); - - // set transfer headers - for (FormParameter fp : getTransferHeaders()) { - String paramString = getFormParameterAsString(fp); - if (paramString == null) { - log.error("Got empty transfer header, ignoring this"); - } else { - String[] keyVal = paramString.split(":", 2); - String key = keyVal[0]; - String val = null; - if (keyVal.length == 2) { - val = keyVal[1]; - val = val.trim(); - } else { - log.error("Invalid transfer header encoding: "+paramString); - throw new SLBindingException(2005); - } - log.debug("Setting header " + key + " to value " + val); - conn.setHTTPHeader(key, val); - } - } - - // set transfer form parameters - for (FormParameter fp : getTransferForms()) { - String contentTransferEncoding = null; - String contentType = fp.getFormParameterContentType(); - String charSet = HttpUtil.getCharset(contentType, false); - if (charSet != null) { - contentType = contentType.substring(0, contentType - .lastIndexOf(HttpUtil.SEPERATOR[0])); - } - for (Iterator header = fp.getHeaderNames(); header.hasNext();) { - if (HttpUtil.CONTENT_TRANSFER_ENCODING - .equalsIgnoreCase(header.next())) { - contentTransferEncoding = getFormParameterAsString(fp); - } - } - log.debug("Setting form: " + fp.getFormParameterName() - + " contentType: " + contentType + " charset: " + charSet - + " contentTransferEncoding: " + contentTransferEncoding); - conn.setHTTPFormParameter(fp.getFormParameterName(), fp - .getFormParameterValue(), contentType, charSet, - contentTransferEncoding); - } - - // connect - conn.connect(); - // fetch and set SL result - targetContext.setTargetIsDataURL(true); - targetContext.setTargetCertificate(conn.getServerCertificate()); - targetContext.setTargetUrl(conn.getUrl()); - SLResult result = commandInvoker.getResult(targetContext); - - // transfer result - conn.transmit(result); - - // process Dataurl response - dataUrlResponse = conn.getResponse(); - log.debug("Received data url response code: " - + dataUrlResponse.getResponseCode()); - - switch (dataUrlResponse.getResponseCode()) { - case 200: - String contentType = dataUrlResponse.getContentType(); - log.debug("Got dataurl response content type: " + contentType); - if (contentType != null) { - if ((contentType.startsWith(HttpUtil.APPLICATION_URL_ENCODED)) - || (contentType.startsWith(HttpUtil.MULTIPART_FOTMDATA))) { - log.debug("Detected SL Request in dataurl response"); - // process headers and request - setHTTPHeaders(dataUrlResponse.getResponseHeaders()); - consumeRequestStream(dataUrlResponse.getStream()); - //TODO check for bindingProcessorError - closeDataUrlConnection(); - srcContex.setSourceCertificate(conn.getServerCertificate()); - srcContex.setSourceIsDataURL(true); - srcContex.setSourceUrl(conn.getUrl()); - currentState = State.PROCESS; - } else if (((contentType.startsWith(HttpUtil.TXT_HTML)) - || (contentType.startsWith(HttpUtil.TXT_PLAIN)) - || (contentType.startsWith(HttpUtil.TXT_XML))) - && (dataUrlResponse.isHttpResponseXMLOK())) { - log.info("Dataurl response matches with content type: " - + contentType); - currentState = State.TRANSFORM; - - } else if ((contentType.startsWith(HttpUtil.TXT_XML)) - && (!dataUrlResponse.isHttpResponseXMLOK())) { - log - .debug("Detected text/xml dataurl response with content != "); - headerMap.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, contentType); - assignXMLRequest(dataUrlResponse.getStream(), HttpUtil.getCharset( - contentType, true)); - closeDataUrlConnection(); - srcContex.setSourceCertificate(conn.getServerCertificate()); - srcContex.setSourceIsDataURL(true); - srcContex.setSourceUrl(conn.getUrl()); - currentState = State.PROCESS; - // just to be complete, actually not used - srcContex.setSourceHTTPReferer(dataUrlResponse.getResponseHeaders() - .get(HttpUtil.HTTP_HEADER_REFERER)); - } else { - resultContentType = contentType; - responseHeaders = dataUrlResponse.getResponseHeaders(); - responseCode = dataUrlResponse.getResponseCode(); - currentState = State.FINISHED; - } - } else { - log.debug("Content type not set in dataurl response"); - closeDataUrlConnection(); - throw new SLBindingException(2007); - } - - break; - case 307: - contentType = dataUrlResponse.getContentType(); - if ((contentType != null) && (contentType.startsWith(HttpUtil.TXT_XML))) { - log.debug("Received dataurl response code 307 with XML content"); - String location = dataUrlResponse.getResponseHeaders().get( - HttpUtil.HTTP_HEADER_LOCATION); - if (location == null) { - log - .error("Did not get a location header for a 307 data url response"); - throw new SLBindingException(2003); - } - // consumeRequestStream(dataUrlResponse.getStream()); - FormParameterStore fp = new FormParameterStore(); - fp.init(location.getBytes(HttpUtil.DEFAULT_CHARSET), - FixedFormParameters.DATAURL, null, null); - formParameterMap.put(FixedFormParameters.DATAURL, fp); - headerMap.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, contentType); - assignXMLRequest(dataUrlResponse.getStream(), HttpUtil.getCharset( - dataUrlResponse.getContentType(), true)); - closeDataUrlConnection(); - srcContex.setSourceCertificate(conn.getServerCertificate()); - srcContex.setSourceIsDataURL(true); - srcContex.setSourceUrl(conn.getUrl()); - currentState = State.PROCESS; - // just to be complete, actually not used - srcContex.setSourceHTTPReferer(dataUrlResponse.getResponseHeaders() - .get(HttpUtil.HTTP_HEADER_REFERER)); - - } else { - log.debug("Received dataurl response code 307 non XML content: " - + dataUrlResponse.getContentType()); - resultContentType = dataUrlResponse.getContentType(); - currentState = State.FINISHED; - } - responseHeaders = dataUrlResponse.getResponseHeaders(); - responseCode = dataUrlResponse.getResponseCode(); - break; - - case 301: - case 302: - case 303: - responseHeaders = dataUrlResponse.getResponseHeaders(); - responseCode = dataUrlResponse.getResponseCode(); - resultContentType = dataUrlResponse.getContentType(); - currentState = State.FINISHED; - break; - - default: - // issue error - log.info("Unexpected response code from dataurl server: " - + dataUrlResponse.getResponseCode()); - throw new SLBindingException(2007); - } - - } catch (SLException slx) { - bindingProcessorError = slx; - log.error("Error during dataurl communication"); - resultContentType = HttpUtil.TXT_XML; - currentState = State.TRANSFORM; - } catch (SSLHandshakeException hx) { - bindingProcessorError = new SLException(2010); - log.info("Error during dataurl communication", hx); - resultContentType = HttpUtil.TXT_XML; - currentState = State.TRANSFORM; - } catch (IOException e) { - bindingProcessorError = new SLBindingException(2001); - log.error("Error while data url handling", e); - resultContentType = HttpUtil.TXT_XML; - currentState = State.TRANSFORM; - return; - } - } - - protected void transformResult() { - log.debug("Entered State: " + State.TRANSFORM); - if (bindingProcessorError != null) { - resultContentType = HttpUtil.TXT_XML; - } else if (dataUrlResponse != null) { - resultContentType = dataUrlResponse.getContentType(); - } else { - targetContext.setTargetIsDataURL(false); - targetContext.setTargetUrl(srcUrl); - try { - slResult = commandInvoker.getResult(targetContext); - resultContentType = slResult.getMimeType(); - log - .debug("Successfully got SLResult from commandinvoker, setting mimetype to: " - + resultContentType); - } catch (SLException e) { - log.info("Cannot get result from invoker:", e); - bindingProcessorError = new SLException(6002); - resultContentType = HttpUtil.TXT_XML; - } - } - templates = getTemplates(getStyleSheetUrl()); - if (templates != null) { - log.debug("Output transformation required"); - resultContentType = templates.getOutputProperties().getProperty("media-type"); - log.debug("Got media type from stylesheet: " + resultContentType); - if (resultContentType == null) { - log.debug("Setting to default text/xml result conent type"); - resultContentType = "text/xml"; - } - log.debug("Deferring sytylesheet processing"); - } - currentState = State.FINISHED; - } - - protected void finished() { - log.debug("Entered State: " + State.FINISHED); - if (bindingProcessorError != null) { - log.debug("Binding processor error, sending quit command"); - resultContentType = HttpUtil.TXT_XML; - } - sendSTALQuit(); - log.info("Terminating Bindingprocessor; Thread: " - + Thread.currentThread().getId()); - finished = true; - } - - // -- END Methods that handle the http binding activities as defined in the - // activity diagram -- - //---------------------------------------------------------------------------- - - /** - * Sets the headers of the SL Request. IMPORTANT: make sure to set all headers - * before invoking {@link #consumeRequestStream(InputStream)} - * - * @param aHeaderMap - * if null all header will be cleared. - */ - public void setHTTPHeaders(Map aHeaderMap) { - headerMap = new HashMap(); - // ensure lowercase keys - if (aHeaderMap != null) { - for (String s : aHeaderMap.keySet()) { - if (s != null) { - headerMap.put(s.toLowerCase(), aHeaderMap.get(s)); - if (s.equalsIgnoreCase(HttpUtil.HTTP_HEADER_REFERER)) { - String referer = aHeaderMap.get(s); - log.debug("Got referer header: " + referer); - srcContex.setSourceHTTPReferer(referer); - } - } - } - } - } - - public void setSourceCertificate(X509Certificate aCert) { - srcContex.setSourceCertificate(aCert); - } - - /** - * The HTTPBindingProcessor does not handle redirect URLs. It only provides - * the parameter. - * - * @return null if redirect url is not set. - */ - public String getRedirectURL() { - return getFormParameterAsString(FixedFormParameters.REDIRECTURL); - } - - public String getFormDataContentType(String aParameterName) { - FormParameter fp = formParameterMap.get(aParameterName); - if (fp != null) { - return fp.getFormParameterContentType(); - } - return null; - } - - public InputStream getFormData(String aParameterName) { - FormParameter fp = formParameterMap.get(aParameterName); - if (fp != null) { - final String enc = fp.getHeaderValue("Content-Transfer-Encoding"); - if (enc == null || "binary".equals(enc)) { - return fp.getFormParameterValue(); - } else if ("base64".equals(enc)) { - return new Base64InputStream(fp.getFormParameterValue()); - } else { - return new InputStream() { - @Override - public int read() throws IOException { - throw new IOException("Content-Transfer-Encoding : " + enc - + " is not supported."); - } - }; - } - } - return null; - } - - protected void assignXMLRequest(InputStream is, String charset) - throws IOException, SLException { - Reader r = new InputStreamReader(is, charset); - StreamSource source = new StreamSource(r); - SLCommandContext commandCtx = new SLCommandContext(); - commandCtx.setSTAL(getSTAL()); - commandCtx.setURLDereferencerContext(new SimpleFormDataContextImpl(this)); - commandCtx.setLocale(locale); - slCommand = SLCommandFactory.getInstance().createSLCommand(source, - commandCtx); - log.debug("Created new command: " + slCommand); - } - - @Override - public void run() { - boolean done = false; - int hopcounter = 0; - if (bindingProcessorError != null) { - currentState = State.FINISHED; - } - try { - while (!done) { - try { - switch (currentState) { - case INIT: - init(); - break; - case PROCESS: - processRequest(); - break; - case DATAURL: - handleDataUrl(); - if (++hopcounter > config.getMaxDataUrlHops()) { - log.error("Maximum number of dataurl hops reached"); - bindingProcessorError = new SLBindingException(2000); - currentState = State.FINISHED; - } - break; - case TRANSFORM: - transformResult(); - break; - case FINISHED: - done = true; - finished(); - break; - } - } catch (RuntimeException rte) { - throw rte; - } catch (Exception t) { - log.error("Caught unexpected exception", t); - responseCode = 200; - resultContentType = HttpUtil.TXT_XML; - responseHeaders = Collections.EMPTY_MAP; - bindingProcessorError = new SLException(2000); - currentState = State.FINISHED; - } - } - } catch (Throwable t) { - log.error("Caught unexpected exception", t); - responseCode = 200; - resultContentType = HttpUtil.TXT_XML; - responseHeaders = Collections.EMPTY_MAP; - bindingProcessorError = new SLException(2000); - currentState = State.FINISHED; - } - log.debug("Terminated http binding processor"); - finished = true; - } - - @Override - public void consumeRequestStream(InputStream is) { - try { - log.debug("Start consuming request stream"); - formParameterMap.clear(); - String cl = headerMap - .get(HttpUtil.HTTP_HEADER_CONTENT_TYPE.toLowerCase()); - if (cl == null) { - log.info("No content type set in http header"); - throw new SLBindingException(2006); - } - InputDecoder id = InputDecoderFactory.getDecoder(cl, is); - if (id == null) { - log.error("Cannot get inputdecoder for is"); - throw new SLException(2006); - } - for (Iterator fpi = id.getFormParameterIterator(); fpi - .hasNext();) { - FormParameter fp = fpi.next(); - log.debug("Got request parameter with name: " - + fp.getFormParameterName()); - if (fp.getFormParameterName().equals(FixedFormParameters.XMLREQUEST)) { - log.debug("Creating XML Request"); - for (Iterator headerIterator = fp.getHeaderNames(); headerIterator - .hasNext();) { - String headerName = headerIterator.next(); - if (HttpUtil.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(headerName)) { - String transferEncoding = fp.getHeaderValue(headerName); - log.debug("Got transfer encoding for xmlrequest: " - + transferEncoding); - if (XML_REQ_TRANSFER_ENCODING.contains(transferEncoding)) { - log.debug("Supported transfer encoding: " + transferEncoding); - } else { - log - .error("Transferencoding not supported: " - + transferEncoding); - throw new SLBindingException(2005); - } - } - } - String charset = HttpUtil.getCharset(cl, true); - assignXMLRequest(fp.getFormParameterValue(), charset); - } else { - FormParameterStore fps = new FormParameterStore(); - fps.init(fp); - //if (!fps.isEmpty()) { - log.debug("Setting form parameter: " + fps.getFormParameterName()); - formParameterMap.put(fps.getFormParameterName(), fps); - //} - } - } - if (slCommand == null) { - throw new SLBindingException(2004); - } - } catch (SLException slx) { - log.info("Error while consuming input stream " + slx); - bindingProcessorError = slx; - } catch (Throwable t) { - log.info("Error while consuming input stream " + t, t); - bindingProcessorError = new SLException(2000); - } finally { - try { - if (is.read() != -1) { - log.warn("Request input stream not completely read."); - while (is.read() != -1); - } - } catch (IOException e) { - log.error(e); - } - } - } - - @Override - public String getResultContentType() { - return resultContentType; - } - - protected Templates getTemplates(String styleSheetURL) { - if (styleSheetURL == null) { - log.debug("Stylesheet URL not set"); - return null; - } - try { - URLDereferencerContext urlCtx = new SimpleFormDataContextImpl(this); - URIResolver resolver = new URIResolverAdapter(URLDereferencer - .getInstance(), urlCtx); - TransformerFactory factory = TransformerFactory.newInstance(); - factory.setURIResolver(resolver); - StreamData sd = URLDereferencer.getInstance().dereference(styleSheetURL, - urlCtx); - return factory.newTemplates(new StreamSource(sd.getStream())); - } catch (Exception ex) { - log.info("Cannot instantiate transformer", ex); - bindingProcessorError = new SLException(2002); - return null; - } - } - - protected void handleBindingProcessorError(OutputStream os, String encoding, - Templates templates) throws IOException { - log.debug("Writing error as result"); - ErrorResultImpl error = new ErrorResultImpl(bindingProcessorError, locale); - Writer writer = writeXMLDeclarationAndProcessingInstruction(os, encoding); - error.writeTo(new StreamResult(writer), templates, true); - } - - protected Writer writeXMLDeclarationAndProcessingInstruction(OutputStream os, String encoding) throws IOException { - if (encoding == null) { - encoding = HttpUtil.DEFAULT_CHARSET; - } - OutputStreamWriter writer = new OutputStreamWriter(os, encoding); - writer.write("\n"); - writer.write("\n"); - return writer; - } - - @Override - public void writeResultTo(OutputStream os, String encoding) - throws IOException { - if (encoding == null) { - encoding = HttpUtil.DEFAULT_CHARSET; - } - if (bindingProcessorError != null) { - log.debug("Detected error in binding processor, writing error as result"); - handleBindingProcessorError(os, encoding, templates); - return; - } else if (dataUrlResponse != null) { - log.debug("Writing data url response as result"); - String charEnc = HttpUtil.getCharset(dataUrlResponse.getContentType(), - true); - InputStreamReader isr = new InputStreamReader( - dataUrlResponse.getStream(), charEnc); - OutputStreamWriter osw = new OutputStreamWriter(os, encoding); - if (templates == null) { - StreamUtil.copyStream(isr, osw); - } else { - try { - Transformer transformer = templates.newTransformer(); - transformer.transform(new StreamSource(isr), new StreamResult(osw)); - } catch (TransformerException e) { - log.fatal("Exception occured during result transformation", e); - // bindingProcessorError = new SLException(2008); - // handleBindingProcessorError(os, encoding, null); - return; - } - } - osw.flush(); - isr.close(); - } else if (slResult == null) { - // result not yet assigned -> must be a cancel - bindingProcessorError = new SLException(6001); - handleBindingProcessorError(os, encoding, templates); - return; - } else { - log.debug("Getting result from invoker"); - boolean fragment = false; - Writer writer; - if (slResult instanceof ErrorResult) { - writer = writeXMLDeclarationAndProcessingInstruction(os, encoding); - fragment = true; - } else { - writer = new OutputStreamWriter(os, encoding); - } - slResult.writeTo(new StreamResult(writer), templates, fragment); - writer.flush(); - } - } +public interface HTTPBindingProcessor extends BindingProcessor { - /** - * The response code from the dataurl server or 200 if no dataurl server - * created the result - * - * @return - */ - public int getResponseCode() { - return responseCode; - } + public void setHTTPHeaders(Map headerMap); + + public InputStream getFormData(String parameterName); - /** - * All headers from the data url server in case of a direct forward from the - * dataurl server. - * - * @return - */ - public Map getResponseHeaders() { - return responseHeaders; - } + public String getRedirectURL(); - @Override - public void setLocale(Locale locale) { - if (locale == null) { - throw new NullPointerException("Locale must not be set to null"); - } - this.locale = locale; - } + public int getResponseCode(); - @Override - public boolean isFinished() { - return finished; - } -} \ No newline at end of file + public Map getResponseHeaders(); + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessorFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessorFactory.java new file mode 100644 index 00000000..41688e9b --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessorFactory.java @@ -0,0 +1,80 @@ +/* +* Copyright 2009 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed 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 at.gv.egiz.bku.binding; + + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSocketFactory; + + +import at.gv.egiz.bku.utils.binding.Protocol; + +public class HTTPBindingProcessorFactory extends AbstractBindingProcessorFactory implements BindingProcessorFactory { + + private HostnameVerifier hostnameVerifier; + + private SSLSocketFactory sslSocketFactory; + + public HTTPBindingProcessorFactory() { + Set sp = new HashSet(); + Collections.addAll(sp, Protocol.HTTP, Protocol.HTTPS); + supportedProtocols = Collections.unmodifiableSet(sp); + } + + /** + * @return the hostnameVerifier + */ + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + + /** + * @param hostnameVerifier the hostnameVerifier to set + */ + public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + } + + /** + * @return the sslSocketFactory + */ + public SSLSocketFactory getSslSocketFactory() { + return sslSocketFactory; + } + + /** + * @param sslSocketFactory the sslSocketFactory to set + */ + public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { + this.sslSocketFactory = sslSocketFactory; + } + + @Override + public BindingProcessor createBindingProcessor() { + HTTPBindingProcessorImpl httpBindingProcessor = new HTTPBindingProcessorImpl(); + configureBindingProcessor(httpBindingProcessor); + httpBindingProcessor.setHostnameVerifier(hostnameVerifier); + httpBindingProcessor.setSslSocketFactory(sslSocketFactory); + return httpBindingProcessor; + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessorImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessorImpl.java new file mode 100644 index 00000000..b5f34689 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessorImpl.java @@ -0,0 +1,896 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed 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 at.gv.egiz.bku.binding; + +import iaik.utils.Base64InputStream; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLSocketFactory; +import javax.xml.transform.Templates; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.bku.conf.MoccaConfigurationFacade; +import at.gv.egiz.bku.slcommands.ErrorResult; +import at.gv.egiz.bku.slcommands.SLCommand; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slcommands.SLResult; +import at.gv.egiz.bku.slcommands.SLSourceContext; +import at.gv.egiz.bku.slcommands.SLTargetContext; +import at.gv.egiz.bku.slcommands.impl.ErrorResultImpl; +import at.gv.egiz.bku.slexceptions.SLBindingException; +import at.gv.egiz.bku.slexceptions.SLException; +import at.gv.egiz.bku.spring.ConfigurationFactoryBean; +import at.gv.egiz.bku.utils.StreamUtil; +import at.gv.egiz.bku.utils.urldereferencer.StreamData; +import at.gv.egiz.bku.utils.urldereferencer.URIResolverAdapter; +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; +import at.gv.egiz.stal.QuitRequest; +import at.gv.egiz.stal.STALRequest; + +/** + * Class performing the HTTP binding as defined by the CCE specification. + * Currently a huge monolithic class. + * + * @TODO refactor + */ +@SuppressWarnings("unchecked") +public class HTTPBindingProcessorImpl extends AbstractBindingProcessor implements + HTTPBindingProcessor, FormDataURLSupplier { + + private final Logger log = LoggerFactory.getLogger(HTTPBindingProcessorImpl.class); + + private static enum State { + INIT, PROCESS, DATAURL, TRANSFORM, FINISHED + }; + + public final static Collection XML_REQ_TRANSFER_ENCODING = Arrays + .asList(new String[] { "binary" }); + + protected static String XML_MIME_TYPE = "text/xml"; + protected static String BINARY_MIME_TYPE = "application/octet-stream"; + + /** + * The citizen card environment identifier for Server and + * UserAgent headers. + */ + protected static String CITIZENC_CARD_ENVIRONMENT = "citizen-card-environment/1.2"; + + /** + * The configuration facade used to access the MOCCA configuration. + */ + protected ConfigurationFacade configurationFacade = new ConfigurationFacade(); + + public class ConfigurationFacade implements MoccaConfigurationFacade { + + public static final String DATAURLCLIENT_MAXHOPS = "DataURLConnection.MaxHops"; + + public int getMaxDataUrlHops() { + return configuration.getInt(DATAURLCLIENT_MAXHOPS, 10); + } + + public String getProductName() { + return configuration.getString( + ConfigurationFactoryBean.MOCCA_IMPLEMENTATIONNAME_PROPERTY, "MOCCA"); + } + + public String getProductVersion() { + return configuration.getString( + ConfigurationFactoryBean.MOCCA_IMPLEMENTATIONVERSION_PROPERTY, + "UNKNOWN"); + } + + public String getSignatureLayout() { + return configuration + .getString(ConfigurationFactoryBean.SIGNATURE_LAYOUT_PROPERTY); + } + + } + + /** + * If null everything is ok and the result is taken from the command invoker. + */ + protected SLException bindingProcessorError; + protected SSLSocketFactory sslSocketFactory; + protected HostnameVerifier hostnameVerifier; + protected DataUrlResponse dataUrlResponse; + protected Map headerMap = Collections.EMPTY_MAP; + protected SLCommand slCommand; + protected Map formParameterMap = new HashMap(); + protected SLSourceContext srcContex = new SLSourceContext(); + protected SLTargetContext targetContext = new SLTargetContext(); + protected URL srcUrl; + protected State currentState = State.INIT; + protected Templates templates = null; + protected String resultContentType = null; + protected SLResult slResult = null; + protected int responseCode = 200; + protected Map responseHeaders = Collections.EMPTY_MAP; + protected boolean finished = false; + + @Override + public void setUrlDereferencer(URLDereferencer urlDereferencer) { + super.setUrlDereferencer(new FormDataURLDereferencer(urlDereferencer, this)); + } + + /** + * @return the sslSocketFactory + */ + public SSLSocketFactory getSslSocketFactory() { + return sslSocketFactory; + } + + /** + * @param sslSocketFactory + * the sslSocketFactory to set + */ + public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { + this.sslSocketFactory = sslSocketFactory; + } + + /** + * @return the hostnameVerifier + */ + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + + /** + * @param hostnameVerifier + * the hostnameVerifier to set + */ + public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + } + + protected void sendSTALQuit() { + log.debug("Sending QUIT command to STAL."); + List quit = new ArrayList(1); + quit.add(new QuitRequest()); + getSTAL().handleRequest(quit); + } + + protected String getFormParameterAsString(String formParameterName) { + FormParameter fp = formParameterMap.get(formParameterName); + return getFormParameterAsString(fp); + } + + protected String getFormParameterAsString(FormParameter fp) { + if (fp == null) { + return null; + } + try { + return StreamUtil.asString(fp.getFormParameterValue(), HttpUtil + .getCharset(fp.getFormParameterContentType(), true)); + } catch (IOException e) { + return null; + } + } + + protected String getDataUrl() { + return getFormParameterAsString(FixedFormParameters.DATAURL); + } + + protected String getStyleSheetUrl() { + return getFormParameterAsString(FixedFormParameters.STYLESHEETURL); + } + + protected List getFormParameters(String parameterNamePostfix) { + List resultList = new ArrayList(); + for (Iterator fpi = formParameterMap.keySet().iterator(); fpi + .hasNext();) { + String paramName = fpi.next(); + if (paramName.endsWith(parameterNamePostfix)) { + resultList.add(formParameterMap.get(paramName)); + } + } + return resultList; + } + + protected List getTransferHeaders() { + return getFormParameters("__"); + } + + protected List getTransferForms() { + List resultList = new ArrayList(); + for (Iterator fpi = formParameterMap.keySet().iterator(); fpi + .hasNext();) { + String paramName = fpi.next(); + if ((paramName.endsWith("_")) && (!paramName.endsWith("__"))) { + resultList.add(formParameterMap.get(paramName)); + } + } + return resultList; + } + + protected void closeDataUrlConnection() { + log.debug("Closing data url input stream."); + if (dataUrlResponse == null) { + return; + } + InputStream is = dataUrlResponse.getStream(); + if (is != null) { + try { + is.close(); + } catch (IOException e) { + log.info("Error closing input stream to dataurl server.", e); + } + } + } + + //---------------------------------------------------------------------------- + // ----------- END CONVENIENCE METHODS ----------- + + //---------------------------------------------------------------------------- + // -- BEGIN Methods that handle the http binding activities as defined in the + // activity diagram -- + + protected void init() { + log.info("Starting Bindingprocessor : {}.", id); + if (bindingProcessorError != null) { + log.debug("Detected binding processor error, sending quit command."); + currentState = State.FINISHED; + } else if (slCommand == null) { + log.error("SLCommand not set. (consumeRequest not called?)"); + bindingProcessorError = new SLException(2000); + currentState = State.FINISHED; + } else { + currentState = State.PROCESS; + } + } + + protected void processRequest() { + log.info("Entered State: {}, Processing {}.", State.PROCESS, slCommand.getName()); + SLCommandContext commandCtx = new SLCommandContext( + getSTAL(), + new FormDataURLDereferencer(urlDereferencer, this), + locale); + commandInvoker.setCommand(commandCtx, slCommand); + responseCode = 200; + responseHeaders = Collections.EMPTY_MAP; + dataUrlResponse = null; + try { + commandInvoker.invoke(srcContex); + } catch (SLException e) { + log.info("Failed to invoke command.", e); + bindingProcessorError = e; + currentState = State.TRANSFORM; + } + if (getDataUrl() != null) { + log.debug("DataUrl set to: {}.", getDataUrl()); + currentState = State.DATAURL; + } else { + log.debug("No data url set."); + currentState = State.TRANSFORM; + } + } + + protected void handleDataUrl() { + log.info("Entered State: {}, DataURL={}.", State.DATAURL, getDataUrl()); + try { + DataUrl dataUrl = new DataUrl(getDataUrl()); + HttpsDataURLConnection conn = (HttpsDataURLConnection) dataUrl.openConnection(); + + // set user agent and signature layout headers + conn.setHTTPHeader(HttpUtil.HTTP_HEADER_USER_AGENT, getServerHeaderValue()); + conn.setHTTPHeader(HttpUtil.HTTP_HEADER_SIGNATURE_LAYOUT, getSignatureLayoutHeaderValue()); + conn.setHostnameVerifier(hostnameVerifier); + conn.setSSLSocketFactory(sslSocketFactory); + + // set transfer headers + for (FormParameter fp : getTransferHeaders()) { + String paramString = getFormParameterAsString(fp); + if (paramString == null) { + log.error("Got empty transfer header, ignoring this."); + } else { + String[] keyVal = paramString.split(":", 2); + String key = keyVal[0]; + String val = null; + if (keyVal.length == 2) { + val = keyVal[1]; + val = val.trim(); + } else { + log.error("Invalid transfer header encoding: {}.", paramString); + throw new SLBindingException(2005); + } + log.debug("Setting header '{}' to value '{}'.", key, val); + conn.setHTTPHeader(key, val); + } + } + + // set transfer form parameters + for (FormParameter fp : getTransferForms()) { + String contentTransferEncoding = null; + String contentType = fp.getFormParameterContentType(); + String charSet = HttpUtil.getCharset(contentType, false); + if (charSet != null) { + contentType = contentType.substring(0, contentType + .lastIndexOf(HttpUtil.SEPERATOR[0])); + } + for (Iterator header = fp.getHeaderNames(); header.hasNext();) { + if (HttpUtil.CONTENT_TRANSFER_ENCODING + .equalsIgnoreCase(header.next())) { + contentTransferEncoding = getFormParameterAsString(fp); + } + } + if (log.isDebugEnabled()) { + Object[] args = {fp.getFormParameterName(), contentType, contentTransferEncoding}; + log.debug("Setting form parameter '{}'" + + " (content-type {}, charset {}, content transfer encoding {})", args); + } + conn.setHTTPFormParameter(fp.getFormParameterName(), fp + .getFormParameterValue(), contentType, charSet, + contentTransferEncoding); + } + + // connect + conn.connect(); + // fetch and set SL result + targetContext.setTargetIsDataURL(true); + X509Certificate serverCertificate = null; + if (conn.getServerCertificates() instanceof X509Certificate[]) { + serverCertificate = (X509Certificate) conn.getServerCertificates()[0]; + } + targetContext.setTargetCertificate(serverCertificate); + targetContext.setTargetUrl(conn.getURL()); + SLResult result = commandInvoker.getResult(targetContext); + + // transfer result + conn.transmit(result); + + // process Dataurl response + dataUrlResponse = conn.getResponse(); + log.debug("Received data url response code: {}.", dataUrlResponse.getResponseCode()); + + switch (dataUrlResponse.getResponseCode()) { + case 200: + String contentType = dataUrlResponse.getContentType(); + log.debug("Got dataurl response content type: {}.", contentType); + if (contentType != null) { + if ((contentType.startsWith(HttpUtil.APPLICATION_URL_ENCODED)) + || (contentType.startsWith(HttpUtil.MULTIPART_FOTMDATA))) { + log.debug("Detected SL Request in dataurl response."); + // process headers and request + setHTTPHeaders(dataUrlResponse.getResponseHeaders()); + consumeRequestStream(dataUrlResponse.getUrl(), dataUrlResponse.getStream()); + //TODO check for bindingProcessorError + closeDataUrlConnection(); + srcContex.setSourceCertificate(serverCertificate); + srcContex.setSourceIsDataURL(true); + srcContex.setSourceUrl(conn.getURL()); + currentState = State.PROCESS; + } else if (((contentType.startsWith(HttpUtil.TXT_HTML)) + || (contentType.startsWith(HttpUtil.TXT_PLAIN)) + || (contentType.startsWith(HttpUtil.TXT_XML))) + && (dataUrlResponse.isHttpResponseXMLOK())) { + log.info("Dataurl response matches with content type: {}.", contentType); + currentState = State.TRANSFORM; + + } else if ((contentType.startsWith(HttpUtil.TXT_XML)) + && (!dataUrlResponse.isHttpResponseXMLOK())) { + log.debug("Detected text/xml dataurl response with content != "); + headerMap.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, contentType); + assignXMLRequest(dataUrlResponse.getStream(), HttpUtil.getCharset( + contentType, true)); + closeDataUrlConnection(); + srcContex.setSourceCertificate(serverCertificate); + srcContex.setSourceIsDataURL(true); + srcContex.setSourceUrl(conn.getURL()); + currentState = State.PROCESS; + // just to be complete, actually not used + srcContex.setSourceHTTPReferer(dataUrlResponse.getResponseHeaders() + .get(HttpUtil.HTTP_HEADER_REFERER)); + } else { + resultContentType = contentType; + responseHeaders = dataUrlResponse.getResponseHeaders(); + responseCode = dataUrlResponse.getResponseCode(); + currentState = State.FINISHED; + } + } else { + log.debug("Content type not set in dataurl response."); + closeDataUrlConnection(); + throw new SLBindingException(2007); + } + + break; + case 307: + contentType = dataUrlResponse.getContentType(); + if ((contentType != null) && (contentType.startsWith(HttpUtil.TXT_XML))) { + log.debug("Received dataurl response code 307 with XML content."); + String location = dataUrlResponse.getResponseHeaders().get( + HttpUtil.HTTP_HEADER_LOCATION); + if (location == null) { + log.error("Did not get a location header for a 307 data url response."); + throw new SLBindingException(2003); + } + // consumeRequestStream(dataUrlResponse.getStream()); + FormParameterStore fp = new FormParameterStore(); + fp.init(location.getBytes(HttpUtil.DEFAULT_CHARSET), + FixedFormParameters.DATAURL, null, null); + formParameterMap.put(FixedFormParameters.DATAURL, fp); + headerMap.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, contentType); + assignXMLRequest(dataUrlResponse.getStream(), HttpUtil.getCharset( + dataUrlResponse.getContentType(), true)); + closeDataUrlConnection(); + srcContex.setSourceCertificate(serverCertificate); + srcContex.setSourceIsDataURL(true); + srcContex.setSourceUrl(conn.getURL()); + currentState = State.PROCESS; + // just to be complete, actually not used + srcContex.setSourceHTTPReferer(dataUrlResponse.getResponseHeaders() + .get(HttpUtil.HTTP_HEADER_REFERER)); + + } else { + log.debug("Received dataurl response code 307 non XML content: {}.", + dataUrlResponse.getContentType()); + resultContentType = dataUrlResponse.getContentType(); + currentState = State.FINISHED; + } + responseHeaders = dataUrlResponse.getResponseHeaders(); + responseCode = dataUrlResponse.getResponseCode(); + break; + + case 301: + case 302: + case 303: + responseHeaders = dataUrlResponse.getResponseHeaders(); + responseCode = dataUrlResponse.getResponseCode(); + resultContentType = dataUrlResponse.getContentType(); + currentState = State.FINISHED; + break; + + default: + // issue error + log.info("Unexpected response code from dataurl server: {}.", + dataUrlResponse.getResponseCode()); + throw new SLBindingException(2007); + } + + } catch (SLException slx) { + bindingProcessorError = slx; + log.error("Error during dataurl communication."); + resultContentType = HttpUtil.TXT_XML; + currentState = State.TRANSFORM; + } catch (SSLHandshakeException hx) { + bindingProcessorError = new SLException(2010); + log.info("Error during dataurl communication.", hx); + resultContentType = HttpUtil.TXT_XML; + currentState = State.TRANSFORM; + } catch (IOException e) { + bindingProcessorError = new SLBindingException(2001); + log.error("Error while data url handling", e); + resultContentType = HttpUtil.TXT_XML; + currentState = State.TRANSFORM; + return; + } + } + + protected void transformResult() { + log.info("Entered State: {}.", State.TRANSFORM); + if (bindingProcessorError != null) { + resultContentType = HttpUtil.TXT_XML; + } else if (dataUrlResponse != null) { + resultContentType = dataUrlResponse.getContentType(); + } else { + targetContext.setTargetIsDataURL(false); + targetContext.setTargetUrl(srcUrl); + try { + slResult = commandInvoker.getResult(targetContext); + resultContentType = slResult.getMimeType(); + log.debug("Successfully got SLResult from commandinvoker, setting mimetype to: {}.", + resultContentType); + } catch (SLException e) { + log.info("Cannot get result from invoker:", e); + bindingProcessorError = new SLException(6002); + resultContentType = HttpUtil.TXT_XML; + } + } + templates = getTemplates(getStyleSheetUrl()); + if (templates != null) { + log.debug("Output transformation required."); + resultContentType = templates.getOutputProperties().getProperty("media-type"); + log.debug("Got media type from stylesheet: {}.", resultContentType); + if (resultContentType == null) { + log.debug("Setting to default text/xml result conent type."); + resultContentType = "text/xml"; + } + log.debug("Deferring sytylesheet processing."); + } + currentState = State.FINISHED; + } + + protected void finished() { + log.info("Entered State: {}.", State.FINISHED); + if (bindingProcessorError != null) { + log.debug("Binding processor error, sending quit command."); + resultContentType = HttpUtil.TXT_XML; + } + sendSTALQuit(); + log.info("Terminating Bindingprocessor : {}.", id); + finished = true; + } + + // -- END Methods that handle the http binding activities as defined in the + // activity diagram -- + //---------------------------------------------------------------------------- + + public String getServerHeaderValue() { + return CITIZENC_CARD_ENVIRONMENT + " " + + configurationFacade.getProductName() + "/" + + configurationFacade.getProductVersion(); + } + + public String getSignatureLayoutHeaderValue() { + return configurationFacade.getSignatureLayout(); + } + + /** + * Sets the headers of the SL Request. IMPORTANT: make sure to set all headers + * before invoking {@link #consumeRequestStream(String, InputStream)} + * + * @param aHeaderMap + * if null all header will be cleared. + */ + @Override + public void setHTTPHeaders(Map aHeaderMap) { + headerMap = new HashMap(); + // ensure lowercase keys + if (aHeaderMap != null) { + for (String s : aHeaderMap.keySet()) { + if (s != null) { + headerMap.put(s.toLowerCase(), aHeaderMap.get(s)); + if (s.equalsIgnoreCase(HttpUtil.HTTP_HEADER_REFERER)) { + String referer = aHeaderMap.get(s); + log.debug("Got referer header: {}.", referer); + srcContex.setSourceHTTPReferer(referer); + } + } + } + } + } + + public void setSourceCertificate(X509Certificate aCert) { + srcContex.setSourceCertificate(aCert); + } + + /** + * The HTTPBindingProcessor does not handle redirect URLs. It only provides + * the parameter. + * + * @return null if redirect url is not set. + */ + public String getRedirectURL() { + return getFormParameterAsString(FixedFormParameters.REDIRECTURL); + } + + public String getFormDataContentType(String aParameterName) { + FormParameter fp = formParameterMap.get(aParameterName); + if (fp != null) { + return fp.getFormParameterContentType(); + } + return null; + } + + public InputStream getFormData(String aParameterName) { + FormParameter fp = formParameterMap.get(aParameterName); + if (fp != null) { + final String enc = fp.getHeaderValue("Content-Transfer-Encoding"); + if (enc == null || "binary".equals(enc)) { + return fp.getFormParameterValue(); + } else if ("base64".equals(enc)) { + return new Base64InputStream(fp.getFormParameterValue()); + } else { + return new InputStream() { + @Override + public int read() throws IOException { + throw new IOException("Content-Transfer-Encoding : " + enc + + " is not supported."); + } + }; + } + } + return null; + } + + protected void assignXMLRequest(InputStream is, String charset) + throws IOException, SLException { + Reader r = new InputStreamReader(is, charset); + StreamSource source = new StreamSource(r); + slCommand = slCommandFactory.createSLCommand(source); + log.info("XMLRequest={}. Created new command: {}.", slCommand.getName(), slCommand + .getClass().getName()); + } + + @Override + public void process() { + boolean done = false; + int hopcounter = 0; + if (bindingProcessorError != null) { + currentState = State.FINISHED; + } + try { + while (!done) { + try { + switch (currentState) { + case INIT: + init(); + break; + case PROCESS: + processRequest(); + break; + case DATAURL: + handleDataUrl(); + if (++hopcounter > configurationFacade.getMaxDataUrlHops()) { + log.error("Maximum number ({}) of dataurl hops reached.", + configurationFacade.getMaxDataUrlHops()); + bindingProcessorError = new SLBindingException(2000); + currentState = State.FINISHED; + } + break; + case TRANSFORM: + transformResult(); + break; + case FINISHED: + done = true; + finished(); + break; + } + } catch (RuntimeException rte) { + throw rte; + } catch (Exception e) { + log.error("Caught unexpected exception.", e); + responseCode = 200; + resultContentType = HttpUtil.TXT_XML; + responseHeaders = Collections.EMPTY_MAP; + bindingProcessorError = new SLException(2000); + currentState = State.FINISHED; + } + } + } catch (Throwable t) { + log.error("Caught unexpected exception.", t); + responseCode = 200; + resultContentType = HttpUtil.TXT_XML; + responseHeaders = Collections.EMPTY_MAP; + bindingProcessorError = new SLException(2000); + currentState = State.FINISHED; + } + log.debug("Terminated http binding processor."); + finished = true; + } + + @Override + public void consumeRequestStream(String url, InputStream is) { + try { + this.srcUrl = new URL(url); + srcContex.setSourceUrl(srcUrl); + srcContex.setSourceIsDataURL(false); + log.debug("Start consuming request stream."); + formParameterMap.clear(); + String ct = headerMap + .get(HttpUtil.HTTP_HEADER_CONTENT_TYPE.toLowerCase()); + if (ct == null) { + log.info("No content type set in http header."); + throw new SLBindingException(2006); + } + InputDecoder id = InputDecoderFactory.getDecoder(ct, is); + if (id == null) { + log.error("Cannot get inputdecoder for content type {}.", ct); + throw new SLException(2006); + } + for (Iterator fpi = id.getFormParameterIterator(); fpi + .hasNext();) { + FormParameter fp = fpi.next(); + log.debug("Got request parameter with name: {}.", fp.getFormParameterName()); + if (fp.getFormParameterName().equals(FixedFormParameters.XMLREQUEST)) { + log.debug("Creating XML Request."); + for (Iterator headerIterator = fp.getHeaderNames(); headerIterator + .hasNext();) { + String headerName = headerIterator.next(); + if (HttpUtil.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(headerName)) { + String transferEncoding = fp.getHeaderValue(headerName); + log.debug("Got transfer encoding for xmlrequest: {}.", + transferEncoding); + if (XML_REQ_TRANSFER_ENCODING.contains(transferEncoding)) { + log.debug("Supported transfer encoding: {}.", transferEncoding); + } else { + log.error("Transfer encoding '{}' not supported.", transferEncoding); + throw new SLBindingException(2005); + } + } + } + String charset = HttpUtil.getCharset(ct, true); + assignXMLRequest(fp.getFormParameterValue(), charset); + } else { + FormParameterStore fps = new FormParameterStore(); + fps.init(fp); + //if (!fps.isEmpty()) { + log.debug("Setting form parameter: {}.", fps.getFormParameterName()); + formParameterMap.put(fps.getFormParameterName(), fps); + //} + } + } + if (slCommand == null) { + throw new SLBindingException(2004); + } + } catch (SLException slx) { + log.info("Error while consuming input stream.", slx); + bindingProcessorError = slx; + } catch (Throwable t) { + log.info("Error while consuming input stream.", t); + bindingProcessorError = new SLException(2000); + } finally { + try { + if (is.read() != -1) { + log.warn("Request input stream not completely read."); + while (is.read() != -1); + } + log.debug("Finished consuming request stream."); + } catch (IOException e) { + log.error("Failed to read request input stream.", e); + } + } + } + + @Override + public String getResultContentType() { + return resultContentType; + } + + protected Templates getTemplates(String styleSheetURL) { + if (styleSheetURL == null) { + log.debug("Stylesheet URL not set."); + return null; + } + try { + TransformerFactory factory = TransformerFactory.newInstance(); + factory.setURIResolver(new URIResolverAdapter(urlDereferencer)); + StreamData sd = urlDereferencer.dereference(styleSheetURL); + return factory.newTemplates(new StreamSource(sd.getStream())); + } catch (Exception ex) { + log.info("Cannot instantiate transformer.", ex); + bindingProcessorError = new SLException(2002); + return null; + } + } + + protected void handleBindingProcessorError(OutputStream os, String encoding, + Templates templates) throws IOException { + log.debug("Writing error as result."); + ErrorResultImpl error = new ErrorResultImpl(bindingProcessorError, locale); + Writer writer = writeXMLDeclarationAndProcessingInstruction(os, encoding); + error.writeTo(new StreamResult(writer), templates, true); + } + + protected Writer writeXMLDeclarationAndProcessingInstruction(OutputStream os, String encoding) throws IOException { + if (encoding == null) { + encoding = HttpUtil.DEFAULT_CHARSET; + } + OutputStreamWriter writer = new OutputStreamWriter(os, encoding); + writer.write("\n"); + writer.write("\n"); + return writer; + } + + @Override + public void writeResultTo(OutputStream os, String encoding) + throws IOException { + if (encoding == null) { + encoding = HttpUtil.DEFAULT_CHARSET; + } + if (bindingProcessorError != null) { + log.debug("Detected error in binding processor, writing error as result."); + handleBindingProcessorError(os, encoding, templates); + return; + } else if (dataUrlResponse != null) { + log.debug("Writing data url response as result."); + String charEnc = HttpUtil.getCharset(dataUrlResponse.getContentType(), + true); + InputStreamReader isr = new InputStreamReader( + dataUrlResponse.getStream(), charEnc); + OutputStreamWriter osw = new OutputStreamWriter(os, encoding); + if (templates == null) { + StreamUtil.copyStream(isr, osw); + } else { + try { + Transformer transformer = templates.newTransformer(); + transformer.transform(new StreamSource(isr), new StreamResult(osw)); + } catch (TransformerException e) { + log.error("Exception occured during result transformation.", e); + // bindingProcessorError = new SLException(2008); + // handleBindingProcessorError(os, encoding, null); + return; + } + } + osw.flush(); + isr.close(); + } else if (slResult == null) { + // result not yet assigned -> must be a cancel + bindingProcessorError = new SLException(6001); + handleBindingProcessorError(os, encoding, templates); + return; + } else { + log.debug("Getting result from invoker."); + boolean fragment = false; + Writer writer; + if (slResult instanceof ErrorResult) { + writer = writeXMLDeclarationAndProcessingInstruction(os, encoding); + fragment = true; + } else { + writer = new OutputStreamWriter(os, encoding); + } + slResult.writeTo(new StreamResult(writer), templates, fragment); + writer.flush(); + } + } + + /** + * The response code from the dataurl server or 200 if no dataurl server + * created the result + * + * @return + */ + @Override + public int getResponseCode() { + return responseCode; + } + + /** + * All headers from the data url server in case of a direct forward from the + * dataurl server. + * + * @return + */ + @Override + public Map getResponseHeaders() { + LinkedHashMap headers = new LinkedHashMap(); + headers.put(HttpUtil.HTTP_HEADER_SERVER, getServerHeaderValue()); + headers.put(HttpUtil.HTTP_HEADER_SIGNATURE_LAYOUT, getSignatureLayoutHeaderValue()); + headers.putAll(responseHeaders); + return headers; + } + + public boolean isFinished() { + return finished; + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpDataURLConnection.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpDataURLConnection.java new file mode 100644 index 00000000..d4ee55d2 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpDataURLConnection.java @@ -0,0 +1,68 @@ +/* + * Copyright 2009 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed 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 at.gv.egiz.bku.binding; + +import java.io.InputStream; +import java.net.URL; + +/** + * A HTTP DataURLConnection. + * + * @author mcentner + */ +public abstract class HttpDataURLConnection extends DataUrlConnection { + + /** + * Constructs a DataURL connection to the specified URL. + * + * @param url + * the URL to send responses and retrieve any further requests + */ + public HttpDataURLConnection(URL url) { + super(url); + } + + /** + * Set a HTTP header. + * + * @param key + * the key + * @param value + * multiple values are assumed to have the correct formatting + * (comma-separated list) + */ + public abstract void setHTTPHeader(String key, String value); + + /** + * Set a HTTP form parameter to be transmitted with the SLResult. + * + * @param name + * the name of the form parameter + * @param data + * the content of the form parameter + * @param contentType + * the content type (may be null) + * @param charSet + * the character set (may be null) + * @param transferEncoding + * the transfer encoding (may be null) + */ + public abstract void setHTTPFormParameter(String name, InputStream data, + String contentType, String charSet, String transferEncoding); + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpUtil.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpUtil.java index 5ea7b25e..8282e34e 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpUtil.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpUtil.java @@ -31,7 +31,8 @@ public class HttpUtil { public final static String HTTP_HEADER_CONTENT_TYPE = "Content-Type"; public static final String HTTP_HEADER_USER_AGENT = "User-Agent"; public static final String HTTP_HEADER_SERVER = "Server"; - public final static String HTTP_HEADER_REFERER = "Referer"; + public final static String HTTP_HEADER_REFERER = "Referer"; + public static final String HTTP_HEADER_SIGNATURE_LAYOUT = "SignatureLayout"; public final static String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding"; public final static String MULTIPART_FOTMDATA = "multipart/form-data"; public final static String MULTIPART_FOTMDATA_BOUNDARY = "boundary"; diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpsDataURLConnection.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpsDataURLConnection.java new file mode 100644 index 00000000..0054d52c --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HttpsDataURLConnection.java @@ -0,0 +1,72 @@ +/* +* Copyright 2009 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed 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 at.gv.egiz.bku.binding; + +import java.io.IOException; +import java.net.URL; +import java.security.cert.Certificate; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSocketFactory; + +public abstract class HttpsDataURLConnection extends HttpDataURLConnection { + + /** + * Construct a new + * + * @param url + * @throws IOException + */ + public HttpsDataURLConnection(URL url) { + super(url); + } + + /** + * Sets the SSLSocketFactory to be used when this instance + * creates sockets for secure https URL connections. + * + * @param socketFactory + * the SSL socket factory + */ + public abstract void setSSLSocketFactory(SSLSocketFactory socketFactory); + + /** + * Sets the HostnameVerifier for this instance. + * + * @param hostnameVerifier + * the host name verifier + */ + public abstract void setHostnameVerifier(HostnameVerifier hostnameVerifier); + + /** + * Returns the server's certificate chain which was established as part of + * defining the session. + * + * @return an ordered array of server certificates, with the peer's own + * certificate first followed by any certificate authorities. + * + * @throws SSLPeerUnverifiedException + * if the peer is not verified. + * @throws IllegalStateException + * if this method is called before the connection has been + * established. + */ + public abstract Certificate[] getServerCertificates() throws SSLPeerUnverifiedException, IllegalStateException; + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdFactory.java index 60bf69a4..a29101f4 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdFactory.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdFactory.java @@ -14,93 +14,93 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package at.gv.egiz.bku.binding; - -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Creates or converts Ids for BindingProcessors. - * @author wbauer - * - */ -public class IdFactory { - - public static int DEFAULT_NUMBER_OF_BITS = 168; - - private static Log log = LogFactory.getLog(IdFactory.class); - - private static IdFactory instance = new IdFactory(); - - private SecureRandom random; - private int numberOfBits = DEFAULT_NUMBER_OF_BITS; - - private IdFactory() { - try { - random = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - log.error("Cannot instantiate secure random" + e); - } - } - - public static IdFactory getInstance() { - return instance; - } - - - /** - * set the secure random number generator to create secure ids. - * - * @param random - * must not be null - */ - public void setSecureRandom(SecureRandom random) { - if (random == null) { - throw new NullPointerException("Cannot set secure random to null"); - } - this.random = random; - } - - /** - * Don't use this method unless you know exactly what you do ! - * Be sure to use a sufficient large entropy - * @param numberOfBits >=1 (although this small entropy does not make sense) - */ - public void setNumberOfBits(int numberOfBits) { - if (numberOfBits <1) { - throw new IllegalArgumentException("Cannot set number of bits < 1"); - } - this.numberOfBits = numberOfBits; - } - - public int getNumberOfBits() { - return numberOfBits; - } - - /** - * Creates a new Id object with the factory's secure RNG and the set number of - * bits. - * - * @return - */ - public Id createId() { - return new IdImpl(numberOfBits, random); - } - - /** - * Creates an Id object for the provided String - * - * @param idString - * may be null in this case the method call creates a new Id. - * @return - */ - public Id createId(String idString) { - if (idString == null) { - return createId(); - } - return new IdImpl(idString); - } -} \ No newline at end of file +package at.gv.egiz.bku.binding; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Creates or converts Ids for BindingProcessors. + * @author wbauer + * + */ +public class IdFactory { + + private final Logger log = LoggerFactory.getLogger(IdFactory.class); + + public static int DEFAULT_NUMBER_OF_BITS = 168; + + private static IdFactory instance = new IdFactory(); + + private SecureRandom random; + private int numberOfBits = DEFAULT_NUMBER_OF_BITS; + + private IdFactory() { + try { + random = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + log.error("Cannot instantiate secure random.", e); + } + } + + public static IdFactory getInstance() { + return instance; + } + + + /** + * set the secure random number generator to create secure ids. + * + * @param random + * must not be null + */ + public void setSecureRandom(SecureRandom random) { + if (random == null) { + throw new NullPointerException("Cannot set secure random to null"); + } + this.random = random; + } + + /** + * Don't use this method unless you know exactly what you do ! + * Be sure to use a sufficient large entropy + * @param numberOfBits >=1 (although this small entropy does not make sense) + */ + public void setNumberOfBits(int numberOfBits) { + if (numberOfBits <1) { + throw new IllegalArgumentException("Cannot set number of bits < 1"); + } + this.numberOfBits = numberOfBits; + } + + public int getNumberOfBits() { + return numberOfBits; + } + + /** + * Creates a new Id object with the factory's secure RNG and the set number of + * bits. + * + * @return + */ + public Id createId() { + return new IdImpl(numberOfBits, random); + } + + /** + * Creates an Id object for the provided String + * + * @param idString + * may be null in this case the method call creates a new Id. + * @return + */ + public Id createId(String idString) { + if (idString == null) { + return createId(); + } + return new IdImpl(idString); + } +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdImpl.java index c8a76823..096754a6 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/IdImpl.java @@ -22,8 +22,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.SecureRandom; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Implementation that uses a Base64 representation for self generated Ids. @@ -31,7 +31,8 @@ import org.apache.commons.logging.LogFactory; * */ public class IdImpl implements at.gv.egiz.bku.binding.Id { - private static Log log = LogFactory.getLog(IdImpl.class); + + private final Logger log = LoggerFactory.getLogger(IdImpl.class); private String idString; @@ -50,7 +51,7 @@ public class IdImpl implements at.gv.egiz.bku.binding.Id { b64.close(); idString = new String(baos.toByteArray()); } catch (IOException e) { - log.error("Cannot create secure id: "+e); + log.error("Cannot create secure id.", e); } } @@ -80,4 +81,4 @@ public class IdImpl implements at.gv.egiz.bku.binding.Id { return false; } } -} \ No newline at end of file +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoderFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoderFactory.java index 211deee7..081d24d4 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoderFactory.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/InputDecoderFactory.java @@ -14,76 +14,78 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package at.gv.egiz.bku.binding; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Factory to get a matching instance for a encoded input stream when reading a http request. - * - */ -public class InputDecoderFactory { - - public final static String MULTIPART_FORMDATA = "multipart/form-data"; - public final static String URL_ENCODED = "application/x-www-form-urlencoded"; - - private static InputDecoderFactory instance = new InputDecoderFactory(); - private static Log log = LogFactory.getLog(InputDecoderFactory.class); - - private String defaultEncoding = URL_ENCODED; - private Map> decoderMap = new HashMap>(); - - private InputDecoderFactory() { - decoderMap.put(MULTIPART_FORMDATA, MultiPartFormDataInputDecoder.class); - decoderMap.put(URL_ENCODED, XWWWFormUrlInputDecoder.class); - } - - public static InputDecoder getDefaultDecoder(InputStream is) { - return getDecoder(instance.defaultEncoding, is); - } - - /** - * - * @param contentType - * @param is - * @return null if the content type is not supported - */ - public static InputDecoder getDecoder(String contentType, InputStream is) { - String prefix = contentType.split(";")[0].trim().toLowerCase(); - Class dec = instance.decoderMap.get(prefix); - if (dec == null) { - log.info("Unknown encoding prefix " + contentType); - return null; - } - InputDecoder id; - try { - id = dec.newInstance(); - id.setContentType(contentType); - id.setInputStream(is); - return id; - } catch (InstantiationException e) { - log.error(e); - throw new IllegalArgumentException( - "Cannot get an input decoder for content type: " + contentType); - } catch (IllegalAccessException e) { - log.error(e); - throw new IllegalArgumentException( - "Cannot get an input decoder for content type: " + contentType); - } - } - - /** - * Allows to register decoders for special mime types. - * @param mimeType - * @param decoder - */ - public static void registerDecoder(String mimeType, - Class decoder) { - instance.decoderMap.put(mimeType.toLowerCase(), decoder); - } -} +package at.gv.egiz.bku.binding; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory to get a matching instance for a encoded input stream when reading a http request. + * + */ +public class InputDecoderFactory { + + public final static String MULTIPART_FORMDATA = "multipart/form-data"; + public final static String URL_ENCODED = "application/x-www-form-urlencoded"; + + private static InputDecoderFactory instance = new InputDecoderFactory(); + + private String defaultEncoding = URL_ENCODED; + private Map> decoderMap = new HashMap>(); + + private InputDecoderFactory() { + decoderMap.put(MULTIPART_FORMDATA, MultiPartFormDataInputDecoder.class); + decoderMap.put(URL_ENCODED, XWWWFormUrlInputDecoder.class); + } + + public static InputDecoder getDefaultDecoder(InputStream is) { + return getDecoder(instance.defaultEncoding, is); + } + + /** + * + * @param contentType + * @param is + * @return null if the content type is not supported + */ + public static InputDecoder getDecoder(String contentType, InputStream is) { + + Logger log = LoggerFactory.getLogger(InputDecoderFactory.class); + + String prefix = contentType.split(";")[0].trim().toLowerCase(); + Class dec = instance.decoderMap.get(prefix); + if (dec == null) { + log.info("Unknown encoding prefix " + contentType); + return null; + } + InputDecoder id; + try { + id = dec.newInstance(); + id.setContentType(contentType); + id.setInputStream(is); + return id; + } catch (InstantiationException e) { + log.error("Failed to instantiate InputDecoder.", e); + throw new IllegalArgumentException( + "Cannot get an input decoder for content type: " + contentType); + } catch (IllegalAccessException e) { + log.error("Failed to instantiate InputDecoder.", e); + throw new IllegalArgumentException( + "Cannot get an input decoder for content type: " + contentType); + } + } + + /** + * Allows to register decoders for special mime types. + * @param mimeType + * @param decoder + */ + public static void registerDecoder(String mimeType, + Class decoder) { + instance.decoderMap.put(mimeType.toLowerCase(), decoder); + } +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/MultiPartFormDataInputDecoder.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/MultiPartFormDataInputDecoder.java index f8b13553..2dd57f12 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/MultiPartFormDataInputDecoder.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/MultiPartFormDataInputDecoder.java @@ -14,120 +14,121 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package at.gv.egiz.bku.binding; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Iterator; - -import org.apache.commons.fileupload.FileItemIterator; -import org.apache.commons.fileupload.FileItemStream; -import org.apache.commons.fileupload.FileUpload; -import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.RequestContext; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import at.gv.egiz.bku.slexceptions.SLRuntimeException; - -/** - * The code to detect the multipart boundary is based on - * org.apache.commons.fileupload.FileUploadBase of - * http://commons.apache.org/fileupload/ - * - * @author wbauer - * - */ -public class MultiPartFormDataInputDecoder implements InputDecoder, - RequestContext { - - private static Log log = LogFactory - .getLog(MultiPartFormDataInputDecoder.class); - - private String contentType; - private InputStream stream; - - @Override - public void setContentType(String contentType) { - this.contentType = contentType; - } - - @Override - public String getCharacterEncoding() { - return null; - } - - @Override - public int getContentLength() { - return 0; - } - - @Override - public String getContentType() { - return contentType; - } - - @Override - public InputStream getInputStream() throws IOException { - return stream; - } - - @Override - public Iterator getFormParameterIterator() { - try { - FileUpload fup = new FileUpload(); - FileItemIterator fit = fup.getItemIterator(this); - return new IteratorDelegator(fit); - } catch (Exception iox) { - log.error("Cannot decode multipart form data stream " + iox); - throw new SLRuntimeException(iox); - } - } - - @Override - public void setInputStream(InputStream is) { - stream = is; - } - - static class IteratorDelegator implements Iterator { - - private FileItemIterator fileItemIterator; - - public IteratorDelegator(FileItemIterator fit) { - fileItemIterator = fit; - } - - @Override - public boolean hasNext() { - try { - return fileItemIterator.hasNext(); - } catch (FileUploadException e) { - log.error(e); - throw new SLRuntimeException(e); - } catch (IOException e) { - log.error(e); - throw new SLRuntimeException(e); - } - } - - @Override - public FormParameter next() { - try { - FileItemStream item = fileItemIterator.next(); - return new FormParameterImpl(item.getContentType(), - item.getFieldName(), item.openStream(), item.getHeaders()); - } catch (FileUploadException e) { - log.error(e); - throw new SLRuntimeException(e); - } catch (IOException e) { - log.error(e); - throw new SLRuntimeException(e); - } - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Remove not supported"); - } - } -} +package at.gv.egiz.bku.binding; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; + +import org.apache.commons.fileupload.FileItemIterator; +import org.apache.commons.fileupload.FileItemStream; +import org.apache.commons.fileupload.FileUpload; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.RequestContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.bku.slexceptions.SLRuntimeException; + +/** + * The code to detect the multipart boundary is based on + * org.apache.commons.fileupload.FileUploadBase of + * http://commons.apache.org/fileupload/ + * + * @author wbauer + * + */ +public class MultiPartFormDataInputDecoder implements InputDecoder, + RequestContext { + + private final Logger log = LoggerFactory.getLogger(MultiPartFormDataInputDecoder.class); + + private String contentType; + private InputStream stream; + + @Override + public void setContentType(String contentType) { + this.contentType = contentType; + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public int getContentLength() { + return 0; + } + + @Override + public String getContentType() { + return contentType; + } + + @Override + public InputStream getInputStream() throws IOException { + return stream; + } + + @Override + public Iterator getFormParameterIterator() { + try { + FileUpload fup = new FileUpload(); + FileItemIterator fit = fup.getItemIterator(this); + return new IteratorDelegator(fit); + } catch (Exception iox) { + log.error("Cannot decode multipart form data stream " + iox); + throw new SLRuntimeException(iox); + } + } + + @Override + public void setInputStream(InputStream is) { + stream = is; + } + + static class IteratorDelegator implements Iterator { + + private final Logger log = LoggerFactory.getLogger(MultiPartFormDataInputDecoder.class); + + private FileItemIterator fileItemIterator; + + public IteratorDelegator(FileItemIterator fit) { + fileItemIterator = fit; + } + + @Override + public boolean hasNext() { + try { + return fileItemIterator.hasNext(); + } catch (FileUploadException e) { + log.error("Failed to get next file item.", e); + throw new SLRuntimeException(e); + } catch (IOException e) { + log.error("Failed to get next file item.", e); + throw new SLRuntimeException(e); + } + } + + @Override + public FormParameter next() { + try { + FileItemStream item = fileItemIterator.next(); + return new FormParameterImpl(item.getContentType(), + item.getFieldName(), item.openStream(), item.getHeaders()); + } catch (FileUploadException e) { + log.error("Failed to get next file item.", e); + throw new SLRuntimeException(e); + } catch (IOException e) { + log.error("Failed to get next file item.", e); + throw new SLRuntimeException(e); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported"); + } + } +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/ProcessingContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/ProcessingContext.java deleted file mode 100644 index 913259f6..00000000 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/ProcessingContext.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2008 Federal Chancellery Austria and - * Graz University of Technology - * - * Licensed 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 at.gv.egiz.bku.binding; - -import java.util.Hashtable; -import java.util.Map; -import java.util.concurrent.Future; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * - * @author Clemens Orthacker - */ -public class ProcessingContext { - - public static final String BINDING_PROCESSOR = "binding.processor"; - public static final String FUTURE = "future"; - - protected static final Log log = LogFactory.getLog(ProcessingContext.class); - - protected Map properties = new Hashtable(); - - public ProcessingContext(BindingProcessor bp, Future future) { - properties.put(BINDING_PROCESSOR, bp); - properties.put(FUTURE, future); - } - - public BindingProcessor getBindingProcessor() { - return (BindingProcessor) properties.get(BINDING_PROCESSOR); - } - - public Future getFuture() { - return (Future) properties.get(FUTURE); - } - - public Object get(String key) { - return properties.get(key); - } - - public void put(String key, Object value) { - properties.put(key, value); - } -} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/RemovalStrategy.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/RemovalStrategy.java deleted file mode 100644 index 6c2dcb9f..00000000 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/RemovalStrategy.java +++ /dev/null @@ -1,26 +0,0 @@ -/* -* Copyright 2008 Federal Chancellery Austria and -* Graz University of Technology -* -* Licensed 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 at.gv.egiz.bku.binding; - -/** - * Could be used to remove expired BindingProcessor objects from a BindingProcessorManager. - * - */ -public interface RemovalStrategy { - public void execute(); - public void setBindingProcessorManager(BindingProcessorManager bp); -} \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/SLCommandInvokerImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/SLCommandInvokerImpl.java index a23d96e8..c2ee4ee1 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/SLCommandInvokerImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/SLCommandInvokerImpl.java @@ -16,11 +16,14 @@ */ package at.gv.egiz.bku.binding; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import at.gv.egiz.bku.accesscontroller.SecurityManagerFacade; +import at.gv.egiz.bku.jmx.ComponentMXBean; +import at.gv.egiz.bku.jmx.ComponentState; import at.gv.egiz.bku.slcommands.SLCommand; +import at.gv.egiz.bku.slcommands.SLCommandContext; import at.gv.egiz.bku.slcommands.SLCommandInvoker; import at.gv.egiz.bku.slcommands.SLResult; import at.gv.egiz.bku.slcommands.SLSourceContext; @@ -31,10 +34,11 @@ import at.gv.egiz.bku.slexceptions.SLException; * This class implements the entry point for the CCEs security management. * */ -public class SLCommandInvokerImpl implements SLCommandInvoker { +public class SLCommandInvokerImpl implements SLCommandInvoker, ComponentMXBean { - private static Log log = LogFactory.getLog(SLCommandInvokerImpl.class); + private final Logger log = LoggerFactory.getLogger(SLCommandInvokerImpl.class); + protected SLCommandContext commandContext; protected SLCommand command; protected SLResult result; protected SecurityManagerFacade securityManager; @@ -46,12 +50,11 @@ public class SLCommandInvokerImpl implements SLCommandInvoker { */ public void invoke(SLSourceContext aContext) throws SLException { if (securityManager == null) { - log.warn("Security policy not implemented yet, invoking command: " - + command); - result = command.execute(); + log.warn("Security policy not implemented yet, invoking command: {}.", command); + result = command.execute(commandContext); } else { if (securityManager.mayInvokeCommand(command, aContext)) { - result = command.execute(); + result = command.execute(commandContext); } else { throw new SLException(6002); } @@ -60,9 +63,7 @@ public class SLCommandInvokerImpl implements SLCommandInvoker { public SLResult getResult(SLTargetContext aContext) throws SLException { if (securityManager == null) { - log - .warn("Security policy not implemented yet, getting result of command: " - + command); + log.warn("Security policy not implemented yet, getting result of command: {}.", command); return result; } else { if (securityManager.maySendResult(command, aContext)) { @@ -73,7 +74,8 @@ public class SLCommandInvokerImpl implements SLCommandInvoker { } } - public void setCommand(SLCommand aCmd) { + public void setCommand(SLCommandContext commandContext, SLCommand aCmd) { + this.commandContext = commandContext; command = aCmd; } @@ -92,4 +94,9 @@ public class SLCommandInvokerImpl implements SLCommandInvoker { this.securityManager = securityManager; } -} \ No newline at end of file + @Override + public ComponentState checkComponentState() { + return new ComponentState(true); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java index 9279130d..36d5f723 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java @@ -274,6 +274,7 @@ public class XWWWFormUrlInputIterator implements Iterator { pos = 0; } int c2 = Character.digit(buf[pos], 16); + pos++; return ((c1 << 4) | c2); } else { return buf[pos++]; -- cgit v1.2.3