/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.portal.impl; import java.io.UnsupportedEncodingException; import java.util.Iterator; import java.util.List; import org.apache.avalon.framework.activity.Disposable; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.parameters.ParameterException; import org.apache.avalon.framework.parameters.Parameterizable; import org.apache.avalon.framework.parameters.Parameters; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; import org.apache.avalon.framework.thread.ThreadSafe; import org.apache.cocoon.components.ContextHelper; import org.apache.cocoon.environment.Request; import org.apache.cocoon.portal.LinkService; import org.apache.cocoon.portal.event.ComparableEvent; import org.apache.cocoon.portal.event.ConvertableEvent; import org.apache.cocoon.portal.event.Event; import org.apache.cocoon.portal.event.EventConverter; import org.apache.cocoon.portal.event.RequestEvent; import org.apache.cocoon.util.NetUtils; /** * * @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a> * @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a> * * @version $Id$ */ public class DefaultLinkService extends AbstractLogEnabled implements LinkService, ThreadSafe, Serviceable, Disposable, Contextualizable, Parameterizable { /** The converter used to convert an event into a request parameter */ protected EventConverter converter; /** The service manager */ protected ServiceManager manager; /** The cocoon context */ protected Context context; protected Boolean eventsMarshalled; /** Default port used for http. */ protected int defaultPort = 80; /** Default port used for https. */ protected int defaultSecurePort = 443; /** * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) */ public void service(ServiceManager manager) throws ServiceException { this.manager = manager; this.converter = (EventConverter)this.manager.lookup(EventConverter.ROLE); } /** * @see org.apache.avalon.framework.parameters.Parameterizable#parameterize(org.apache.avalon.framework.parameters.Parameters) */ public void parameterize(Parameters params) throws ParameterException { this.defaultPort = params.getParameterAsInteger("defaultPort", this.defaultPort); this.defaultSecurePort = params.getParameterAsInteger("defaultSecurePort", this.defaultSecurePort); } /** * Return the current info for the request. * @return A LinkInfo object. */ protected LinkInfo getInfo() { final Request request = ContextHelper.getRequest( this.context ); LinkInfo info = (LinkInfo)request.getAttribute(DefaultLinkService.class.getName()); if ( info == null ) { synchronized ( this ) { info = (LinkInfo)request.getAttribute(DefaultLinkService.class.getName()); if ( info == null ) { info = new LinkInfo(request, this.defaultPort, this.defaultSecurePort); request.setAttribute(DefaultLinkService.class.getName(), info); } } } return info; } /** * @see org.apache.cocoon.portal.LinkService#isSecure() */ public boolean isSecure() { return ContextHelper.getRequest(this.context).isSecure(); } /** * @see org.apache.cocoon.portal.LinkService#encodeURL(String url) */ public String encodeURL(String url) { return ContextHelper.getResponse(this.context).encodeURL(url); } /** * @see org.apache.cocoon.portal.LinkService#getLinkURI(org.apache.cocoon.portal.event.Event) */ public String getLinkURI(Event event) { return this.getLinkURI(event, null); } /** * @see org.apache.cocoon.portal.LinkService#getLinkURI(org.apache.cocoon.portal.event.Event, Boolean) */ public String getLinkURI(Event event, Boolean secure) { if (event == null) { return this.getRefreshLinkURI(secure); } final LinkInfo info = this.getInfo(); final StringBuffer buffer = new StringBuffer(initBuffer(info, event, secure)); boolean hasParams = info.hasParameters(); // add comparable events final boolean comparableEvent = event instanceof ComparableEvent; Iterator iter = info.comparableEvents.iterator(); while (iter.hasNext()) { Object[] objects = (Object[]) iter.next(); ComparableEvent current = (ComparableEvent) objects[0]; if (!comparableEvent || !current.equalsEvent((ComparableEvent) event)) { if (hasParams) { buffer.append('&'); } else { buffer.append('?'); } try { buffer.append((String) objects[1]).append('=').append(NetUtils.encode((String) objects[2], "utf-8")); } catch (UnsupportedEncodingException uee) { // ignore this as utf-8 is always supported } hasParams = true; } } // now add event hasParams = this.addEvent(buffer, event, hasParams); return buffer.toString(); } protected String initBuffer(LinkInfo info, Event event, Boolean secure) { return info.getBase(secure); } /** * Add one event to the buffer * @return Returns true, if the link contains a parameter */ protected boolean addEvent(StringBuffer buffer, Event event, boolean hasParams) { if ( hasParams ) { buffer.append('&'); } else { buffer.append('?'); } StringBuffer value = new StringBuffer(""); String parameterName = processEvent(event, value); try { buffer.append(parameterName).append('=').append(NetUtils.encode(value.toString(), "utf-8")); } catch (UnsupportedEncodingException uee) { // ignore this as utf-8 is always supported } return true; } /** * @see org.apache.cocoon.portal.LinkService#getLinkURI(java.util.List) */ public String getLinkURI(List events) { return this.getLinkURI(events, null); } /** * @see org.apache.cocoon.portal.LinkService#getLinkURI(java.util.List) */ public String getLinkURI(List events, Boolean secure) { if (events == null || events.size() == 0) { return this.getRefreshLinkURI(secure); } final LinkInfo info = this.getInfo(); boolean hasParams = info.hasParameters(); final StringBuffer buffer = new StringBuffer(initBuffer(info, events, secure)); // add comparable events Iterator iter = info.comparableEvents.iterator(); while (iter.hasNext()) { Object[] objects = (Object[]) iter.next(); ComparableEvent current = (ComparableEvent) objects[0]; Iterator eventIterator = events.iterator(); boolean found = false; while (!found && eventIterator.hasNext()) { final Object inEvent = eventIterator.next(); if (inEvent instanceof ComparableEvent && current.equalsEvent((ComparableEvent) inEvent)) { found = true; } } if (!found) { if (hasParams) { buffer.append('&'); } else { buffer.append('?'); } try { buffer.append((String) objects[1]).append('=').append(NetUtils.encode((String) objects[2], "utf-8")); } catch (UnsupportedEncodingException uee) { // ignore this as utf-8 is always supported } hasParams = true; } } // now add events iter = events.iterator(); while (iter.hasNext()) { final Object current = iter.next(); if (current instanceof Event) { hasParams = this.addEvent(buffer, (Event) current, hasParams); } else if ( current instanceof ParameterDescription ) { if (hasParams) { buffer.append('&'); } else { buffer.append('?'); hasParams = true; } buffer.append(((ParameterDescription) current).parameters); } } return buffer.toString(); } protected String initBuffer(LinkInfo info, List events, Boolean secure) { return info.getBase(secure); } /** * @see org.apache.cocoon.portal.LinkService#addEventToLink(org.apache.cocoon.portal.event.Event) */ public void addEventToLink(Event event) { if (event == null) { return; } StringBuffer value = new StringBuffer(""); String parameterName = processEvent(event, value); final LinkInfo info = this.getInfo(); if (event instanceof ComparableEvent) { // search if we already have an event for this! final Iterator iter = info.comparableEvents.iterator(); boolean found = false; while (!found && iter.hasNext()) { Object[] objects = (Object[])iter.next(); if (((ComparableEvent) objects[0]).equalsEvent((ComparableEvent) event)) { found = true; info.comparableEvents.remove(objects); } } info.comparableEvents.add(new Object[]{event, parameterName, value.toString()}); } else { this.addParameterToLink(parameterName, value.toString()); } } /** * @see org.apache.cocoon.portal.LinkService#addParameterToLink(java.lang.String, java.lang.String) */ public void addParameterToLink(String name, String value) { final LinkInfo info = this.getInfo(); info.addParameterToBase(name, value); } /** * @see org.apache.cocoon.portal.LinkService#addUniqueParameterToLink(java.lang.String, java.lang.String) */ public void addUniqueParameterToLink(String name, String value) { final LinkInfo info = this.getInfo(); info.deleteParameterFromBase(name); this.addParameterToLink(name, value); } /** * @see org.apache.cocoon.portal.LinkService#getRefreshLinkURI() */ public String getRefreshLinkURI() { return this.getRefreshLinkURI(null); } /** * @see org.apache.cocoon.portal.LinkService#getRefreshLinkURI(java.lang.Boolean) */ public String getRefreshLinkURI(Boolean secure) { final LinkInfo info = this.getInfo(); final StringBuffer buffer = new StringBuffer(info.getBase(secure)); // add comparable events Iterator iter = info.comparableEvents.iterator(); boolean hasParams = info.hasParameters(); while (iter.hasNext()) { Object[] objects = (Object[])iter.next(); if ( hasParams ) { buffer.append('&'); } else { buffer.append('?'); } try { buffer.append((String)objects[1]).append('=').append(NetUtils.encode((String)objects[2], "utf-8")); } catch (UnsupportedEncodingException uee) { // ignore this as utf-8 is always supported } hasParams = true; } return buffer.toString(); } /** * @see org.apache.avalon.framework.activity.Disposable#dispose() */ public void dispose() { if (this.manager != null) { this.manager.release( this.converter ); this.converter = null; this.manager = null; } } /** * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) */ public void contextualize(Context context) throws ContextException { this.context = context; } private boolean getEventsMarshalled() { if (this.eventsMarshalled == null) { this.eventsMarshalled = new Boolean(this.converter.isMarshallEvents()); } return this.eventsMarshalled.booleanValue(); } protected String processEvent(Event event, StringBuffer value) { String parameterName = DEFAULT_REQUEST_EVENT_PARAMETER_NAME; if (event instanceof ConvertableEvent && getEventsMarshalled()) { final String eventParName = ((ConvertableEvent) event).getRequestParameterName(); String eventStr = ((ConvertableEvent) event).asString(); if (eventStr == null) { // Could not convert the event value.append(this.converter.encode(event)); } else { parameterName = DEFAULT_CONVERTABLE_EVENT_PARAMETER_NAME; try { String eventValue = NetUtils.encode(eventStr, "utf-8"); value.append(eventParName).append('(').append(eventValue).append(')'); } catch (UnsupportedEncodingException uee) { // ignore this as utf-8 is always supported } } } else { if (event instanceof RequestEvent) { final String eventParName = ((RequestEvent) event).getRequestParameterName(); if (eventParName != null) { parameterName = eventParName; } } value.append(this.converter.encode(event)); } return parameterName; } }